数据库作为二进制文件,当内部有部分数据不一致或丢失时,可能会发生数据库损坏。数据不一致或丢失的原因较多,可能是代码问题、操作系统或文件系统故障、磁盘损坏等等。从根源上,数据库损坏是不可能完全避免的。
因此,WCDB Swift 内建了修复工具,可以尽最大限度地将数据找回,减少数据丢失。
损坏检测
在监控与错误处理一章,已经提到了监控和处理错误信息了。损坏也可以用同样地方式监控,当错误类型为 SQLite
且错误码为 11 或 26 时,代表发生了数据库损坏,然后可以根据 tag
确认损坏的数据库,而后进行处理。
- Database.globalTrace(ofError: { (error: WCDBSwift.Error) in
- if error.type == .sqlite && (error.code.value == 11 || error.code.value == 26) {
- print("Tag: \(error.tag) is corrupted")
- }
- })
元数据备份
在有备份的情况,修复工具的能力将大大提升。在数据库内的数据发生变化时,元数据备份有可能会过期。因此建议在子线程定期对其备份。
- DispatchQueue.global(qos: .background).async {
- Timer.scheduledTimer(withTimeInterval: 5 * 60, repeats: true) {_ in
- let backupPassword = "backupPassword".data(using: .ascii)
- try? database.backup(withKey: backupPassword)
- }
- }
元数据通常只有几 kb 大小,且属于读操作,可以与其他操作并发执行。因此备份不会对性能产生大的影响。
数据修复
数据库修复通过 recover(fromPath:withPageSize:databaseKey:backupKey:)
接口完成。它将尝试从已损坏的数据库中读出数据,并插入到新数据库中。其函数原型为:
- func recover(fromPath source: String, // 已损坏的数据库路径
- withPageSize pageSize: Int32 = 4096, // 已损坏的数据库的page size
- databaseKey: Data? = nil, // 已损坏的数据库的密码,若未加密,则为 nil
- backupKey: Data? = nil) throws // 元数据备份的密码,若备份未加密,则为 nil
修复过程根据数据库的大小不同,需要一定的时间。建议在界面上提示等待,并在子线程进行修复。
- //view.startLoading()
- DispatchQueue.global(qos: .background).async {
- let newDatabase = Database(withPath: "newPath")
- try? newDatabase.recover(fromPath: pathToCorruptedDatabase,
- withPageSize: 4096,
- databaseKey: corruptedDatabaseKey,
- backupKey: backupKey)
- //DispatchQueue.main.async {
- // view.stopLoading()
- //}
- }
对于可再生的数据,如可从服务端重新拉取的数据,直接将数据库删掉重建是更好的恢复手段。