0
点赞
收藏
分享

微信扫一扫

SQLite数据库损坏与修复


【引子】

平时用sqlite数据库比较少。被问到sqlite数据库损坏怎么办这个问题时就把我难住了,于是就整合了几篇比较好的文章,记录一下。

SQLite算是非常稳定的数据库,不容易出现损坏,就算应用程序崩溃,或者操作系统崩溃,甚至是执行事务时出现断电,都能在下一次使用数据库时自动修复。但是,还是不能避免不出现损坏的情况。

导致SQLite数据库损坏的情况

导致SQLite数据库损坏的情况大致可归结为4类:文件覆盖问题、文件锁问题、数据同步问题、内存问题

文件覆盖问题

SQLite数据库文件被覆盖是可能的,毕竟是一个普通的磁盘文件,意味着所有的进程都可以打开和覆盖,所以不可能完全避免文件覆盖的情况。

1. 多线程写数据库问题。

SQLite数据库是支持多进程并发读写,但是如果这时候关闭和重新打开数据库,就很可能出现一些线程还在写数据到数据库,出现部分数据被覆盖的情况。

2. 执行事务时备份或恢复数据

事务都是一个过程性的操作,需要一定时间,而数据备份是原子操作,如果在事务执行过程时备份,可能导致复制的内容包含了部分新的内容和部分旧的内容,就出现数据库损坏。恢复也是一样。

3. 删除日志文件

SQLite数据库通常都是存储所有内容到一个文件,但执行事务时,为了实现程序崩溃,断电时可以回滚日志,就伴随着一些附加的日志文件。如果日志被删除了,就会导致恢复出现异常。

文件锁问题

为了实现SQLite数据库并发读写,SQLite会使用文件锁来保证数据安全。

1. 系统文件锁问题

SQLite依赖于底层的文件系统对文件锁的实现,但是,一些文件系统存在锁逻辑错误,使得锁并不可靠,这在网络文件系统和NFS情况比较常见。

2. POSIX协同锁(advisory lock)

在linux 或者unix下,SQLite 默认锁是协同锁。当进程使用 协同锁,如果其中有一个线程执行 close() 就可能导致锁被取消。如果已经有两个线程同时连接到同一个数据库,再来一个线程不以SQLite API的形式,就是以系统文件形式读取数据库( open(), read() , 然后close()),就会导致这个进程的数据库锁被取消,而两个线程同时操作数据库就会导致数据覆盖引起错乱。

3. 不同的连接协议

不同的 连接协议锁也可能会不同,也就导致锁没有发挥错误引起错误。

4.当数据库正在使用时删除或重命名数据库文件

出现这种情况往往是在linux等类POSIX系统,windows下不会出现这个情况,而且同时有事务执行就会放大这个问题。

数据同步问题

为了保证数据一致性, SQLite有时候会请求操作系统将所有等待持久化的数据刷入磁盘,然后等待这个操作完成。

1.磁盘驱动器的同步请求可能是不可靠的

现有普通消费级别的磁盘驱动器 多数 都会谎报数据同步结果,以期望得到更高的写入速度。当数据刚到达磁盘缓冲区,还没真正写入氧化物介质,磁盘驱动器就报告内容已经安全写入。但是这时候断电、硬件复位就会导致数据同步失败。这种情况主要出现在闪存介质。

2.使用PRAGMAs会影响同步

通过设置PRAGMA synchronous=OFF, SQLite所有的同步操作都会被忽略。这使得SQLite运行得更快,但如果出现电源故障或硬件复位就会前面保存的所有数据。 如果单纯为了获得最大的数据可靠性和健壮性,SQLite可设置synchronous = FULL

内存问题

SQLite作为一个 C运行库, 和使用它的应用程序运行在同一个内存地址空间。这意味着,任何野指针,缓冲区溢出,堆损坏等都有可能损坏了 SQLite的数据结构,并最终导致数据库文件损坏。
另外,使用​​​内存映射I/O模型​​(如mmap)的时候,内存问题会变得更加严重。当数据库文件的一部分或全部被映射到应用程序的地址空间,虽然减少了文件IO操作,但是野指针可能访问并修改到任何部分的 映射空间 数据。

更多SQLite数据库损坏的原因可以看官网
​​​How To Corrupt An SQLite Database File​​。

  1. File overwrite by a rogue thread or process
    1.1. Continuing to use a file descriptor after it has been closed
    1.2. Backup or restore while a transaction is active
    1.3. Deleting a hot journal
    1.4. Mispairing database files and hot journals
  2. File locking problems
    2.1. Filesystems with broken or missing lock implementations
    2.2. Posix advisory locks canceled by a separate thread doing close()
    2.2.1. Multiple copies of SQLite linked into the same application
    2.3. Two processes using different locking protocols
    2.4. Unlinking or renaming a database file while in use
    2.5. Multiple links to the same file
    2.6. Carrying an open database connection across a fork()
  3. Failure to sync
    3.1. Disk drives that do not honor sync requests
    3.2. Disabling sync using PRAGMAs
  4. Disk Drive and Flash Memory Failures
    4.1. Non-powersafe flash memory controllers
    4.2. Fake capacity USB sticks
  5. Memory corruption
  6. Other operating system problems
    6.1. Linux Threads
    6.2. Failures of mmap() on QNX
    6.3. Filesystem Corruption
  7. SQLite Configuration Errors
  8. Bugs in SQLite
    8.1. False corruption reports due to database shrinkage
    8.2. Corruption following switches between rollback and WAL modes
    8.3. I/O error while obtaining a lock leads to corruption
    8.4. Database pages leak from the free page list
    8.5. Corruption following alternating writes from 3.6 and 3.7.
    8.6. Race condition in recovery on windows system.

修复损坏的SQLite数据库

前阵子由于分区空间满出现了sqlite3数据库文件损坏的现象,操作的时候报错:

​Error: database disk image is malformed​

这里记录一下修复的操作过程:

1.将数据库中的数据导出为sql语句文件

sqlite3 test_file.db #打开损坏的数据库文件
.output recovery.sql #设置输出文件
.dump # 导出文件
.q

2.将导出的recovery.sql文件最后一行ROLLBACK改为COMMIT;

vim recovery.sql

3.导入到一个新库中

sqlite3 fix.db  #打开用于导入修复后的数据库数据
.read recovery.sql #读取数据文件
.q

SQLite使用建议

这里有4点建议:

  1. 减少多进程或多线程操作,尽可能单线程写。
  2. 减少事务操作,减小事务复杂度,减少检查点
  3. 减少数据库的大小
  4. 避免使用PRAGMA synchronous=OFF

举报

相关推荐

0 条评论