LiWenGu/smooth_switch_datasource

平滑切库的测试项目,重点在于业务双写,数据源的测试案例。对应文档解析:https://www.yuque.com/liwenguang/ogxvf1/ny150b

★ 1Forks 2JavaGitHub ↗Compare

README

平滑迁库的思考与测试项目

对应文档解析地址:https://www.yuque.com/liwenguang/ogxvf1/ny150b


平滑切库

基于 SpringBoot2.X
做平滑双写先要考虑,如何在一个应用中支持多数据源,以及多数据源的事务问题:失败一起失败、成功一起成功

1. 理解 Spring 事务注解


当一个 @Transaction 注解中的方法里中有多个数据源时,是不能切换数据源的,默认在该事务中,拿的是第一个数据源的事务管理器执行,当然你可以使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 方法来让某个事务内部的方法强制切换数据源,但是不会回滚。

2. 理解数据源切换


因为数据源切换是通过切面+ ThreadLocal 实现,因此功能也会受到 ThreadLocal 限制,比较典型的就是父子线程的变量和线程池的变量问题,如果想支持这种级别的数据源切换,建议使用开源工具:transmittable-thread-local。关于数据源切面保存的策略,大致有如下策略

  1. 切面before:put 数据源,切面 after:remove 数据源
  2. 切面 before:put 数据源,切面 after:不操作
  3. 切面 before:使用 stack put 数据源,切面 afer:stack pop 数据源


最佳方案是使用 stack 保存每次切面的数据源,并每次执行完后 pop,这样能在复杂的切数据源场景,能避免出现数据源乱掉的情况,并且在复杂的全链路上也支持实时的数据源监控

3. 理解平滑


什么是平滑,就是不停机,不影响现在的线上用户!说的简单,但是做起来还有很多要注意点,总结如下:

  1. 由于是切库,因此会有源库(老库)O 和目标库(新库)N 的区分

后面无特殊说明,O 代表源库,N 代表新库

  1. O 库的老数据,需要全量同步到 N 库,此时可以通过 binlog 实现,具体使用 canal/otter 或阿里云商业版 DTS 实现。
  2. 业务上新的数据改动(Insert/Update/Delete 等)需要在业务上对 O/N 库同时生效
  3. 对现有业务代码不能有过大影响改动,即对业务透明

4. 理解切库


什么是切库,就是换个新库,既然换个新库,就要理解新库和老库的区别

暂时默认为 MySQL 从 MySQL

  1. MySQL 版本的问题,如果 O 和 N 相差版本过大,需要看下 MySQL 的版本更新记录,尤其是索引这种是否有改动什么的,最好是通过模拟线上流量进行压测,来查看是否是正常的
  2. 切库我们这边是从 MySQL 到 DRDS(分库分表中间件,类似 Cobar/MyCat),会有部分 SQL以及索引特性不支持和更新。具体要看具体 O/N 库的区别
  3. O/N 库一些通用配置最好能一样或 N 库更高,例如像最大数据源,最小数据源,慢 SQL 日志、慢 SQL 阈值、TIMEOUT 等,数据库的一些自定义配置(谨慎,因为某些老库的配置可能是当年为了解决某个 bug 而配的,切到新库能一模一样是最好的)

5. 测试代码


基于:src/test/http/rest-api.http

  1. 没有事务,没有子线程,库切换表现正常
  2. 有事务,没有线程切换,库切换异常,并且异常后,第一个正常库是正常回滚的
  3. 有事务,没有线程切换,但是第二库使用了 @Transactional(propagation = Propagation.REQUIRES_NEW) ,因此可以切换库成功。但是因为挂起原事务,新起一个事务,当报错时,只有第一个库的操作会被回滚
  4. 没事务,有线程切换,根据线程本地变量不同表现不同的结果

Contributors

LiWenGu

Issues