SQLite 是一款轻型的嵌入式数据库它占用资源非常的低,处理速度快,高效而且可靠。在嵌入式设备中,可能只需要几百 K 的内存就够了。因此在移动设备爆发时,它依然是最常见的数据持久化方案之一。不过即使 SQLite 已经非常成熟,但是我们在编程中依然会遇到一些问题,其中最常见也最难搞的就是 —— 并发。
就像其他类似的问题一样,SQLite 在移动端的并发处理也存在多种不同的设计。下面我们通过 iOS 中四个常用类库 (SQLite.swift, FMDB, GRDB, Core Data) 来看看这些设计。不过在此之前,我们需要明确 SQLite 在并发编程环境下到底存在哪些问题:并发写操作:某一时刻可能存在对同一个数据库的写操作,而这是 SQLite 不允许的行为。
操作隔离:连续的两个数据库查询操作可能会出现结果差异,因为在并发环境下你无法保证着两个读操作中间不会出现写操作。
操作冲突:并发环境下数据库的新增和修改操作执行的时序并不一定与调用时序是一致的。这就导致一个可能的情形就是:数据库多个更新操作调用后可能存在一些意料之外的情形,而且你还难以追踪排除。
明确这些问题后,接下来我们就来看看这些类库做出了何种应对。
SQLite.swift 方案
SQLite.swift 采用了最简单粗暴的一种方案,使用者只会得到一个数据库连接,所有的操作都是在该连接上串下执行,类库的作者并没有提供数据库连接池类似的特性。通过这种设计,任意时刻都只会存在一个线程对数据库拥有访问权限。也就是说上诉第一个并发问题被完美解决了。
然而改方案却无法应对第二个问题。例如,我们需要为数据库中的某位用户设置头像,如果该用户存在时则执行插入操作,对应代码如下