【问题标题】:SQLite3 database or disk is full / the database disk image is malformedSQLite3 数据库或磁盘已满/数据库磁盘映像格式错误
【发布时间】:2011-07-13 13:13:58
【问题描述】:

我的数据库大约 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 命令行实用程序来排除应用程序代码的任何问题?
  • 您能说得更具体一点吗(例如,您将从 CLU 运行哪个命令)?
  • 嗯,危机快结束了。我能够将损坏的数据库导出到一个新的数据库中,该数据库现在正在通过其完整性检查。我确认所有关键数据都是完整的(即“用户”表等)。我还确认(非常幸运)唯一无法恢复的实际上是一些我当然可以没有的微不足道的网站使用数据(页面浏览统计数据等)。有一些数据无法保存,这很重要,但我有一个备份,因此我可以通过 INSERT 重新导入它。所以,没有什么重要的损失;我还有一点工作要做。

标签: file sqlite filesystems corruption


【解决方案1】:

要修复损坏的数据库,您可以使用 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, '数据库磁盘映像格式错误' }

【讨论】:

  • 谢谢你一百万次 - 我花了几个小时创建一个 Core Data 数据库,它有这个错误代码 11 问题(在 iOS 4.3 但由于某种原因不是 4.2)并且不会加载,但是像这样转储到新创建的数据库中解决了这个问题。是的!
  • 这正是我所需要的。在运行 OE 的远程目标上进行开发工作。我试图复制数据库,但不知道它正在被写入,并且遇到了正在抛出数据库的错误。这解决了问题。
  • 向 Pegolon 大喊……刚刚用这个食谱保存了一位顾客的培根。非常感谢!
  • 我遇到了类似的问题,这为我指明了正确的方向,但仅仅进行转储是不够的......它正在生成一个 0 字节的新 sqlite 文件。我必须先将 sql 文件转储到文本文件中,然后删除一些明显来自 sqlite 系统的写有“错误”的行,然后使用该文本文件的 sql 创建一个 sqlite 文件。这对我有用。
  • 你也可以只使用 .clone newdb.sqlite
【解决方案2】:

需要考虑的几点:

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

  • 您不应该对 SQLite(或任何其他数据库,就此而言)使用传统的备份工具,因为它们没有考虑对确保数据库未损坏至关重要的数据库状态信息。特别是,在插入事务的中间复制数据库文件是灾难的根源......

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

  • 是的,您的数据库文件似乎是corrupted。这可能是硬件/文件系统错误。或者,也许您在使用它们时复制了它们?或者可能恢复了未正确备份的备份?

【讨论】:

  • 鉴于我看到的症状,它看起来像#3(备份正在使用的数据库的副本)。分页大小/数据库大小与您描述它的方式完全吻合。所以,我正在尝试修复它,或者从通过完整性检查的每日备份副本中恢复(谢天谢地,我有一个),然后更改我的备份方案。
  • 为了社区的利益,我最终使用此处概述的方法修复/恢复数据库:community.spiceworks.com/how_to/show/1468 我也有每日备份,所以这个潜在的“哦 sh*t”时刻是变成了一个相对较小的中断。
  • 对于那些使用 *nix 系统的人,我发现了一组类似于 @normalocity 在 spiceworks 中发现的指令:askubuntu.com/questions/30185/…
【解决方案3】:

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

#!/bin/bash

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

大多数情况下,当 sqlite 数据库格式错误时,仍然可以进行转储。这个转储基本上是很多重建数据库的SQL语句。

转储中可能缺少某些行(可能是因为它们已损坏)。如果是这种情况,缺失行的 INSERT 语句将被一些 cmets 替换,并且脚本将以 ROLLBACK TRANSACTION 结束。

因此,我们在这里所做的是进行转储(排除格式错误的行)并将 ROLLBACK 替换为 COMMIT,以便提交整个转储脚本而不是回滚。

这种方法已经挽救了我的生命 100 次 \o/

【讨论】:

    【解决方案4】:

    为避免一开始就出现“数据库或磁盘已满”,如果您有大量 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),因此请小心在代码中使用它。

    【讨论】:

    • 我在 /tmp 中有很多似乎没有使用的磁盘空间。我正在创建一个临时表,导致抛出“数据库或磁盘已满”错误。设置temp_store 使用内存解决了这个问题。我有 3GB 的内存和 1.6GB 的 /tmp。
    【解决方案5】:

    从 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'),
    }}
    

    【讨论】:

    • 事实上,对我来说,克隆比.dump 效果更好。
    • 我收到此错误:sqlite_sequence... Error: object name reserved for internal use: sqlite_sequence
    【解决方案6】:

    我看到数据库损坏时会发生这种情况,您是否尝试将其克隆到新数据库中?

    Safley copy a s SQLite db

    安全地复制 SQLite 数据库

    复制 SQLite 数据库非常容易。做起来不那么简单 这不会破坏它。方法如下:

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

    这将为您提供一个不错的干净备份,并且肯定会处于正确的状态 状态,因为在复制的中途写入数据库 过程是不可能的。

    【讨论】:

    • 这是有道理的,因为我们最近实现了数据库的每日备份(运行了大约 30 天),并且我们正在按照上面链接中指定的方式进行操作。我的应用程序中还有一个“健全性检查”功能,当出现问题时提醒我,警报开始响起,我突然出现了 ID 字段错误的行和其他奇怪的情况。
    • 我尝试了上述方法,但生成的数据库仍然未能通过完整性检查。不过,我正在寻找其他方法。感谢您的领导-这非常有帮助。无论哪种方式,看起来用于备份数据库的方法都导致了损坏。我们将对其进行更改,以免将来出现此问题。
    • 所以我假设你回去测试了原始副本并且它有效?我会向 severoverflow 发布一个问题,并询问批准的方式来执行 sqlite 的例行备份......
    • 我修复了数据库,使用以下方法尽可能多地恢复数据:community.spiceworks.com/how_to/show/1468 然后我找到了一种看起来正确的方法。我会在 ServerFault 上发布该方法,看看那里的人是否同意这是一个好方法。
    【解决方案7】:

    在使用 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
    

    之后它终于奏效了。

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        我有“数据库磁盘映像格式错误”尝试使用更高版本的 lib 版本创建且当前版本不支持的对象定义读取数据库。

        我想可能有很多这样的例子,在我的例子中,它是 GENERATED ALWAYS AS 列 - 它导致 lib 版本 3.24.0 出现“数据库磁盘映像格式错误”错误。将其更新到支持此功能的 3.37.0 - 已解决此问题。

        【讨论】:

          猜你喜欢
          • 2014-06-08
          • 1970-01-01
          • 1970-01-01
          • 2014-05-02
          • 1970-01-01
          • 1970-01-01
          • 2015-02-11
          相关资源
          最近更新 更多