数据库恢复软件:虽然各大IT业界的领头羊都是鼓吹和倡导云端和大数据,但就目前来说,对于大多...
SQLiteDoctor最新版是一款很实用的数据库修复软件,它使用起来很简单,你只需选择要恢复的sqlite数据库和输出数据库文件。欢迎下载!
易于使用的向导
要开始,只需选择要恢复的sqlite数据库和输出数据库文件。如果损坏的数据库已加密,您可以选择设置加密密钥。
SQLiteDoctor支持所有标准的SEE sqlite加密:AES128,AES192和AES256。
表格面板
扫描数据库以查找所有表和所有rowid值。如果无法找到特定表的最大RowID值,则使用N / A值,您可以手动将该值设置为高于您知道在该数据库中为该表使用的最大rowid值的数值(如果不是输入的值超过使用的默认值100000)。
详细日志
恢复开始后,会生成详细的日志输出,以便让您了解当前执行的步骤。如果出现不可恢复的错误,将显示红色消息(带有总错误计数标签)。
MacOS 10.10.5或更高版本
Windows 7SP1 / 8.1 / 10或更高版本
SQLite数据库对腐败具有很强的抵抗力。如果应用程序崩溃,操作系统崩溃,甚至在事务中发生电源故障,则部分写入的事务应在下次访问数据库文件时自动回滚。恢复过程是完全自动的,不需要用户或应用程序的任何操作。虽然SQLite可以抵御数据库损坏,但它并不是免疫的。本文档描述了SQLite数据库可能损坏的各种方法。
1.文件被流氓线程或进程覆盖
SQLite数据库文件是普通的磁盘文件。这意味着任何进程都可以打开文件并用垃圾覆盖它。SQLite库没有什么能够防范这种情况。
1.1。关闭后继续使用文件描述符
我们已经看到多个文件描述符在文件上打开的情况,然后该文件描述符被关闭并在SQLite数据库上重新打开。后来,其他一些线程继续写入旧文件描述符,没有意识到原始文件已经关闭。但是因为SQLite重新打开了文件描述符,所以打算进入原始文件的信息最终会覆盖部分SQLite数据库,从而导致数据库损坏。
其中一个例子发生在大约2013-08-30,在Fossil DVCS的规范存储库中。在那种情况下,文件描述符2(标准错误)在sqlite3_open_v2()之前被错误地关闭(我们怀疑 是 stunnel ),因此用于存储库数据库文件的文件描述符是2.稍后,应用程序错误导致断言( )通过调用write(2,...)发出错误消息的语句。但由于文件描述符2现在已连接到数据库文件,因此错误消息覆盖了数据库的一部分。为防止出现此类问题,SQLite 版本3.8.1(2013-10-17)及以后拒绝对数据库文件使用低编号文件描述符。(请参阅SQLITE_MINIMUM_FILE_DESCRIPTOR 了解更多信息。)
Facebook工程师在2014-08-12的博客文章中报告了使用封闭文件描述符导致损坏的另一个例子 。
1.2。事务处于活动状态时备份或还原
在后台运行自动备份的系统可能会尝试在事务处理过程中制作SQLite数据库文件的备份副本。然后,备份副本可能包含一些旧内容和一些新内容,因此会损坏。
制作SQLite数据库的可靠备份副本的最佳方法是使用作为SQLite库一部分的备份API。如果不这样做,只要任何进程没有正在进行的事务,就可以安全地复制SQLite数据库文件。如果先前的事务失败,那么将任何回滚日志(* -journal文件)或预写日志(* -wal文件)与数据库文件本身一起复制是很重要的。
1.3。删除热门日记
SQLite通常将所有内容存储在单个磁盘文件中。但是,在执行事务时,在崩溃或电源故障后恢复数据库所需的信息存储在辅助日志文件中。这种日志文件被描述为“热门”。日志文件与原始数据库文件具有相同的名称,并添加了-journal或-wal后缀。
SQLite必须查看日志文件才能从崩溃或电源故障中恢复。如果在崩溃或电源故障后移动,删除或重命名热日志文件,则自动恢复将不起作用,并且数据库可能会损坏。
此问题的另一个表现是 由于使用不一致的8 + 3文件名而导致的数据库损坏。
1.4。错误配对数据库文件和热门期刊
前面的示例是更一般问题的特定情况:SQLite数据库的状态由数据库文件和日志文件控制。在静止状态下,日志文件不存在,只有数据库文件才对。但是,如果日志文件确实存在,则必须将其与数据库保持在一起以避免损坏。以下行为都可能导致腐败:
在两个不同的数据库之间交换日志文件。
使用不同的日志文件覆盖日志文件。
将日志文件从一个数据库移动到另一个数据库
复制数据库文件而不复制其日志。
用另一个数据覆盖数据库文件而不删除与原始数据库关联的任何热日志。
2.文件锁定问题
SQLite在数据库文件和预写日志或WAL文件上使用文件锁 来协调并发进程之间的访问。如果没有协调,两个线程或进程可能会尝试同时对数据库文件进行不兼容的更改,从而导致数据库损坏。
2.1。锁实现损坏或丢失的文件系统
SQLite依赖于底层文件系统来进行锁定,正如文档所说的那样。但是一些文件系统在其锁定逻辑中包含错误,因此锁定并不总是像宣传的那样。特别是网络文件系统和NFS尤其如此。如果在锁定原语包含错误的文件系统上使用SQLite,并且如果两个或多个线程或进程同时尝试访问同一数据库,则可能导致数据库损坏。
2.2。Posix咨询锁由一个单独的线程取消close()
SQLite在unix平台上使用的默认锁定机制是POSIX咨询锁定。不幸的是,POSIX咨询锁定具有设计怪癖,使其容易被滥用和失败。特别是,同一进程中具有持有POSIX顾问锁的文件描述符的任何线程都可以使用不同的文件描述符覆盖该锁。一个特别有害的问题是close()系统调用将取消所有线程和进程中所有文件描述符的同一文件上的所有POSIX顾问锁。
因此,例如,假设一个多线程进程有两个或多个线程,它们与同一个数据库文件有不同的SQLite数据库连接。然后出现第三个线程,并希望自己从同一个数据库文件中读取内容,而不使用SQLite库。第三个线程执行open(),read()然后close()。人们会认为这将是无害的。但是关闭()系统调用导致所有其他线程删除数据库中的锁。那些其他线程无法知道他们的锁刚刚被破坏(POSIX没有提供任何机制来确定这一点),所以他们继续运行,假设他们的锁仍然有效。这可能导致两个或多个线程或进程同时尝试写入数据库,从而导致数据库损坏。
请注意,两个或多个线程使用SQLite库访问同一SQLite数据库文件是完全安全的。SQLite的unix驱动程序知道POSIX咨询锁定怪癖并解决它们。只有当线程试图绕过SQLite库并直接读取数据库文件时,才会出现此问题。
2.2.1。SQLite的多个副本链接到同一个应用程序
正如前一段所指出的,SQLite采取措施解决POSIX顾问锁定的怪癖。部分解决方法涉及保持开放SQLite数据库文件的全局列表(互斥保护)。但是,如果SQLite的多个副本链接到同一个应用程序,那么这个全局列表将有多个实例。使用SQLite库的一个副本打开的数据库连接将不知道使用另一个副本打开的数据库连接,并且将无法解决POSIX顾问锁定问题。一个close()方法的一个连接操作可能在不知不觉中清除不同的数据库连接上的锁,导致数据库损坏。
上面的场景听起来很牵强。但是,SQLite开发人员已经知道至少有一个商业产品正好发布了这个错误。供应商来到SQLite开发人员寻求帮助,以追踪他们在Linux和Mac上看到的一些不常见的数据库损坏问题。这个问题最终被追溯到这样一个事实,即应用程序是连接两个单独的SQLite副本。解决方案是将应用程序构建过程更改为仅链接一个SQLite副本而不是两个副本。
2.3。两个进程使用不同的锁定协议
SQLite在unix平台上使用的默认锁定机制是POSIX建议锁定,但还有其他选项。通过使用sqlite3_open_v2()接口选择备用sqlite3_vfs,应用程序可以使用可能更适合某些文件系统的其他锁定协议。例如,可以选择在必须在不支持POSIX建议锁定的NFS文件系统上运行的应用程序中使用点文件锁定。
与同一数据库文件的所有连接使用相同的锁定协议非常重要。如果一个应用程序正在使用POSIX顾问锁,而另一个应用程序正在使用点文件锁定,则这两个应用程序将不会看到彼此的锁定,并且无法协调数据库访问,从而可能导致数据库损坏。
2.4。在使用时取消链接或重命名数据库文件
如果两个进程具有到同一数据库文件的打开连接,并且一个进程关闭其连接,则取消链接该文件,然后在其位置创建一个具有相同名称的新数据库文件并重新打开新文件,然后这两个进程将与不同的进程通信具有相同名称的数据库文件。(请注意,这仅适用于Posix和Posix类系统,它允许文件在读取和写入时仍然打开时取消链接.Windows不允许这样做。)由于回滚日志和WAL文件基于数据库文件的名称,两个不同的数据库文件将共享相同的回滚日志或WAL文件。其中一个数据库的回滚或恢复可能会使用其他数据库中的内容,从而导致损坏。
换句话说,取消链接或重命名打开的数据库文件会导致行为未定义且可能不合需要。
从SQLite 版本3.7.17(2013-05-20)开始,如果数据库文件在仍在使用时取消链接,则unix OS接口将向错误日志发送SQLITE_WARNING消息。
2.5。指向同一文件的多个链接
如果单个数据库文件有多个链接(硬链接或软链接),那么这只是说该文件有多个名称的另一种方式。如果两个或多个进程使用不同的名称打开数据库,那么它们将使用不同的回滚日志和WAL文件。这意味着如果一个进程崩溃,另一个进程将无法恢复正在进行的事务,因为它将查找适当日志的错误位置。
换句话说,打开和使用具有两个或多个名称的数据库文件会导致行为未定义且可能不合需要。
从SQLite 版本3.7.17(2013-05-20)开始,如果数据库文件有多个硬链接,则unix OS界面将向错误日志发送SQLITE_WARNING消息。
从SQLite 版本3.10.0(2016-01-06)开始,unix OS界面将尝试解析符号链接并按其规范名称打开数据库文件。在3.10.0版之前,通过符号链接打开数据库文件类似于打开具有多个硬链接的数据库文件并导致未定义的行为。
2.6。跨fork()进行打开的数据库连接
不要打开SQLite数据库连接,然后fork(),然后尝试在子进程中使用该数据库连接。将导致各种锁定问题,您很容易就会遇到损坏的数据库。SQLite不是为支持这种行为而设计的。必须在子进程中打开子进程中使用的任何数据库连接,而不是从父进程继承。
如果在父进程中打开了连接,甚至不要在子进程的数据库连接上调用sqlite3_close()。关闭底层文件描述符是安全的,但sqlite3_close() 接口可能会调用清除活动,这些活动将从父项下删除内容,从而导致错误甚至数据库损坏。
3.未能同步
为了保证数据库文件始终保持一致,SQLite偶尔会要求操作系统将所有挂起的写入刷新到持久存储,然后等待该刷新完成。这是使用Windows 下的unix和FlushFileBuffers()下的fsync()系统调用 完成的。我们将此备用写入的刷新称为“同步”。
实际上,如果只关注原子和一致写入并且愿意放弃持久写入,则同步操作不需要等到内容完全存储在持久性介质上。相反,同步操作可以被认为是I / O障碍。只要在同步之前发生的所有写入在同步之后发生的任何写入之前完成,就不会发生数据库损坏。如果同步作为I / O屏障运行而不是真正的同步,则电源故障或系统崩溃可能导致一个或多个先前提交的事务回滚(违反“ACID”的“持久”属性)但数据库至少会继续保持一致,这就是大多数人关心的问题。
3.1。不支持同步请求的磁盘驱动器
不幸的是,大多数消费级大容量存储设备都在于同步。磁盘驱动器一旦到达轨道缓冲区并且在实际写入氧化物之前就会报告内容在安全介质上是安全的。这使得磁盘驱动器似乎运行得更快(这对于制造商来说非常重要,因此他们可以在贸易杂志中显示出良好的基准数据)。并且公平地说,只要在轨道缓冲器实际写入氧化物之前没有功率损耗或硬复位,谎言通常不会造成伤害。但是,如果确实发生了断电或硬复位,并且如果这导致在同步到达氧化物之后写入的内容而在同步之前写入的内容仍在轨道缓冲区中,则可能发生数据库损坏。
USB闪存棒似乎是关于同步请求的特别恶毒的骗子。通过将大型事务提交到USB记忆棒上的SQLite数据库,可以很容易地看到这一点。COMMIT命令将相对快速地返回,表明记忆棒告诉操作系统并且操作系统告诉SQLite所有内容都安全地存在于持久存储中,但记忆棒末端的LED将继续闪烁几个更多秒。在LED仍在闪烁时拔出记忆棒会经常导致数据库损坏。
请注意,SQLite必须相信操作系统和硬件告诉它有关同步请求状态的任何信息。SQLite无法检测到任何一个正在撒谎并且写入可能无序发生。但是,WAL模式下的 SQLite对于无序写入比对默认回滚日志模式要宽松得多。在WAL模式下,失败的同步操作可能导致数据库损坏的唯一时间是在检查点操作期间。COMMIT期间的同步失败可能导致耐久性丢失,但不会导致损坏的数据库文件。因此,由于同步操作失败导致数据库损坏的一条防线是在WAL模式下使用SQLite并尽可能不频繁地检查点。
3.2。使用PRAGMA禁用同步
可以使用同步编译指示在运行时禁用SQLite执行以帮助确保完整性的同步操作。通过设置PRAGMA synchronous = OFF,省略所有同步操作。这使得SQLite似乎运行得更快,但它也允许操作系统自由地重新排序写入,如果在所有内容到达持久存储之前发生电源故障或硬重置,则可能导致数据库损坏。
为了获得最大的可靠性和针对数据库损坏的稳健性,SQLite应始终以其默认同步设置FULL运行。
4.磁盘驱动器和闪存故障
如果文件内容因磁盘驱动器或闪存故障而发生更改,则SQLite数据库可能会损坏。这是非常罕见的,但磁盘偶尔会在扇区中间翻转一下。
4.1。非电源安全闪存控制器
我们被告知,在一些闪存控制器中,如果在写入期间电源中断,则耗损均衡逻辑可能导致随机文件系统损坏。例如,这可以表现为在断电时甚至没有打开的文件中间的随机变化。因此,例如,当发生断电时,设备会将内容写入闪存中的MP3文件,这可能导致SQLite数据库损坏,即使数据库在断电时甚至没有使用。
4.2。假容量USB棒
有许多流通的USB棒在报告中具有高容量(例如:8GB),但实际上只能存储少量(例如:1GB)。尝试在这些设备上写入通常会导致不相关的文件被覆盖。因此,任何使用欺诈性闪存设备都很容易导致数据库损坏。诸如“假容量usb”之类的互联网搜索将会发现许多有关此问题的令人不安的信息。
5.记忆腐败
SQLite是一个C库,它在与它所服务的应用程序相同的地址空间中运行。这意味着应用程序中的杂散指针,缓冲区溢出,堆损坏或其他故障可能会破坏内部SQLite数据结构,并最终导致损坏的数据库文件。通常情况下,这些类型的问题在任何数据库损坏发生之前都表现为段错误,但是有些情况下应用程序代码错误导致SQLite发生故障,从而破坏数据库文件而不是恐慌。
使用内存映射I / O时,内存损坏问题变得更加严重。当全部或部分数据库文件映射到应用程序的地址空间时,覆盖该映射空间的任何部分的杂散指针将立即破坏数据库文件,而不需要应用程序执行后续的write()系统调用。
6.其他操作系统问题
有时,操作系统会出现可能导致问题的非标准行为。有时这种非标准行为是故意的,有时在实施中是错误的。但无论如何,如果操作的执行方式与SQLite期望执行的方式不同,则存在数据库损坏的可能性。
6.1。Linux线程
一些旧版本的Linux使用LinuxThreads库来提供线程支持。LinuxThreads类似于Pthreads,但在处理POSIX顾问锁方面略有不同。SQLite版本2.2.3到3.6.23认识到LinuxThreads正在运行时使用,并采取适当的措施来解决LinuxThreads的非标准行为。但是大多数现代Linux实现都使用了Pthreads的更新,更正确的NPTL实现。从SQLite 版本3.7.0(2010-07-21)开始,假设使用NPTL。没有检查。因此,如果在使用LinuxThreads的旧Linux系统上运行的多线程应用程序中使用,SQLite的最新版本将巧妙地出现故障并可能损坏数据库文件。
6.2。QNX上的mmap()失败
在QNX上存在mmap()的一些微妙问题,使得对单个文件描述符进行第二次mmap()调用可以使得从第一个mmap()调用获得的内存归零。unix上的SQLite使用mmap()为WAL模式下的事务协调创建共享内存区域,并且它将为大事务多次调用mmap()。已证明QNX mmap()在该场景下损坏了数据库文件。QNX工程师意识到了这个问题,正在研究解决方案; 你读这篇文章时可能已经解决了这个问题。
当QNX运行,建议内存映射I / O永远不会被使用。此外,要使用WAL模式,建议应用程序采用独占锁定模式,以便在没有共享内存的情况下使用WAL。
6.3。文件系统腐败
由于SQLite数据库是普通的磁盘文件,因此文件系统中的任何故障都可能破坏数据库。现代操作系统中的文件系统非常可靠,但仍然会发生错误。例如,在2013-10-01,持有Wiki for Tcl / Tk的SQLite数据库 在主机被转移到文件系统层中存在问题的(linux)内核的狡猾版本后几天就被破坏了。在那种情况下,文件系统最终变得如此严重损坏,以至于机器无法使用,但最早出现问题的症状是SQLite数据库损坏。
7. SQLite配置错误
SQLite有许多针对数据库损坏的内置保护。但是配置选项可以禁用许多这些保护。如果禁用保护,则可能会发生数据库损坏。
以下是禁用SQLite内置保护机制的示例:
如果出现操作系统崩溃或电源故障,设置PRAGMA synchronous = OFF会导致数据库损坏,但此设置可以避免因应用程序崩溃而造成的损坏。
在打开其他数据库连接时更改PRAGMA schema_version。
使用PRAGMA journal_mode = OFF或PRAGMA journal_mode = MEMORY 并在写入事务中使应用程序崩溃。
设置PRAGMA writable_schema = ON然后使用DML语句更改数据库模式可能会使数据库完全不可读,如果不仔细完成的话。
8. SQLite中的错误
SQLite经过了非常仔细的测试,以帮助确保它尽可能没有错误。在为每个SQLite版本执行的许多测试中,有一些模拟电源故障,I / O错误和内存不足(OOM)错误的测试,并验证在任何这些事件期间都没有发生数据库损坏。SQLite也经过现场验证,大约有20亿次活动部署,没有严重问题。
然而,没有软件100%完美。SQLite(现已修复)中存在一些可能导致数据库损坏的历史错误。还有一些还未被发现。由于SQLite的广泛测试和广泛使用,导致数据库损坏的错误往往非常模糊。应用程序遇到SQLite错误的可能性很小。为了说明这一点,下面给出了一个帐户,其中列出了在2009-04-01至2013-04-15四年期间SQLite中发现的所有数据库损坏错误。这个帐户应该让读者直观地了解SQLite中存在的各种错误,这些错误可以通过测试程序完成,并使其成为一个版本。
8.1。由于数据库收缩导致的虚假损坏报告
如果数据库是由SQLite版本3.7.0或更高版本编写的,然后由SQLite版本3.6.23或更早版本再次编写,以使数据库文件的大小减小,那么下一次SQLite版本3.7.0访问数据库文件,它可能会报告数据库文件已损坏。但是,数据库文件并没有真正损坏。版本3.7.0在腐败检测中过于热心。
问题已在2011-02-20修复。该修复程序首先出现在SQLite 版本3.7.6(2011-04-12)中。
8.2。在回滚和WAL模式之间切换后出现损坏
在一个进程或线程中反复切换进出WAL模式的SQLite数据库并在交换机之间 运行VACUUM命令会导致另一个打开数据库文件的进程或线程错过数据库已更改的事实。然后,第二个进程或线程可能会尝试使用陈旧缓存修改数据库并导致数据库损坏。
这个问题是在内部测试中发现的,从未在野外观察到过。问题已在2011-01-27和3.7.5版本中修复。
8.3。获取锁定时的I / O错误会导致损坏
如果操作系统在尝试在WAL模式下获得对共享内存的某个锁定时返回I / O错误,则SQLite可能无法重置其缓存,如果尝试后续写入,则可能导致数据库损坏。
请注意,只有在尝试获取锁定导致I / O错误时才会出现此问题。如果没有授予锁定(因为某些其他线程或进程已经存在冲突锁定),则不会发生任何损坏。我们不知道在尝试获取共享内存上的文件锁时,任何操作系统都会因I / O错误而失败。所以这是一个理论问题,而不是一个真正的问题。不用说,在野外从未观察到这个问题。在模拟I / O错误的测试工具中对SQLite进行压力测试时发现了这个问题。
对于SQLite版本3.7.3,此问题已于2010-09-20修复。
8.4。数据库页面从空闲页面列表中泄漏
从SQLite数据库中删除内容时,不再使用的页面将添加到空闲列表中,并重新用于保存后续插入添加的内容。在使用incremental_vacuum时,版本3.6.16到3.7.2中存在的SQLite中的错误可能会导致页面丢失在空闲列表之外。这不会导致数据丢失。但是这会导致数据库文件超出必要的范围。并且它会导致integrity_check编译指示报告空闲列表中缺少的页面。
对于SQLite版本3.7.2,此问题已在2010-08-23修复。
8.5。从3.6和3.7交替写入后的腐败。
SQLite版本3.7.0引入了许多SQLite数据库文件格式的新增强功能(例如但不限于WAL)。3.7.0版本是这些新功能的震撼发布。我们期待找到问题并且没有失望。
如果最初使用SQLite版本3.7.0创建数据库,然后由SQLite版本3.6.23.1编写,使得数据库文件的大小增加,然后由SQLite版本3.7.0再次编写,则数据库文件可能会损坏。
对于SQLite版本3.7.1,此问题已在2010-08-04修复。
8.6。Windows系统恢复中的竞争条件。
SQLite版本3.7.16.2修复了Windows系统上锁定逻辑中的细微竞争条件。当数据库文件需要恢复时,因为写入它的先前进程在事务中间崩溃并且两个或多个进程尝试同时打开该数据库,那么竞争条件可能导致其中一个进程获得错误指示已完成恢复,允许该进程继续使用数据库文件而不先运行恢复。如果该进程写入文件,则该文件可能会损坏。这种竞争条件显然存在于SQLite for Windows的所有早期版本中,可追溯到2004年。但竞争非常紧张。实际上,您需要一台快速的多核计算机,在该计算机中启动两个进程以在两个单独的核心上同时运行恢复。此缺陷仅在Windows系统上,并不影响posix OS界面。