互联网应用随着业务的发展,部分单表数据体量越来越大,应对服务性能与稳定的考虑,有做分库分表、数据迁移的需要,本文介绍了vivo帐号应对以上需求的实践。

1. Canal 使用场景

1.1. 不停服迁移

业务在发展初期,为了快速支撑业务发展,很多数据存储设计较为粗放,比如用户表、订单表可能都会设计为单表,此时常规手段会采用分库分表来解决容量和性能问题。

但数据迁移会面临最大的问题:线上业务需要正常运行,如果数据在迁移过程中有变更,如何保证数据一致性是最大的挑战。

基于Canal,通过订阅数据库的 Binlog,可以很好地解决这一问题。

1.2. 缓存刷新

互联网业务数据源不仅仅为数据库,比如 Redis 在互联网业务较为常用,在数据变更时需要刷新缓存,常规手段是在业务逻辑代码中手动刷新。

基于Canal,通过订阅指定表数据的Binlog,可以异步解耦刷新缓存。

图片

1.3. 任务下发

另一种常见应用场景是“下发任务”,当数据变更时需要通知其他依赖系统。其原理是任务系统监听数据库变更,然后将变更的数据写入MQ/Kafka进行任务下发。

比如帐号注销时下游业务方需要订单此通知,为用户删除业务数据,或者做数据归档等。

基于Canal可以保证数据下发的精确性,同时业务系统中不会散落着各种下发MQ的代码,从而实现了下发归集,如下图所示:

图片

1.4. 数据异构

在大型网站架构中,数据库都会采用分库分表来解决容量和性能问题,但分库分表之后带来的新问题。比如不同维度的查询或者聚合查询,此时就会非常棘手。一般我们会通过数据异构机制来解决此问题。所谓的数据异构,那就是将需要join查询的多表按照某一个维度又聚合在一个DB中。

基于Canal可以实现数据异构,如下图示意:

图片

2. 实践一: 分库分表

2.1. 需求

图片
  • 难点:

    表数据量大,单表3亿多。

    常规定时任务迁移全量数据,时间长且对业务有损。

  • 核心诉求:

    不停机迁移,最大化保证业务不受影响

    给在公路上跑着的车换轮胎

2.2. 迁移方案

图片

2.2.1. 迁移过程

图片

整体过程大致如下:

  • 分析帐号现有痛点

  • 单表数据量过大:帐号单表3亿+

  • 用户唯一标识过多

  • 业务划分不合理

  • 确定分库分表方案

  • 存量数据迁移方案

使用传统的定时任务迁移,时长过长,且迁移过程中为了保证数据一致性,需要停机维护,对用户影响较大。确定使用canal进行迁移,对canal做充分调研与评估,与中间件及DBA共同确定,可支持全量、以及增量同步。

  • 迁移过程通过开关进行控制,单表模式 → 双写模式 → 分表模式。
  • 数据迁移周期长,迁移过程中遇到部分未能预估到的问题,进行了多次迁移。
  • 迁移完成后,正式切换至双写模式,即单表及分表同样写入数据,此时数据读取仍然在单表模式下读取数据,Canal仍然订阅原有单表,进行数据变更。
  • 运行两周后线上未产生新问题,正式切至分表模式,此时原有单表不再写入数据,即单表不会再有新的Binlog产生,切换后线上出现了部分问题,即时跟进处理,“有惊无险”。

3. 实践二: 跨国数据迁移

3.1. 需求

在vivo海外业务开展初期,海外部分国家的数据存储在中立国新加坡机房,但随着海外国家法律合规要求越来越严格,特别是欧盟地区的GDPR合规要求,vivo帐号应对合规要求,做了比较多的合规改造工作。

部分非欧盟地区的国家合规要求随之变化,举例澳洲当地要求满足GDPR合规要求,原有存储在新加坡机房的澳洲用户数据需要迁移至欧盟机房,整体迁移复杂度增加,其中涉及到的难点有:

  • 不停机迁移,已出货的手机用户需要能正常访问帐号服务。
  • 数据一致性,用户变更数据一致性需要保证。
  • 业务方影响,不能影响现网业务方正常使用帐号服务。

3.2. 迁移方案

图片

3.3. 迁移过程

  • 在新加坡机房搭建备库,主从同步 Binlog。
  • 搭建 Canal 的server及client端,同步订阅消费Binlog。
  • client端基于订阅的Binlog进行解析,将数据加密传输至欧盟GDPR机房。
  • 欧盟应用数据解析传输的数据,落地存储。
  • 数据同步完成后运维同事协助将上层域名的DNS解析转发至欧盟机房,完成数据切换。
  • 观察新加坡机房Canal服务运行情况,没有异常后停止Canal服务。
  • 通过业务方,帐号侧完成切换。
  • 待业务方同步切换完成后,将新加坡机房的数据清除。

4. 总结

4.1. 数据序列化

Canal底层使用protobuf作为数据数据列化的方式,Canal-client在订阅到变更数据时,为null的数据会自动转换为空字符串,在ORM侧数据更新时,因判断逻辑不一致,导致最终表中数据更新为空字符串。

4.2. 数据一致性

帐号本次线上Canal-client只有单节点,但在数据迁移过程中,因业务特性,导致数据出现了不一致的现象,示例大致如下:

  • 用户换绑手机号A。
  • Canal此时在还未订阅到此 Binlog position。
  • 用户又换绑手机号B。
  • 在对应时刻,Canal消费到更新手机号A的Binlog,导致用户新换绑的手机号做了覆盖。

4.3 数据库主从延时

出于数据一致性地考虑(结合帐号业务数据未达到需要分库的必要性),帐号分表在同一数据库进行,即迁移过程中分表数据不断地进行写入,加大数据库负载的同时造成了从库读取延时。

解决方案

增加速率控制,基于业务的实际情况,配置不同的策略,例如白天业务量大,可以适当降低写入速度,夜间业务量小,可以适当提升写入速度。

4.4 监控告警

在整体数据迁移过程中,vivo帐号在client端增加了实时同步数据的简易监控手段,即基于业务表基于内存做计数。

整体监控粒度较粗,包括以上数据不一致性,在数据同步完成后,未能发现异常,导致切换至分表模式下出现了业务问题,好在逻辑数据可以通过补偿等其他手段弥补,且对线上数据影响较小。

5. 拓展思考

5.1. 现有问题分析

图片

以上是基于 Canal现有架构画出的简易图,虽然基于HA整体高可用,但细究后还是会发现一些隐患,其中标记红色X的节点,可以视为可能出现的故障点。

  • zookeeper

    Zookeeper本身较为健壮,但不排除机房断电的可能从图中可以看到zookeeper异常对系统的影响是巨大的

  • canal-server

    canal集群模式已经通过 zookeeper 做了 HA, binlog 未持久化,所以canal的非常吃内存,如果使用不当可能导致整体内存溢出

  • canal-client

    理论上client节点有异常,不影响整体功能,得益于canal的消息ACK确认机制,消息仍然会再次消费,但在使用时需要注意,如果消息是多线程异步处理则很有可能漏掉消息另外不排除消息有重复投递的情况

  • mysql

    数据库基于主备实现高可用,如果效据库发生了主从切换,binlog 消费位置如何保证

5.2. 通用组件复用

基于以上可能出现的问题点,我们可以尝试做上图中的优化。

图片 图片

5.3. 延展应用-多数据中心同步

在互联网行业,大家对“异地多活”已经耳熟能详,而数据同步是异地多活的基础,所有具备数据存储能力的组件如:数据库、缓存、MQ等,数据都可以进行同步,形成一个庞大而复杂的数据同步拓扑,相互备份对方的数据,才能做到真正意义上”异地多活”。