美文网首页
sharding-jdbc使用总结

sharding-jdbc使用总结

作者: 雨夜都行 | 来源:发表于2021-05-28 11:21 被阅读0次

sharding-jdbc

由于生产或者QA环境下的数据库是按主从进行部署,在业务上默认读操作会使用从库查询来实现与主库的读写分离,提高性能。但是不可避免的是 主从延迟 的存在,此时就需要我们切换到主库进行查询操作,来保证业务的正常执行。在现有技术栈背景下,是通过使用sharding-jdbc的主库路由切换到主库上的。主库路由在使用上需要注意下面几个点:

在进行数据库路由的时候会使用到HintManager.getInstance() ,它会将HintManager实例放入ThreadLocal中,该ThreadLocal清除的方式有两种:

  • 调用HintManager.close()手动清除
  • 由sharding-jdbc自动支持,当数据库连接被归还到连接池后自动清除

自动清除的方式又可以分为两种:

  • 有事务,当前事务执行结束之后,会释放连接,同时清除该ThreadLocal
  • 没有事务,spring-data-jpa的方法 findone等,或者native sql 执行完成之后也会释放连接,同时清除ThreadLocal

如果不清除该ThreadLocal会导致什么后果?

  • 当下次请求到同一线程的时候,调用HintManager.getInstance()发现ThreadLocal中已经有值了,抛出异常
  • 等待GC清除ThreadLocal,由于ThreadLocal中在存储结构实际上是一个weakReference,gc发生会清除弱引用,但是实际上存储的实例还在,与此同时这就导致了造成了内存泄漏(实际上不会有这种情况,因为HintManager使用的ThreadLocal是静态成员变量)

需要避免下面这种错误写法:

void test() {
    HintManager hintManager = HintManager.getInstance();
    hintManager.setMasterRouteOnly();
    // 1.做了一些其它非数据库的操作,可能会出现异常(比如查询redis, 数据校验等)
    pupuRedissonHash.getMulti();
    // 2.查询数据库
    repository.findOne();
}
  • 上面这段代码如果在1中出现了异常,会导致ThreadLocal没有被清除,再下次请求用到了同一个线程(比如dubbo线程)就出现异常
  • 上面这段代码如果能正常的执行到2的话,一旦2执行结束后就会清除ThreadLocal,保证了下次请求使用到相同线程的正常执行

在讨论过了ThreadLocal清除方式的情况下,可以发现下述代码产生的两种不同情况了:

// 没有事务先主库路由,再多次查询
void testNoTransaction() {
    HintManger hintManager = HintManger.getInstance();
    hintManager.setMasterRouteOnly();
    repository.findOne(); // 1. 查询的是主库,同时清除ThreadLocal
    repository.findOne(); // 2. 查询的是从库,因为ThreadLocal被清除了
}

// 有事务先主库路由,再多次查询
@Transactional
void testHasTransaction() {
    HintManger hintManager = HintManger.getInstance();
    hintManager.setMasterRouteOnly();
    repository.findOne(); // 1. 查询的是主库,事务还没结束,不清除ThreadLocal
    repository.findOne(); // 2. 查询的是主库,因为ThreadLocal还没被清除
} // 事务结束,归还数据库连接,清除ThreadLocal

在讨论完了自动清除的场景,下述代码表述了手动清除ThreadLocal:

// 使用try finally进行清除
void testManualClearByTryFinally(DTO dto) {
    HintManger hintManager = HintManger.getInstance();
    hintManager.setMasterRouteOnly();
    try {
      checkDTO(dto);
      redissonHash.getMulti();
      repository.findOne();  
    } finally {
      hintManager.close();
    }
}

// 使用try-with-resource进行清除
void testManualClearByTryWithResource(DTO dto) {
    try (HintManager ignored = routeToMaster()) {
      checkDTO(dto);
      redissonHash.getMulti();
      repository.findOne();  
    }
}

HintManager routeToMaster() {
    HintManger hintManager = HintManger.getInstance();
    hintManager.setMasterRouteOnly();
    return hintManager;
}

建议:在使用HintManager.getInstance()后,不确定在此之后执行的代码是否会出现异常的情况下,使用try finally或者try-with-resource手动清除ThreadLocal

相关文章

网友评论

      本文标题:sharding-jdbc使用总结

      本文链接:https://www.haomeiwen.com/subject/gfouwhtx.html