SQLite3数据库或磁盘已满/数据库磁盘映像格式错误。

83

我的数据库大约有25 MB,我已经确认访问它的用户名以及文件权限在几个月内没有更改。我遇到了一个问题,查询失败,提示“数据库或磁盘已满”,有时还会出现“数据库磁盘映像文件损坏”的问题。

除非我读错了,否则我的磁盘远未满(这是一个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

我刚刚进行了一个添加新记录的测试,它运行正常。我正在试图找出是否有特定的操作失败了。然而,在插入记录后(并验证其已存在),数据库在磁盘上的字节数未发生任何变化(既不增加也不减少)。

使用命令行实用程序会导致类似以下的结果,这明显是失败的:)

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命令行实用工具来排除应用程序代码问题吗? - thkala
你能再具体一些吗?比如说,你会从CLU运行哪个命令? - jefflunt
好的,危机快要过去了。我成功地将受损的数据库导出到一个新的数据库中,并通过完整性检查。我确认所有关键数据都完好无损(例如“用户”表等)。我还确认(非常幸运地)唯一无法恢复的是一些微不足道的网站使用数据(页面浏览统计等),我可以毫不犹豫地放弃它们。有一些重要的数据没有存活下来,但我有备份,并且可以通过插入重新导入。因此,没有丢失任何关键内容,只需要多做一点工作。 - jefflunt
10个回答

84

为修复一个损坏的数据库,您可以使用sqlite3命令行实用程序。在设置环境变量后,在 shell 中输入以下命令:

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

以下代码帮助我恢复了一个作为Core Data持久化存储的SQLite数据库,该数据库在保存时出现以下错误:

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


2
非常感谢您——我花了几个小时创建一个Core Data数据库,但它一直出现错误代码11问题(在iOS 4.3中,但在某些原因下不会在4.2中出现),并且无法加载。但是像这样转储到新创建的数据库中解决了该问题。太好了! - Cory Imdieke
向Pegolon大声喊出来...用这个方法刚刚拯救了一个客户。非常感谢! - MichelV69
2
我曾经遇到过类似的问题,这指引了我正确的方向,但仅仅进行转储是不够的...它会生成一个新的大小为0字节的sqlite文件。我必须先将sql文件转储到文本文件中,然后删除一些明显来自sqlite系统的带有“error”字样的行,然后使用该文本文件的sql创建一个sqlite文件。这对我起作用了。 - Z S
3
你也可以只使用 .clone newdb.sqlite。 - David Benko
3
这里生成一个大小为 0 字节的输出文件。将其转储到 .sql 文件时,返回一个带有“由于错误”注释的“回滚”作为最后一行。 - robertspierre
显示剩余2条评论

34

我使用以下脚本来修复格式不正确的sqlite文件:

#!/bin/bash

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

大多数情况下,当sqlite数据库损坏时,仍然可以进行转储。这个转储实际上是一堆SQL语句,可以重建数据库。

由于某些行可能已经损坏,因此在这个转储中可能会缺少一些行。如果出现这种情况,则缺少的行的INSERT语句将被替换为一些注释,并且脚本将以ROLLBACK TRANSACTION结束。

因此,我们在这里进行的操作是制作转储(排除了损坏的行),并将ROLLBACK替换为COMMIT,以便整个转储脚本能够提交而不是回滚。

这种方法已经救了我的命数百次 \o/


34

需要考虑以下几点:

  • SQLite3数据库文件大致以DB页面大小的倍数增长,如果不使用VACUUM,则不会收缩。如果您删除某些行,则释放的空间会被标记并在稍后的插入中重复使用。因此,插入通常不会导致支持DB文件大小的更改。

  • 不应该使用传统的备份工具备份SQLite(或任何其他数据库),因为它们没有考虑到关键的DB状态信息,这对于确保无损坏的数据库非常重要。特别地,在插入事务进行中复制DB文件是灾难的开始...

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

  • 是的,似乎您的DB文件已经损坏了。可能是硬件/文件系统错误。或者在使用它们时复制了它们?或者恢复了未正确获取的备份副本?


3
根据我看到的症状,似乎是#3(备份正在使用的数据库副本)。根据你的描述,页面大小/数据库大小非常合理。因此,我正在尝试修复它,或从通过完整性检查的每日备份副本中还原(幸运的是,我有一个),然后更改我的备份方案。 - jefflunt
7
为了造福社区,我使用这里提供的方法修复/恢复了数据库:http://community.spiceworks.com/how_to/show/1468 。我还有每日备份,因此这个可能的“糟糕时刻”被转化为相对较小的中断。 - jefflunt
1
对于使用*nix系统的用户,我在spiceworks上找到了一组类似于@normalocity发现的指令:http://askubuntu.com/questions/30185/banshee-encountered-a-fatal-error-sqlite-error-11-database-disk-image-is-malfo - daxiang28

14

如果您有大量的RAM,为了避免首次出现“数据库或磁盘已满”的情况,请尝试以下方法:

sqlite> pragma temp_store = 2;

这告诉SQLite将临时文件放入内存中。(“数据库或磁盘已满”消息并不意味着数据库已满或磁盘已满!它意味着临时目录已满。)我有256G的RAM,但只有2G的/tmp,所以对我非常有效。你拥有的RAM越多,你可以处理的db文件就越大。

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

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

temp_store_directory已被弃用(这很荒谬,因为temp_store并未被弃用,而且需要temp_store_directory),因此在编写代码时请小心使用。


3
我在 /tmp 目录中有很多可用的磁盘空间,但似乎没有被使用。我正在创建一个临时表,导致出现“数据库或磁盘已满”的错误。将 temp_store 设置为使用内存解决了这个问题。我有 3GB 的 RAM 和 1.6GB 的 /tmp 空间。 - N Klosterman

12

在sqlite3命令行克隆当前数据库对我起了作用。

.open /path/to/database/corrupted_database.sqlite3
.clone /path/to/database/new_database.sqlite3

在Django的设置文件中更改数据库名称

DATABASES = {
'default': {
    'ENGINE': 'django.db.backends.sqlite3',
    'NAME': os.path.join(BASE_DIR, 'new_database.sqlite3'),
}}

2
事实上,对我来说,克隆的效果比.dump好得多。 - grin
我遇到了这个错误:sqlite_sequence... 错误:对象名称保留供内部使用:sqlite_sequence - chovy

9

我见过这种情况发生在数据库损坏时,您尝试将其克隆到新的数据库中了吗?

安全地复制SQLite数据库

Safely copy a SQLite database

It's trivially easy to copy a SQLite database. It's less trivial to do this in a way that won't corrupt it. Here's how:

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

This will give you a nice clean backup that's sure to be in a proper state, since writing to the database half-way through your copying process is impossible.


这是有道理的,因为我们最近实施了每日备份数据库(已运行约30天),并且我们正在按照上面链接中指定的方式进行。我还在我的应用程序中设置了一个“健全性检查”功能,当出现问题时会向我发出警报,并且警报开始响起,我突然发现有一些带有错误ID字段和其他奇怪情况的行。 - jefflunt
我尝试了上述方法,但是生成的数据库仍然无法通过完整性检查。不过,我正在寻找其他的解决方法。谢谢你提供的线索 - 它非常有帮助。不管怎样,看起来备份数据库时采用的方法导致了损坏。我们将更改这种方法,以避免未来出现此类问题。 - jefflunt
那么我假设你回去测试了原始副本并且它运行正常?我会在severoverflow上发布一个问题,并询问执行SQLite常规备份的批准方式。 - Nix
我修复了数据库,并尽可能地恢复了许多数据,使用了这个方法:http://community.spiceworks.com/how_to/show/1468 然后,我找到了一个看起来正确的方法。我会在ServerFault上发布这个方法,看看那里的人们是否同意这是一个好方法。 - jefflunt

3

我在使用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应用引擎:

$ dev_appserver.py --clear_datastore --clear_search_index

之后,它终于成功了。


2
在应用程序开发中,我发现消息来自频繁和大量的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)

1

我曾遇到一个问题:“database disk image is malformed”,这是因为使用当前版本不支持的较新的lib库创建的具有对象定义的数据库。

我想在这个问题上可能会有许多例子,而在我的情况下,是由于“GENERATED ALWAYS AS column”引起了“database disk image is malformed”的错误,因为lib版本 3.24.0不支持此功能。将lib更新到支持此功能的3.37.0版本可以解决此问题。


0

看起来您正在从文件被锁定的系统访问SQLITE文件,并且使用该文件的应用程序不允许读取该文件。 如果您尝试将文件复制到另一个位置,它将变得不完整,无法使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接