这一篇,我们讲下meepo如何生成前后镜像信息及提交回滚操作!

  • meepo和阿里GTS一样,在事务分支执行sql的时候,记录下sql语句,并根据sql生成前后镜像

    那么第一个问题,我们如何获取到业务方法中所有执行的sql?

    起初我从数据库的日志中获取sql。分析了下业务执行的流程,比如demo中的int value = this.jdbcTemplate.update("update tb_account_two set amount = amount + "+amount+" where acct_id ='"+acctId+"'");这一句

    代码跟踪进入jdbcTemple,完了后直接返回到demo方法中,貌似没有地方可以做手脚。为此,我花了不少时间在数据库的sql日志获取实现上。

    mysql还算顺利,general_log 完整的记录了所有历史sql,而且可以通过 set output =table,记录在mysql.general_log数据表中。但是仍然碰到了不少问题,比如:

    1、mysql日志数量太大,比如心跳连接的select x,close及其他无意义的日志。这个我通过精确开关日志解决,在执行业务sql前合适的地方打开日志,执行完后再关闭

    2、在历史记录中查找业务sql有点冒险,会不会查到上次执行的sql或者执行了一组sql,只拿到一条或者几条,这个风险只能通过反复测试,健壮的程序来保证

    解决了mysql的日志,还需要扩展其他流行的数据库,但是当研究了postgresql的日志后,准备放弃了,因为拿到想要的sql比mysql麻烦多了

    一想到后面还有无数种数据库,我迫切地想知道,GTS怎么解决这个问题

    在翻了网上有限的资料,和混淆过的反编译源码,在专利上找到这一句

    专利

    GTS貌似很轻松的拿到了执行的sql,翻遍整个专利说明,没有任何提及数据库日志的地方,那只有一种可能,就是通过代理。在gts代码中找到下图代码

    gtscode1

    图中路径是com.taobao.txc.resourcemanager.b.a

    gts在初始化数据连接的时候,已经对connection和Statement进行代理,jdbcTemple拿到的Statement是自定义增强后的Statement,此时想拿到sql就非常简单了,如下图的var1

    gtscode2

    这里我有点不理解的是gts用了静态代理,虽然不影响实现,但是对被代理类的三四十个方法都要多写两句,非常不优雅,如下

    gtscode3

    这一点我当然不会学,用动态代理就好了,if("executeUpdate".equalse(methodName)搞定,见meepo的DynamicPreparedStatementProxyHandler类

    拿到sql后就简单了,把DML的sql处理成select的sql,获得前后镜像,持久化到txc_undo_log,在txc_lock插入记录行,防止其他事务修改。

    这一部分代码,体现在在DynamicPreparedStatementProxyHandler的invoke方法

  • 最近有朋友加我,跟我说想参与meepo,帮我加上mybatis的支持。我问他你怎么知道meepo不支持mybatis,回答说这meepo上没有看到mybatis包的引用

    这里强调一下,meepo支持mybatis,这是meepo动态代理statement的原理决定的

    无论是jdbcTemplate或者是mybatis,无论怎么封装,底层都是jdbc,执行顺序:getConnection->getStatement->stmt.execute()

    jdbcTemplate、mybatis或是其他orm框架,执行到getStatement的时候,获取的都是增强过的自定义statment

    mybatis底层

  • 全局提交

    各分支事务成功返回后,JTA的transManager发起全局事务提交

    各分支事务接到commit指令,因为本地事务早已提交,只是做一些清理工作,如删除前后镜像(测试阶段未删)、锁信息清理

    这就是GTS所谓的一阶段提交+异步清理(1pc+1)

  • 全局回滚

    任一分支事务如返回失败,JTA的transManager发起全局事务回滚

    各分支事务接到rollback指令,因为本地事务早已提交,由meepo根据前后镜像进行回滚

    meepo首先获取目标记录行与后像进行比较,如果符合,则执行相应sql回滚到前像,但是如果不符合,则表示有meepo事务之外的sql,改变了记录行,这时就会出现问题

    meepo与gts一样,前提需要避免这种情况(比如事务期间,手动执行sql)

    gts注意事项

  • FAQ

    最后在补上一个多人问过的问题

    A调用B,B调用C、D,D再调用E,E抛异常,meepo是否会回滚?

    回答如下:这得看 E出现 异常后,D是否会捕获,如果没有捕获,当成可控的,事务不会回滚;如果没有捕获,B一直向上抛,当然就回滚了

    这个依赖spring实现事务的原理

    gts注意事项

    spring 客户端事务管理器,在第一层调用的时候,如果返回异常就回滚,没有就提交