使用WCDB中踩过的坑
一.遇到的问题
- 在版本覆盖安装时,发现有些数据丢失;后通过抓包查看沙盒数据库文件时,发现WCDB数据库某张表损坏,造成数据丢失。
二.调查问题过程
- 查看问题出现的版本4.7.6,首次安装此版本,安装时会先执行,内置数据库会被拷贝到沙盒中,由于此时有数据更新,会拉取接口,更新数据库文件,发现所有缓存数据没有问题;
- 首次安装上一个版本4.7.0,此时一样会先执行,内置数据库会被拷贝到沙盒中,由于此时有数据更新,会拉取接口,更新数据库文件,发现4.7.0版本,所有缓存数据也没有问题;
- 在第二步基础上,用4.7.6版本,覆盖安装4.7.0版本,此时,app内的执行顺序是:删除本地原有的数据库,把内置数据库会被拷贝到沙盒中,替换4.7.0版本的数据库文件,此时发现有数据更新,会拉取接口,更新数据库文件,问题出现了,WCDB中某张表损坏了,数据丢失。此时打印的日志如下:和查询表时提示一致;
- 对比4.7.0版本和4.7.6版本代码,发现4.7.6版本的某张表里数据的存储格式和4.7.0有区别;在执行内置数据库会被拷贝到沙盒中前,只删除了Cache.db文件,而Cache.db-wal和Cache.db-shm文件没有被删除,此时拉取接口更新数据库时,更新到Cache.db-wal,再缓存到Cache.db时,由于格式不一致,造成表损坏。
三.SQLite中的wal和shm
- wal全称Write Ahead Log 即预写日志,shm是日志索引文件;当每个事务对数据库执行修改时,会将修改后的日志存入到wal文件中,当事务读取数据库数据时,优先读取wal文件中读取,读不到再从数据库中读取。
- 如何做到读写并发操作???首先事务在写入时,是将新数据写入到日志文件中,不更新db,当事务提交时,也不影响db,只是将日志持久化了。这样看,最新的数据是在日志中的,在事务读取时,首先在日志文件中,找到最近的一个end-mark(事务提交节点),在这个节点之前查找对应的数据。如果找不到,再去db中查找,这样看在极端的情况下,每次查询都要在wal文件和db中各查找一次,影响性能。为了提高性能,wal-index文件(也就是.shm文件)用来记录page号和该page在wal文件中的偏移,并且wal-index文件采用共享内存,避免了对整个wal文件的查找(共享内存也有一个弊端就是SQlite的进程不能跨机器)。
- wal文件由32字节的首部和若干个帧组成,每个帧由24字节帧首部和一页数据组成。
四.总结
- 删除本地Cache.db文件时,要同时删除Cache.db-wal和Cache.db-shm文件;否则会出现,由于前后数据格式不一致时,造成数据库表损坏情况;
- 检测数据库损坏,做数据库修复或者删除重建库处理。