SQLite3数据库或磁盘已满/数据库磁盘映像格式不正确

我的数据库大约是25 MB,并且我已经validation了访问它的用户名以及文件权限在几个月内没有改变。 我遇到一个问题,由于“数据库或磁盘已满”而导致查询失败,然后有时“数据库磁盘映像格式错误”问题。

除非我正在读这个错误,否则我的磁盘没有接近完整(这是一个Ubuntu服务器,9.10,如果有任何区别的话)

Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda1 19610300 2389596 16224560 13% / udev 10240 128 10112 2% /dev none 254136 0 254136 0% /dev/shm none 254136 36 254100 1% /var/run none 254136 0 254136 0% /var/lock none 254136 0 254136 0% /lib/init/rw 

作为一个testing,我只是做了一个添加了新logging的操作,并没有问题。 我试图找出是否有一组特定的操作失败。 但是,在插入(并validation它在那里)之后,数据库在磁盘上的字节数没有变化(既不是向上也不是向下)。

使用命令行实用程序导致类似于以下内容,这是令人惊叹的:)

 SQLite version 3.6.12 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> pragma integrity_check; *** in database main *** On tree page 2 cell 0: 2nd reference to page 26416 On tree page 2 cell 1: 2nd reference to page 26417 On tree page 2 cell 2: 2nd reference to page 26434 On tree page 2 cell 3: 2nd reference to page 26449 On tree page 2 cell 4: 2nd reference to page 26464 On tree page 2 cell 5: 2nd reference to page 26358 On tree page 2 cell 6: 2nd reference to page 26494 On tree page 2 cell 7: Child page depth differs On tree page 2 cell 8: 2nd reference to page 26190 On tree page 2 cell 8: Child page depth differs ... etc., etc. ... 

任何想法,我应该看下一个? 在表中有最大的行数还是有问题? 我做了一些SQLite3最大值的阅读,我的数据库中没有任何东西接近他们据我所知。

然后,我看了一下我的每日备份,并且看到数据库备份在3-4天内没有更改文件大小 – 非常奇怪。 我从没有改变文件大小的时间之前恢复了数据库的备份副本,但仍然遇到了奇怪的问题。

我想我将不得不(1)从一个较旧的备份恢复,(2)重新运行我的Rails迁移修复。

有几件事要考虑:

  • SQLite3数据库文件大致以DB页面大小的倍数增长,除非使用VACUUM否则不会缩小。 如果删除了一些行,释放的空间会在内部进行标记,并在以后的插入中重新使用。 因此,插入通常不会导致备份DB文件的大小发生变化。

  • 你不应该为SQLite(或其他任何数据库)使用传统的备份工具,因为它们没有考虑到确保数据库没有损坏的关键数据库状态信息。 尤其是,在插入事务的中间复制数据库文件是灾难的秘诀。

  • SQLite3有专门用于备份或复制正在使用的数据库的API 。

  • 是的,这似乎是你的数据库文件已损坏 。 这可能是一个硬件/文件系统错误。 或者也许你在使用它们时复制了它们? 或者可能恢复了没有正确使用的备份?

要修复损坏的数据库,您可以使用sqlite3命令行工具。 设置环境variables后,在shell中input以下命令:

 cd $DATABASE_LOCATION echo '.dump'|sqlite3 $DB_NAME|sqlite3 repaired_$DB_NAME mv $DB_NAME corrupt_$DB_NAME mv repaired_$DB_NAME $DB_NAME 

这段代码帮助我恢复了一个SQLite数据库,我用它作为Core Data的一个持久化存储,并在保存时产生了以下错误:

无法保存:域NSCocoaErrorDomain NSFror 259 {NSFilePath = mydata.db NSUnderlyingException =致命错误。 mydata.db中的数据库已损坏。 SQLite错误代码:11,'数据库磁盘映像格式不正确'}

我使用以下脚本来修复格式错误的sqlite文件:

 #!/bin/bash cat <( sqlite3 "$1" .dump | grep "^ROLLBACK" -v ) <( echo "COMMIT;" ) | sqlite3 "fix_$1" 

大多数情况下,当一个SQLite数据库格式不正确时,仍然有可能进行转储。 这个转储基本上是很多重build数据库的SQL语句。

某些行可能从转储中丢失(可能是因为它们已损坏)。 如果是这种情况,缺less行的INSERT语句将被replace为一些注释,脚本将以ROLLBACK TRANSACTION结束。

所以我们在这里做的是我们做的转储(格式不正确的行被排除),我们用COMMIT来replaceROLLBACK,这样整个转储脚本将被提交而不是回滚。

这种方法拯救了我的生活几百次已经\ o /

为了避免首先获得“数据库或磁盘已满”,如果您有大量RAM,请尝试以下操作:

 sqlite> pragma temp_store = 2; 

这告诉SQLite把临时文件放在内存中。 (“数据库或磁盘已满”的消息并不意味着数据库已满或磁盘已满!这意味着临时目录已满。)我有256G的RAM,但只有2G的/ tmp,所以这个工作对我很好。 你有更多的RAM,可以使用更大的数据库文件。

如果你没有很多的内存,试试这个:

 sqlite> pragma temp_store = 1; sqlite> pragma temp_store_directory = '/directory/with/lots/of/space'; 

temp_store_directory已被弃用(这是愚蠢的,因为temp_store不被弃用,并需要temp_store_directory),所以要谨慎在代码中使用它。

当数据库被损坏时,我看到了这种情况,您是否试图将其克隆到一个新的?

Safley复制为SQLite数据库

安全地复制SQLite数据库

复制SQLite数据库非常简单。 以一种不会破坏它的方式来做这件事并不那么简单。 就是这样:

 shell$ sqlite3 some.db sqlite> begin immediate; <press CTRL+Z> shell$ cp some.db some.db.backup shell$ exit sqlite> rollback; 

这将给你一个很好的干净的备份,肯定是在一个适当的状态,因为写入数据库中途通过复制过程是不可能的。

在使用Google App Engine时,我遇到了这个问题。 出于某种原因,我从那以后Google App Engine从未开始。

 $ echo '' > /tmp/appengine.apprtc.root/*.db 

要解决它,我需要手动执行:

 $ sqlite3 datastore.db sqlite> begin immediate; <press CTRL+Z> $ cp datastore.db logs.db 

然后用标志运行Google App Engine:

 $ dev_appserver.py --clear_datastore --clear_search_index 

之后它终于奏效了。

如果腐败是在第一页上,我们不能使用“.dump”方法来恢复它。

有一个名为undark的工具,如果logging没有被覆盖,则从db文件中导出所有的行(被删除或不被删除)。 最后版本在这里 。

我也使用SQLite-Deleted-Records-Parser,但不给出相同types的输出。

如果你是一个开发者,你可以避免再次使用下面的一些问题:

  • litereplica – 具有时间点恢复的单主复制
  • litesync – 多主同步
  • rqlite – 多主复制

或定期使用备份API 。 虽然它在每个备份上传输整个数据库。

请记住:如果在事务处于活动状态时复制SQLite文件或使用常规备份方法,则副本可能会损坏 。

在应用程序开发过程中,我发现这些消息来自频繁和大量的INSERT和UPDATE操作。 确保在一个操作中插入和更新多个行或数据。

 var updateStatementString : String! = "" for item in cardids { let newstring = "UPDATE "+TABLE_NAME+" SET pendingImages = '\(pendingImage)\' WHERE cardId = '\(item)\';" updateStatementString.append(newstring) } print(updateStatementString) let results = dbManager.sharedInstance.update(updateStatementString: updateStatementString) return Int64(results)