关于seata1.4.2版本适配达梦数据库8.0的改造方案

关于seata1.4.2版本适配达梦数据库8.0的改造方案

一、前言
  1. 近期公司接到不少客户需求,希望系统支持国产达梦数据库
  2. 由于系统默认一直适配的是MySQL,所以在应用层面改造了较多地方
  3. 同时,也配置了达梦数据库对MySQL的兼容配置,尽可能减少系统业务代码的修改
  4. 最后还有一个问题就是分布式事务解决方案,seata的适配
  5. 系统目前使用的是最新稳定版本 1.4.2,查看了GitHub及官方文档,考虑在2.0版本才会适配达梦数据库,这对我们来说肯定有点来不及了
  6. 同时也查看到https://github.com/seata/seata/pull/3672,这个PR
  7. 也是考虑尽可能少的改动,这个PR暂时作为后备方案
  8. 目前的思路就是,seata-server端改用Redis做存储,只修改客户端这边兼容代码,这样看来似乎改动不会太大
  9. 自把数据库驱动及连接换成达梦后,测试了seata就工作不正常了,关键错误如下
not found service provider for : io.seata.sqlparser.druid.SQLOperateRecognizerHolder
二、具体改造过程(艰难)
  1. 首先感谢 seata QQ交流群中的大佬 wangliang181230 指点

  2. 诊断问题肯定是dbType变成了dm,经过我debug,确实
    关于seata1.4.2版本适配达梦数据库8.0的改造方案_第1张图片

  3. 既然达梦做了适配MySQL的配置,那么思路是将dbType固定为MySQL

  4. 目前dbType没有直接的配置项,只有dbParserType可以配置,把dbParserType配置成自定义开发的,也就是这个实现类

    io.seata.sqlparser.util.DbTypeParser
    
  5. 然后就不啰嗦了,直接3个步骤,进行一波修改

  6. 第一步:在自己的客户端工程下,创建类MyDbTypeParser,内容如下

    package io.seata.sqlparser.druid;
    
    import io.seata.common.loader.LoadLevel;
    import io.seata.sqlparser.util.DbTypeParser;
    
    @LoadLevel(name = "druid", order = 1)
    public class MyDbTypeParser implements DbTypeParser {
    
    	public MyDbTypeParser() {
    	}
    
    	@Override
    	public String parseFromJdbcUrl(String jdbcUrl) {
    		return "mysql";
    	}
    }
    
  7. 第二步:创建配置文件,并添加到resource目录下

    文件名:/META-INF/services/io.seata.sqlparser.util.DbTypeParser
    文件内容:io.seata.sqlparser.druid.MyDbTypeParser
    
  8. 第三步:添加启动配置:

    seata.client.rm.sql-parser-type=druid
    

关于seata1.4.2版本适配达梦数据库8.0的改造方案_第2张图片

  1. 以上三步做完,情况如上图所示,重启服务提供者和调用者测试
    关于seata1.4.2版本适配达梦数据库8.0的改造方案_第3张图片

  2. 从debug情况看,dbType已经对了,不过又出现一个问题 needs to contain the primary key.

  3. 经过确认,达梦库的表肯定是有主键的,然后一波debug,最后发现这个地方判主键的地方太MySQL了,直接取索引名称=PRIMARY,达梦那边主键索引名是类似 INDEX33566573,而且经过一顿研究,还无法自定义这个索引名称
    关于seata1.4.2版本适配达梦数据库8.0的改造方案_第4张图片

  4. 于是先简单粗暴的改下代码试试,加一个或列名=ID ,再探下还有没有别的坑

    if ("PRIMARY".equalsIgnoreCase(indexName) || "id".equalsIgnoreCase(colName)) {
    
  5. 然后接着调试,又发现如下异常,没办法,都到这里了,不想放弃,于是又是一波研究,找到了问题所在
    关于seata1.4.2版本适配达梦数据库8.0的改造方案_第5张图片

  6. 看下面的debug里的SQL,拿到达梦客户端那边执行下就知道了,content是达梦的关键字,哎!!!
    关于seata1.4.2版本适配达梦数据库8.0的改造方案_第6张图片
    关于seata1.4.2版本适配达梦数据库8.0的改造方案_第7张图片

  7. 不想放弃,继续尝试把列名修改下,然后再修改下 seata 代码,把这个常量改成列名为 context_val

    io.seata.core.constants.ClientTableColumnsName#UNDO_LOG_CONTEXT=context_val
    
  8. 再试,哎,功夫不负有心人,成功了!最后不能高兴太早,完整的提交、异常回滚都得要测试一波,没问题才好

关于seata1.4.2版本适配达梦数据库8.0的改造方案_第8张图片
16. 对了,还有个地方要放开,之前为了让其不干扰排查seata问题给注释掉了,所以这个再加上,主要也是解决MP框架下的列名关键字问题,找到 MybatisAutoConfiguration

// 解决达梦数据库查询时间时,列名与实体属性不一致的问题
GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
dbConfig.setPropertyFormat("\"%s\"");
dbConfig.setColumnFormat("\"%s\"");
globalCfg.setDbConfig(dbConfig);
  1. 经过多种情况测试,一切正常,nice!
三、总结
  1. 通过上面的三个步骤,主要的目的是利用SPI机制,将DbTypeParser替换为自己的MyDbTypeParser,让seata工作时,dbType固定为 mysql,因为还么有适配dm的一系列处理类,所以没办法。
  2. 其次就是再遇到其他问题时,仔细的调试,根据情况,结合上下文推断问题所在,因为有源码在手,很多东西还是比较好研究、调试。不过这块也挺难的,需要有耐心、细心跟代码,也需要一定的推断经验,才好搞定问题。
  3. 这个思路也是尽可能偷懒、求稳定的思路,不确定一些复杂的业务场景下,seata是否能工作正常。
  4. 最后把代码整理下,可以考虑把DbTypeParser类和SPI配置文件放到自己工程的公共模块下,那个配置也是一样,可以放到配置中心,这样就不需要每个服务都加了
  5. 期待后续发布的2.0版本,能够适配更多的国产数据库,感谢你能看到这,希望这篇文章对你有帮助,省得自己去绕弯子了!

你可能感兴趣的:(#,知识总结,工作积累,seata,达梦,数据库)