微信SQLite数据库损坏恢复实践johnwhe(何俊伟)◊问题背景◊常规做法◊数据备份◊RepairKit◊组合方案SQLite恢复◊微信聊天记录只存客户端◊SQLCipher加密数据库问题背景◊SQLite概率性损坏◊1/20,000~1/10,000◊256MB~1%,1GB~1‰◊设备断电、kernelcrash、空间不足问题背景◊问题背景◊常规做法◊数据备份◊RepairKit◊组合方案SQLite恢复◊.dump命令◊来自SQLite命令行工具◊整个DB输出为SQL语句常规做法.dump输出◊打开损坏DB后使用◊遇到错误前一直输出◊遇到错误,跳到下一个表.dump用于恢复◊成功率仅~30%◊打不开DB◊打开了读不出数据.dump成功率◊问题背景◊常规做法◊数据备份◊RepairKit◊组合方案SQLite恢复◊主要考虑因素◊恢复成功率◊空间大小◊性能◊加密数据备份备选方案方案优点缺点文件复制+压缩实现简单非原子操作BackupAPI热备份稍慢.dump(SELECT输出)有选择备份备选方案测试:~50MB,10W条目,加密DB.dump:先压缩后加密→压缩率高48486.5大小(MB)3.23.292恢复耗时(秒)复制+压缩Backup+压缩dump+压缩143218备份耗时(秒)◊基于dump+压缩◊加密保存◊自定义二进制格式◊多线程流水线◊只备份不可恢复数据◊灭屏充电时备份我们的方案性能效果100%100%254%142%备份速度(条/秒)恢复速度(条/秒)原始dump实现我们的方案5469139161088154572%28%恢复成功恢复失败线上恢复率备份未覆盖备份损坏时效问题◊问题背景◊常规做法◊数据备份◊RepairKit◊组合方案SQLite恢复SQLite损坏情况41%59%表损坏初始化失败可以打开数据部分损坏无法打开数据可能没坏SQLite文件格式123456789101112131415DB文件14101312Page(定长)B-tree279B-treeHeader(100–16bytes)SQLiteHeaderMagic/KDFSalt(16bytes)sqlite_master表根节点(PageSize–100bytes)Page1sqlite_master节点sqlite_master节点sqlite_master节点PagePagePagesqlite_master表列说明typeB-tree类型(table/index)name表名/索引名tbl_name索引对应的表名root_pageB-tree根节点Page号sqlCREATETABLE/CREATEINDEX语句初始化流程打开文件读取Salt,根据密钥生成Key解码Page1,读取Header解析sqlite_master,生成Schema关键数据打开文件读取Salt,根据密钥生成Key解码Page1,读取Header解析sqlite_master,生成SchemaSalt【用于正确解密】PageSize【正确划分Page】root_page、SQL【正确访问表数据】◊序列化备份关键数据◊时机:Schema改变时◊内容:KDFSalt、PageSize、sqlite_master◊开销:12kB、10ms◊~350个table/indexMasterBackup◊最小化系统◊读数据,写新DB◊只包含核心逻辑◊高度容错◊Fallback到备份RepairKit78%22%成功修复无法恢复线上恢复率统计方法:按Page数◊问题背景◊常规做法◊数据备份◊RepairKit◊组合方案SQLite恢复组合方案RepairKit备份恢复dump尝试恢复最新数据遇到错误,填补缺漏前面都无法恢复,最后尝试◊WeChatDatabase◊加密数据库组件◊包含全部三种恢复方案◊Android/iOS◊其他特性与优化WCDB组件WCDB组件今天开源!关注我们的公众号欢迎Fork/Stars/PR/Issues