一、项目背景
今年我们架构组主导的主要任务之一就是组织会员系统的分库分表操作,经过上半年的立项、改造、测试、联调、验收、演练,终于完成上线,会员系统是一个基础微服务,经过两年多的运行,目前已为很多系统提供基础服务,现在已不能进行停机升级操作,于是我们确定了不停机升级方案,经过演练最终上线完成。
二、分库分表实施
1. 现有数据库表梳理
按场景对会员库数据量大于1000万的表进行了梳理,根据不同表的使用情况进行了分析,根据分析的情况将大表的数据分两种处理方式,
- 一种是将表数据定期归档,这种情况不在此次讨论范围内;
- 另一种是进行分库分表操作。
会员库数据大表包括三种,
- 一种是历史数据归档表,这种表只是数据的归档存储,这类型表不需要分库分表;
- 一种是以前业务临时使用,现在已不使用的表,这部分表也不需要分库分表;
- 最后一种核心表,需要根据场景进行分库分表,主要是五张表
2.数据表拆分原则
经过对五张表的调用梳理发现,这五张表使用的字段为会员号、手机号、卡号和虚拟卡号这几个表字段,对此我们每张表确定一个主要拆分字段,需要关联查询的情况新建一张中间映射表,所以最后将五张表变成了八张表(含三张关联表)。主要拆分原则是:
- 分库分表需要考虑未来多租户等扩展场景,能支持按业务维度来进行切分;
- 会员分库分表主要考虑在线业务,离线业务不在考虑范围内
- 分库分表优先考虑优先级高的接口
3. 拆分策略
基于业务对拆分策略进行分析
根据2018年相关表的数据增长量统计为
序号 | 表名 | 2018年4月数据量 | 2019年3月数据量 | 增长量 |
1 | A表 | 15063400 | 23351297 | 8287897 |
2 | B表 | 14793400 | 23087874 | 8294474 |
3 | C表 | 21412500 | 33707846 | 12295346 |
4 | D表 | 83906300 | 99358704 | 15452404 |
5 | E表 | 21669200 | 32926241 | 11257041 |
五张表的增长量在800万-1500万之间,在未来5年预计表的总数据为
序号 | 表名 | 2019年3月数据量 | 未来5年总量 | 备注 |
1 | A表 | 23351297 | 63351297 | 按年800万增长量预估 |
2 | B表 | 23087874 | 63087874 | 按年800万增长量预估 |
3 | C表 | 33707846 | 93707846 | 按年1200万增长量预估 |
4 | D表 | 99358704 | 174358704 | 按年1500万增长量预估 |
5 | E表 | 32926241 | 87926241 | 按年1100万增长量预估 |
最后确定拆分策略为:
将表拆分的数据库数量为4个库,单个数据库分为10张表。
落库取模基数为4,落表取模基数为10
Hash(key)%4=m
Hash(key)%10=n
数据库 | n | 表 |
库1 | 0 1 2 3 以此内推 | 表1 表2 表3 表4
|
库2 | 同上 |
|
库3 | 同上 |
|
库4 | 同上 |
|
按此方案折分后五年各表数据量为
序号 | 表名 | 未来5年总量 | 未来5年各表管理数据量 | 备注 |
1 | A表 | 63351297 | 142万-174万 | 按各表10%分布误差进行计算 |
2 | B表 | 63087874 | 142万-174万 | |
3 | C表 | 93707846 | 210万-256万 | |
4 | D表 | 174358704 | 390万-480万 | |
5 | E表 | 87926241 | 198万-242万 |
未来5年最大单表管理数据约为480万,在mysql数据管理范围内,所以按以上方案拆分能满足未来5年业务预估增长量。
4. 改造方案
根据改造范围我们对五张表使用场景进行了梳理,梳理出所个场景使用的sql情况(dao层使用mybatis),特别是复杂sql的梳理,从以上梳理结果看可以分为几类:
- 涉及子查询以及left join等复杂查询的sql;
- 涉及非分库分表字段情况;
- 模糊查询
通过对梳理结果的分类整理,我们确定本次以下处理策略:
- 能取消的left join就取消,找业务确认
- 不能取消的接口改造,新增加dao层支持新的改造,(按四个步骤),聚合在atom层做
- 涉及跨表查询分页的放es去做
- 涉及非分库分表主键的去掉或在es处理,
- 模糊查询由es处理
- 前端配合调整
为了最小化改造,我们增加了Es和聚合层(主要是对复杂sql的聚合功能),并对原有dao层进行改造
5. 实施阶段
- 梳理现在DAO,对应表,清理代码,确定需要增加的DAO(拉分支)
- 折分jar包(dao重构)
- 新加DAO
- 开发逻辑
- 测试
- 验收
- 演练
- 上线
三、生产切换方案及数据迁移
生产我们切换采用四个阶段,通过设置枚举类指定开关状态
0 初始状态,单表及相关操作
1 双写,查询按单表(旧)的方式实现
2 双写,查询按分表(新)的方式实现
3 单写(新),查询按分表(新)的方式实现
上线的数据迁移开发了一个的数据迁移程序,采用java多线程调用存储过程的方式。本次上线为了保证业务一直在线,我们在上线方案中使用了Canal+proxy的方案
主要分为三部分,
- 分库数据迁移。分库是新增加的库,可以提前进行数据迁移,使用Canal作为从库的一个从库来监听从库数据变化
- 停主从同步;
- 运行数据迁移程序
- 将从库binlog重置
- 搭建Canal+proxy同步
- 重新开始主从同步
- 初始状态为0的版本发布。
- 状态切换为1。
- 最新数据状态同步,断开Canal+proxy
数据切换中遇到shardingsphere的坑:
- 4.0.0以下版本的sharding-proxy不支持基于字符的分表策略
- 4.0.0-rc1的版本在分表时出现部分字段解析为null,使用的解析类为org.apache.shardingsphere.core.parse.old.parser.expression.SQLTextExpression,在shardingshpere中有提issue,在rc2的版本中解决了,参考:https://github.com/apache/incubator-shardingsphere/issues/2696