数据库损坏的可能原因
1. 数据库文件被其他线程覆盖或删除
- 在文件描述符关掉以后,继续使用这个文件描述符访问
- 打开文件,获取文件描述符fd(其实是一个整形)
- 关闭文件
- 打开sqlite文件,获取文件描述符(碰巧也是)fd
- 另一个线程继续使用fd,写文件
- sqlite文件被损坏
- 在事务进行过程中,进行数据库备份或恢复
在数据库事务过程中,数据库文件既包括老的内容,也包括新的内容。如果此时拷贝这个文件,数据库可能会被损坏。 备份数据库最好使用sqlite的api。 - 删除日志文件
日志文件中包括rollback需要的信息。删除以后,无法正确回滚,有可能会导致数据库损坏。
2. 文件锁相关
sqlite使用文件锁来在保证多线程访问。如果文件锁机制不正常,会导致同时读写文件等信息,导致数据库损坏
- 文件系统没有正确的实现文件锁的机制
在网络操作系统中,比较常见 - 不正确使用close()函数
在Unix中,close()函数会解除所有线程的文件锁。
例如A、B线程打开了数据库文件,使用sqlite的api。此时,线程C依次调用了open()
,read()
以及close()
。此时,这个文件的所有锁已经没了。因此A、B有可能会同时写数据到文件中。 - 两个进程使用不同的锁协议(locking protocols)
默认使用POSIX advisory locking
,可以用sqlite3_open_v2()
函数修改。如果不一致,可能发生同时读写,数据库损坏。 - 在数据库文件使用时rename或unlink
两个进程A、B,同时对一个数据库文件建立数据库连接。A关闭连接,unlink文件,用同样的名字创建一个新的数据库文件,在打开这个数据库。这样子A、B两个进程在使用不同的数据库,名字却是一样的。 然而,日志文件是根据数据库名字来区分数据库文件的。因此这两个进程的数据库文件会是同一个。导致数据库文件损坏。 - 一个文件有多个连接
也就是说一个数据库文件有多个名字。假如A、B使用不同的名字打开同一个数据库链接,会有两个日志文件。 如果线程A crash了,B检测到需要进行rollback。找不到日志文件,无法回滚。
3. sync失败
为了保证数据库文件的一致性(consistent),会调用fsync()
系统调用,把内存中的数据刷到磁盘中。如果这个sync操作失败,会导致数据库文件损坏
- sync系统调用和文档描述不一致
USB闪存经常这样子。例如写大数据时,在函数返回已写入成功时,USB的指示灯还在亮着。 - 使用PRAGMAs禁用sync
synchronous=OFF
可以提高速度,却会导致文件不一致。
4. 硬盘、闪存损坏
5. 内存损坏
当野指针、内存溢出等原因,可能导致内存中的数据库结构损坏,从而有可能导致数据库文件损坏。
当进行memory-mapped I/O时,由于内存直接映射到磁盘,如果发生数组越界等,内存中的数据损坏,磁盘文件也会损坏
6. 其他操作系统问题
- 文件系统崩溃 ##7.sqlite的bug
下起雨,也要勇敢前行