美文网首页
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