数据库水平拆分复盘小结

最近完成了一张近亿数据的拆分,其中遇到了一些问题,值得记录并改进。

交互调整

被拆分的是一张结算交易表。根据其基本特性,决定对其按创建年份拆分。由于数据在创建后会有大量更新,所以无法使用ES技术进行跨年搜索。和业务协商后,可以对现有业务做一定调整——将分页查询需求控制在单个年份内。 在这里插入图片描述
在交互上做出一定微调后,用户仍可以正常查询历史年份数据数据。只是查询体验有一定变动。

数据层改写

由于被拆分的表上有两个同时被业务使用的主键。评估使用shardingsphere中间件处理有一定难度,决定还是通过代码改造原有的数据层。这种方案的优点是灵活,也相对简单。缺点是无法推广到其他系统中。
表上的两个主键一个交易编号中直接含有年份数据,故可以很方便的检索到分表。另一个主键则是单调自增的ID,为了优化他的相关查询,还需要维护一张ID和年份对应关系的表,方便快速定位分表。
一些统计类的SQL,原先是全表遍历实现的。那么改写后将有程序数据层完成遍历。
总的来说数据层改写相对直接和简单。

数据迁移

出于降低业务稳定性考虑,水平拆分任务不能长时间停服。所以就必须面对水平拆分后的数据如何初始化,初始化后如何与和主表保持一致的问题。
数据初始化如果使用SQL批量插入,由于数据量大,瞬时造成的数据库压力会过高,故被否决。实际是选择了相对灵活的python脚本来完成数据初始。

cursor.execute("insert ignore Trade_" + year + " select * from Trade where id >=" + str(j) + " and id <" + str(i) +
               " and timeCreated >= '" + yearStart + "'" + "and timeCreated < '" + nextYearStart + "'")

数据初始化离最终上线还有一段时间,这个时间内数据仍在不停的插入和更新。依靠批量脚本无法满足数据同步的要求。需要一个相对实时的同步方式。
最初我们选择的是基于debezium来同步的mysql binlog。使用kafka接收binlog来处理数据的变更,但线上实用后,同步任务总是不明原因崩溃,由于项目时间紧,只能被迫放弃此方案。
备用方案是在原有的老数据层上发出MQ消息,携带变更的ID来通知数据变更,接收到消息后驱动分表数据更新。这个方案会侵入原有的业务代码,但老数据dao层后续将废弃,也不会造成太大影响。实际使用中,通过MQ监听数据变更最大的问题是,MQ往往太快了,数据层的事务在未真正提交时就已经开始处理。这造成监听程序获得数据并非最新的数据。

if (TransactionSynchronizationManager.isSynchronizationActive()) {
          TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
              @Override
              public void afterCommit() {
                  try {
                      historyTradeDaoDispatcher.sendMsg(String.valueOf(id));
                  } catch (IOException e) {
                      LOGGER.error("HISTORY_MQ_EX ", e);
                  }
              }
          });
      } 

通过spring事务管理回调可以规避部分问题。但当原代码并未开启事务时此方式无效,所以对原有非事务代码还需要增加事务。

数据检查

数据同步有时延和其他异常,需要有工具去检查同步进度和质量。使用的dbforget软件只支持同名表的比对,并且没有自动修复功能。还是考虑使用python实现分表数据和主表的比对。根据数据的修改时间(注意修改时间如果仅到秒可能无法反映先后关系,相同时间也要以主表为准)。将主表中更新的数据同步至分表。

程序上线

新的程序上线后,所有操作将基于新的分表进行。由于分表数据已提前初始化,新服务是立即可以对外使用的。而此时之前的数据迁移功能必须关停,否则会将历史表较旧的数据复制到分表。

数据修复

再次执行数据检查脚本,将切换过程中未同步的数据进行同步。

小结

目前的方案在不停服务的前提下,可以降低切换过程中同步时延造成的数据问题。在业务低谷时如果评估数据变动不大情况下,可以执行切换操作。事后通过脚本检查并修复数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值