为什么要从 FMDB 迁移到 WCDB?

作者:sanhuazhang

背景

WCDB 开源至今已两个月有余,我们在不断迭代功能、完善文档的同时,也与来自世界各地的开发者进行交流,帮助他们更快地了解、掌握 WCDB 。这其中,也不乏使用 FMDB 的开发者。他们正准备将项目的数据库模块改为 WCDB 。

对于一个已经上线运行的项目,数据库这类基础组件与业务的耦合通常较多,迁移有一定工作量的。因此,开发者通常会做很多预研,以确定是否进行迁移。

WCDB 在 Github 的 wiki 上提供了专门的教程,帮助使用FMDB的开发者进行迁移。同时,也希望通过本文全面地介绍 WCDB 和 FMDB 在使用方式、性能等方面的差异,以及迁移中可能遇到的问题,帮助开发者决定是否进行迁移。

平滑迁移

文件格式

由于 FMDB 和 WCDB 都基于 SQLite ,因此两者在数据库的文件格式上一致。用 FMDB 创建、操作的数据库,可以直接通过 WCDB 打开、使用。因此开发者无需做额外的数据迁移。

表结构

WCDB 提供了 ORM 的功能,将类的属性绑定到数据库表的字段。在日常实践中,类的属性名和表的字段名通常不一致。因此,WCDB 提供了 WCDB_SYNTHESIZE_COLUMN(className, propertyName, columnName) 宏,用于映射属性名。

对于

  • 表:CREATE TABLE message (db_id INTEGER, db_content TEXT)
  • 类:

这里表字段都加了"db_"的前缀,并且使用了不一样的字段名。通过 WCDB 的 ORM,可以映射为

通过 WCDB_SYNTHESIZE_COLUMN 宏映射后,WCDB 同样能兼容 FMDB 的表结构,开发者也不需要做数据迁移。

因此,开发者可以平滑地从 FMDB 迁移到 WCDB 。

性能比较

对于已经上线运行的项目,解决性能瓶颈会是一个常见的迁移理由。相较于 FMDB 直白的封装, WCDB 上到 OC 层的 ORM ,下到 SQLite 源码,都做了各类性能优化。

为了验证优化效果,我们提供了 benchmark ,并将性能测试结果和测试代码上传到了 Github 。同时, benchmark 中也加入了 FMDB 的测试代码,用于横向比较。

以下性能测试均为 WAL 模式、缓存大小2000字节、页大小 4 KB:

  • PRAGMA cache_size=-2000
  • PRAGMA page_size=4096
  • PRAGMA journal_mode=WAL

测试数据均为含有一个整型和一个二进制数据的表: CREATE TABLE benchmark(key INTEGER, value BLOB) ,二进制数据长度为 100 字节。

读操作性能测试

写操作性能测试

批量写操作性能测试 (事务)

对于读操作,SQLite 速度很快,因此封装层的消耗占比较多。 FMDB 只做了最简单的封装, 而 WCDB 还包括 ORM 、 WINQ 等操作,因此执行的指令会比 FMDB 多,从而导致 WCDB 在读操作上性能劣于 FMDB 5%

而写操作通常是性能的瓶颈,我们重点对其做了许多针对性的优化,使得 WCDB 写操作优于 FMDB 28%、批量写操作优于 FMDB 180%

多线程读并发性能测试

多线程读写并发性能测试

多线程写并发性能测试

WCDB 多线程并发的优势,将读操作的性能劣势拉了回来,使得在读操作 WCDB 的性能与 FMDB 基本持平 ,而 多线程读写性能则优于 FMDB 62%

在多线程写操作的测试中, FMDB 直接返回错误 SQLITE_BUSY,无法完成。

初始化性能测试

SQLite 连接的初始化速度会随着数据库内表的数量增加而逐渐上升, WCDB 也针对这个场景做了优化。相较于没有优化的 FMDB , WCDB 的初始化速度有 107% 的性能优势。

易用性比较

与已经上线运行项目不同,新项目更关注开发的效率。此时数据库的易用和便捷更重要。 对于等价的功能, WCDB 所需的代码量往往会比 FMDB 少很多。而更少的代码量通常意味着更快的开发效率和更少的错误。

基础操作

ORM 是现代客户端数据库比较普遍的功能。 CoreData 、 Realm 都支持 ORM , WCDB 也不例外。

FMDB 因其直白的封装,没有提供该功能。但在设计数据库表时,开发者通常会对数据进行建模。因此开发者只需将已有建模用 WCDB 的 ORM 表达出来即可。

对于在 FMDB 的一组定义:

  • 表:CREATE TABLE message (localID INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, createTime INTEGER, modfiedTime INTEGER)索引:CREATE INDEX message_index ON message(createTime)
  • 类:

WCDB需要对其建模,可以定义为

其中:

  • WCDB_IMPLEMENTATION(className) 用于定义进行绑定的类
  • WCDB_PROPERTY(propertyName) 和 WCDB_SYNTHESIZE(className, propertyName) 用于声明和定义字段。
  • WCDB_PRIMARY_AUTO_INCREMENT(className, propertyName) 用于定义主键且自增。
  • WCDB_INDEX(className, indexNameSubfix, propertyName) 用于定义索引。

虽然 WCDB 多了一步 ORM 的操作,但这是一劳永逸的,并且会给我们后续的使用带来很大的便利。 经过 ORM 的类,大部分操作都只需要一行代码即可完成。 Talk is cheap,直接看代码对比:

查询操作

插入操作

可以看到,

  • 对于查询操作, FMDB 需要进行很多拼装组合,而 WCDB 只需要一行代码就能完成。
  • 对于插入操作, FMDB 也只用了一行代码,但其需要将 property 逐个拆分为最基本的类型。而 WCDB 所需要关注的只有 object 和表名两个参数。

数据库升级

SQLite 的数据库升级一直是一个比较繁杂的问题。

通常的做法是,开发者自行定义一个版本号,并保存下来。数据库创建时每次检查版本号,若版本号较低,则对其字段进行升级,并更新版本号。但在多个版本的增增减减之后,版本的处理逻辑会越来越复杂,甚至可能弄错表内哪些字段是新增的,哪些是废弃的。

WCDB 将数据库升级和 ORM 结合起来,对于需要增删改的字段,只需直接在 ORM 层面修改,并再次调用 createTableAndIndexesOfName:withClass: 接口即可自动升级。以下是一个数据库升级的例子。

删除字段

如例子中的 createTime 字段,删除字段只需直接将 ORM 中的定义删除即可。

增加字段

如例子中的 aNewProperty 字段,增加字段只需直接添加 ORM 的定义即可。

修改字段类型

如例子中的 content 字段,字段类型可以直接修改,但需要确保新类型与旧类型兼容。

修改字段名称

如例子中的 aNewModifiedTime ,字段名称可以通过 WCDB_SYNTHESIZE_COLUMN(className, propertyName, columnName) 重新映射。

增加约束

如例子中的 WCDB_UNIQUE(Message, aNewModifiedTime) ,新的约束只需直接在 ORM 中添加即可。

增加索引

如例子中的 WCDBINDEX(Message, "``newIndex", aNewProperty) ,新的索引只需直接在 ORM 添加。

多线程操作

WCDB 与 FMDB 都支持多线程操作。 在 FMDB 内,当开发者需要进行多线程操作时,需要使用另外一个类 FMDatabasePool来进行操作。 而 WCDB 基础的 CRUD 接口都支持多线程,因此开发者不需要额外关心线程安全的问题。同样的, WCDB 多线程使用的代码量也比 FMDB 少得多。

功能完整性比较

加密

WCDB 基于 SQLCipher 提供了加密功能

统计

WCDB 内提供统计的接口注册获取数据库操作的 SQL 、性能、错误等,开发者可以将这些信息打印到日志或上报到后台,以调试或统计

修复

WCDB 提供了数据库修复工具,以应对数据库损坏无法使用的极端情况。

总结

与 FMDB 对比, WCDB 使得开发者可以写更少的代码,但能获得更高的性能。开发者不需要额外关注数据库升级和多线程操作的问题。同时, WCDB 还提供了加密、统计、修复等功能。 因此,对于新项目,我们推荐使用 WCDB ,以获得更好的性能和开发效率。对于已经上线、稳定运行的项目,如果遇到性能瓶颈,或者对加密、统计、修复等功能有需求,我们建议参考 Github 上的文档进行迁移。 后续我们还将加入更多的功能,欢迎来 Github 关注我们。

本文来源于:WeMobileDev 微信公众号

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏熊二哥

MyBatis快速入门

这部分内容不是很多,主要在于具体的应用,之后会针对痛点持续更新。 ? 概念 相对于全自动映射框架Hibernate,MyBatis是一个半自动映射框架,其需...

1976
来自专栏软件开发

MyBatis学习总结(二)——MyBatis核心配置文件与输入输出映射

在上一章中我们学习了《MyBatis学习总结(一)——ORM概要与MyBatis快速起步》,这一章主要是介绍MyBatis核心配置文件、使用接口+XML实现完整...

893
来自专栏coolblog.xyz技术专栏

MyBatis 源码分析 - 插件机制

一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展。这样的好处是显而易见的,一是增加了框架的灵活性。二是开发者可以结合实际需求,对框架进行拓展...

813
来自专栏用户画像

mysql模拟题二

  3) MSSQLServer2005Enterprise Edition是哪一种版本?

806
来自专栏数据和云

故障分析:数据库一致性关闭缓慢问题诊断

想必我们大家都知道,Shutdown immediate即一致性关闭数据库,数据库下次启动不需要做实例恢复即可open数据库。那么当数据库一致性关闭出现缓慢等状...

2865
来自专栏张善友的专栏

Net Framework 2.0 事务处理

事务 处理事务是构建许多业务逻辑的一个重要方面。 .NET Framework 2.0 中的事务 在 .NET Framework 2...

1678
来自专栏一个会写诗的程序员的博客

spring boot 集成mybatis 注解版查询

spring boot 和 mybatis已经正常集成,在使用查询时使用的是注解,(项目没有任何XML文件)

781
来自专栏软件开发

MyBatis学习总结(二)——MyBatis核心配置文件与输入输出映射

在上一章中我们学习了《MyBatis学习总结(一)——ORM概要与MyBatis快速起步》,这一章主要是介绍MyBatis核心配置文件、使用接口+XML实现完整...

632
来自专栏情情说

深入浅出MyBatis:MyBatis解析和运行原理

上一篇介绍了反射和动态代理基础,主要是为本篇文章做个铺垫,反射使配置和灵活性大大提高,可以给很多配置设置参数,动态代理可以在运行时创建代理对象,做一些特殊的处理...

3457
来自专栏芋道源码1024

数据库中间件 MyCAT源码分析 —— XA分布式事务

---- 1. 概述 2. XA 概念 3. MyCAT 代码实现 3.1 JDBC Demo 代码 3.2 MyCAT 开启 XA 事务 3.3 MyCAT...

3449

扫码关注云+社区