跑 SQLite 查询不及时使用 sqlite3_finalize() 的严重后果,望周知。

本事件已被我投稿至纯靠北工程师(「#純靠北工程師 7ib 」)。

教训是:用 SQLite 跑完「每一笔」查询之后一定要「立刻」用 sqlite3_finalize(StatementPointer) 释放运存,不然会产生连 Xcode Instruments 都抓不到的运存泄漏。

事情是这样:

为了因应 Apple 对 8GB 运存的病态执着, 我最近刚刚给威注音输入法的原厂词库格式由 JSON 升级至 SQLite 。

我是第一次用 SQLite ,看这篇学的(威注音是用 Swift 开发的): itisjoe.gitbooks.io/swiftgo/con... 现在 Swift 用 SQLite 超方便,直接 import SQLite3 就行。 ( Linux 或者旧版 macOS 还可以直接读 SQLite3 的 almagation 。) 只是,窃以为我犯的最大错误就是「没有针对相关的 SQLite API 详读 SQLite 官方的手册」。

我一开始以为让所有的 SQLite Statement 共用一个(作为静态变数的) nullable OpaquePointer 就可以了, 于是我一开始的设计就是让辞典模组在 deinit() 的时候才跑一次「 sqlite3_finalize()」。 因为原厂词库是只读的、在 App Bundle 内,所以我将 journal 塞在了运存内。

结果呢?施工完毕之后,单元测试啥的都成功通过了,刚刚开启输入法时的运存占用是 18MB 左右。 当时我感觉很开心,因为这几乎是直接消灭了输入法启动时花在原厂辞典上的载入时间。 但是,周一上午,我用这输入法没打六分钟的字,输入法碰到了我之前设定的保险丝而自尽了: 我早前做了这么个保险设计:只要运存占用超过 768MB ,输入法就自尽、自动重新启动来释放运存。

然后,我用 Xcode 14.2 的 Instruments 抓运存泄漏, 但只抓到一个与系统输入法选单有关的小微型泄漏(还是无法解决的那种)。 正好我周一那天公司休假,我花了一整个周一的时间排查自己做过的所有修改, 最后开始怀疑「 sqlite3_finalize()」是不是得在每次查询之后都执行一次。 于是我补上了这句试了试:

swift 复制代码
extension vChewingLM.LMInstantiator {
  fileprivate static func querySQL(strStmt sqlQuery: String, coreColumn column: CoreColumn, handler: (String) -> Void) {
    guard Self.ptrSQL != nil else { return }
    defer { sqlite3_finalize(Self.ptrStatement) } // 补上了这句。
    sqlite3_prepare_v2(Self.ptrSQL, sqlQuery, -1, &Self.ptrStatement, nil)
    while sqlite3_step(Self.ptrStatement) == SQLITE_ROW {
      guard let rawValue = sqlite3_column_text(Self.ptrStatement, column.id) else { continue }
      handler(String(cString: rawValue))
    }
  }
...
}

结果发现运存泄漏的问题失踪了。 这问题幸好发现得早,不然威注音 3.6.2 正式版带着这个 Bug 发出去的话会坑死很多人。

然后我就将这次经历投稿到靠北工程师,但没想到反应非常空前。 居然还有人说我积阴德。还有人在转发时提到这么个例子:

超市的系统在频繁的查询资料后都会有整个系统延迟的状况,不知道跟这有没关系... 虽然资料库跟我没啥屁关系,也先备份再说。

于是我决定将这个故事也放在掘金这边让各位 SQLite 用户共勉。

相关推荐
cookqq11 分钟前
mongodb源码分析session异步接受asyncSourceMessage()客户端流变Message对象
数据库·sql·mongodb·nosql
呼拉拉呼拉23 分钟前
Redis故障转移
数据库·redis·缓存·高可用架构
什么都想学的阿超26 分钟前
【Redis系列 04】Redis高可用架构实战:主从复制与哨兵模式从零到生产
数据库·redis·架构
pp-周子晗(努力赶上课程进度版)1 小时前
【MySQL】视图、用户管理、MySQL使用C\C++连接
数据库·mysql
斯特凡今天也很帅1 小时前
clickhouse常用语句汇总——持续更新中
数据库·sql·clickhouse
超级小忍2 小时前
如何配置 MySQL 允许远程连接
数据库·mysql·adb
吹牛不交税2 小时前
sqlsugar WhereIF条件的大于等于和等于查出来的坑
数据库·mysql
hshpy3 小时前
setting up Activiti BPMN Workflow Engine with Spring Boot
数据库·spring boot·后端
文牧之4 小时前
Oracle 审计参数:AUDIT_TRAIL 和 AUDIT_SYS_OPERATIONS
运维·数据库·oracle
篱笆院的狗4 小时前
如何使用 Redis 快速实现布隆过滤器?
数据库·redis·缓存