背景

每个动作都会生产一条动态数据,如今已经生成了一千多万条数据,而且正以每天好几万的速度迅速增长,频繁的读写导致 RDS 数据库实例压力非常大,该库还有核心业务的数据,为了避免对核心数据的影响,决定将其分出来。

结合其业务特点,决定使用 MongDB,那么第一个问题就是如何同步这些数据了。

方案一 CDC

不能停止服务,还不能影响业务,所以选择通过 SQL Server 的 CDC 功能进行同步,先不用更改操作数据的代码,继续往 SQL Server 中写入,再将数据同步到 MongoDB,然后把查询的改用 MongoDB, 最后把操作数据的业务改用 MongoDB,如此即可实现数据的无缝迁移。当万事俱备的时候,发现阿里云的 RDS 实例需要 sa 权限才能开启 CDC, 而用户是没有 sa 权限的,因此不得已只好放弃这个方案。

方案二 timestamp

为了实现高速换轮胎,我们找到了另一个解决方案,通过给每行记录 DateModified 时间,但是这样就需要先更改业务代码,在每次增删改中记录操作时间,这个工作量是有难度的,因为有两个版本的 API 在不同的环境中运行的,修改代码不难,难的是需要在不同的环境中测试,保证线上业务不能崩溃,这个成本是比较高的。就在游移不定的时候,发现了 SQL Server 的 timestamp 类型,它是一个连续的数字,在增改的时候自动更新值,如此就不用更改业务代码了,直接在表中增加一个 timestamp 类型的字段即可,然后按照该字段 DateModified 进行同步数据即可。

尝试使用了 alibaba 开源的 DataX,结果发现对 GUID 类型的支持不是很好,无法满足我们的需求,因此果断放弃,还是自己来写代码实现。

大概思路如下,同步程序只需要从 SQL Server 中 select 出来,同步到 MongoDB 中,记录一下这次同步的最大 timestamp,隔一分钟后继续重复,即使遇到异常,也只要记录一下,不能中断程序。 最好不要通过 DataReader 去读,因为数据量大会长时间占用数据库链接。

当然,还要对 DateModified 字段建索引,否则同步速度会很慢。索引要在访问量低谷的时候建,不能影响线上性能。

使用GeneicHost方式的控制台程序,命名为Feeds.Syncer,部署在 MongoDB 所在的服务器,只能跑一个实例,防止多个同步出现冲突。

当数据同步完成之后,就可以逐步将数据操作转移到 MongoDB 了,这一步不着急,可以慢慢来。

接下来最重要的是将查询业务改成从 MongoDB 查询,业务逻辑都改完之后,发现查询速度比 SQL Server 还慢,原来 MongoDB 也是需要建索引的,而且索引功能非常强大,从官方文档中可以看到它支持各种索引,复合的,条件的,联合的等等,分别针对不同的场景。

针对我们的业务场景准备好要建的索引,MongoDB 提供了方法来判断查询是否用到了索引,和 SQL Server 的查询计划有点类似,据此可以判断索引是否建对了。直接建立索引可能会对性能产生影响,所以要分而治之,依次对 MongoDB 集群中的实例停止,独立运行,建立索引,停止,添加到集群中运行。

有了索引加持后,果然查询速度就有了明显的改善。