MySQL版本5.7.99?

序:在项目工作中需要从三方厂商数据库同步数据到项目业务库中,本平平无奇的功能却被一个报错打破。

  在使用某框架的DataSourceConfig(Object)方法初始化数据库连接时,日志输出报错:

    Unknown system variable 'transaction_isolation'

  经过网上查找相关资料,发现问题现象大同小异都是连接驱动与数据库版本不匹配。从 MySql-5.7.20 版本后 transaction_isolation 作为 tx_isolation 的别名被引入,而在mysql-8.0之后的版本tx_isolation参数被彻底废弃。给出的解决方案无非就是:1.升级数据库的版本。2.降低MySQL连接驱动的版本。

  方法1:升级数据库版本

  数据库为三方厂商的生产数据库,让他们升级数据库版本似乎不太可能。

  方法2:降低MySQL连接驱动版本

  要降低连接驱动版本,首先要确定三方厂商的数据库版本,在查询他们的数据库版本时,令人窒息的事情发生了,查询出他们的数据库版本为MySQL-5.7.99。

  What?,在MySQL-5.7.20版本后,不是已经有transaction_isolation参数了吗?而且用 show variables like 't%_isolation'; 语句在三方库查询,确实没有transaction_isolation这个系统参数。下表是使用同一语句在三方数据库库和我们数据库查询结果。

表1

 三方数据库 我方数据库
show variables like 't%_isolation'; 
show variables like 't%_read_only';
select version();

  可以看到我方查询事务隔离级别的语句是包含transaction_isolation和tx_isolation系统参数,而三方库只有tx_isolation这一个。没办法只能更换驱动包试试,找到MySQL-5.7.20版本适配的驱动包版本是mysql-connector-java-5.1.12,替换系统中的jar包后重启业务应用,结果在启动过程中因为不适配低版本的驱动包导致启动报错。立即更换其他稍微高点版本的驱动,虽然启动没报错了,但是三方的数据库在连接时依然报出Unknown system variable 'transaction_isolation'这个错误。看来更换连接驱动这个办法也行不通。

  没办法只能先看看mysql-connector-java驱动的源码,看能不能从源码中找到问题。连接驱动版本为5.1.48。先从报错日志打印的错误堆栈信息入手。

 

图1

  好在方法调用层级不算多,众所周知java方法调用信息使用“栈”结构进行存储,栈弹出逻辑为即“先进后出”,那么最下面的方法就是要排查的起点。从ConnectionImpl.connectWithRetries()方法依次为ConnectionImpl.initializePropsFromServer()、ConnectionImpl.loadServerVariables() ...... 进行查看。

  首先看 ConnectionImpl.connectWithRetries()方法,此方法做了些参数初始化后调用了initializePropsFromServer()方法:

图2

  在initializePropsFromServer()方法中也是初始化参数后调用了loadServerVariables()方法,进入到loadServerVariables()方法后发现了问题的关键。

图3

 其中有行判断数据库版本后,选择在查询系统参数的sql中拼接 @@tx_isolation AS transaction_isolation 还是拼接 @@transaction_isolation AS transaction_isolation语句,而三方库数据库版本为MySQL-5.7.99,不满足条件则拼接的查询语句为 @@transaction_isolation AS transaction_isolation 从而引起报错。查看连接驱动版本大于5.1.48的的源码判断逻辑也是如此,那么就排除是连接驱动jar包bug的问题。可以确定问题就出在了这个不按常理出牌的三方数据库身上。当时正值深夜,而且三方数据库已投入生产使用,也不可能让厂商处理数据库问题,那么就只能看看能不能修改源码规避掉这个问题。

图4 在三方数据库执行查询语句

 

  我方数据库版本也是使用transaction_isolation参数来查询事务隔离级别,但是通过前期的排查,发现同时存在tx_isolation这个系统参数(详见表1)。那么把queryBuf.append(", @@transaction_isolation AS transaction_isolation"); 改为 queryBuf.append(", @@tx_isolation AS transaction_isolation");,不仅可以规避掉三方数据库没有transaction_isolation参数的问题,而且也不会对我们的数据库造成影响。只需要下载源码修改后再编译为jar包就可以了。

  从github(https://github.com/mysql/mysql-connector-j/blob/5.1.48/src)上找到5.1.48版本的源码后导入工程,修改完成后发现并不能直接编译,因为还引用了hibernate,而源码中也没找到关于其他依赖引用的描述,依赖问题一时半会也解决不了,那么就直接修改编译后的class字节码文件吧。

  把mysql-connector-java-5.1.48.jar中的ConnectionImpl.class文件提取出来,使用jclasslib工具打开,在“方法”中搜索 loadServerVariables,在字节码文件中找到拼接“, @@transaction_isolation AS transaction_isolation”对于的行,点击行中间的常量地址跳转到常量池,在常量池把值修改为“, @@tx_isolation AS  transaction_isolation”后保存,把修改后文件放回jar包中。

  

图5

  jar包调整完成后更新应用,发现连接三方库还是有报错,但从这次的异常日志中发现报错的方法变了,原来在getTransactionIsolation()方法中也需要把transaction_isolation改为tx_isolation。还是用上面的方法修改后再更新应用。

图6

  再次更新后发现又报Unknown system variable 'transaction_read_only'的错误,看来之前的修改是有效果的。transaction_read_only和transaction_isolation一样,都是在MySQL-5.7.20版本后加进去的,而三方数据库只有tx_read_only这个系统参数,我们的数据库既有transaction_read_only又有tx_read_only,还是可以使用上面的方法进行调整。根据报错调整isReadOnly()方法,调整完成更新后,再次连接三方数据库,可正常连接,同时业务应用没有因为驱动调整而产生报错和异常。

 

 图7

  事后在MySQL官网查询,并没有发现又5.7.99的版本,那么友商5.7.99这个版本的数据库到底从何而来?为何数据库会缺少系统参数?至今仍是个迷......

图8

完......

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值