一、现象
https://docs.pingcap.com/zh/tidb/stable/performance-tuning-practices#%E5%BA%94%E7%94%A8%E9%85%8D%E7%BD%AE-1
官方文档建议使用 maxPerformance 屏蔽 JDBC 向数据库发送的一些查询设置类的 SQL 语句
发现使用 maxperformance 后,数据库事务行为和预想的不一致。
具体情况如下
useConfigs=maxPerformance | con.setAutoCommit(true) | con2.setAutoCommit(false) |
---|---|---|
TiDB: auto commit = 0 | General log 里没有 set autocommit语句(不符合代码预期) | General log 里有 set autocommit=0 |
TiDB: auto commit = 1 | General log 里没有 set autocommit语句 | General log 里有 set autocommit=0 |
useConfigs=maxPerformance &useLocalSessionState=true | con.setAutoCommit(true) | con2.setAutoCommit(false) |
---|---|---|
TiDB: auto commit = 0 | General log 里有 set autocommit=1 | General log 里有 set autocommit=0 |
TiDB: auto commit = 1 | General log 里有 set autocommit=1 | General log 里有 set autocommit=0 |
二、排查
useConfigs = maxPerformance 在 Mysql-connector 等同于如下的一组配置
cachePrepStmts=true
cacheCallableStmts=true
cacheServerConfiguration=true
useLocalSessionState=true
elideSetAutoCommits=true
alwaysSendSetIsolation=false
enableQueryTimeouts=false
connectionAttributes=none
其中 jdbc 驱动是依赖 useLocalSessionState 这个变量,来决定是使用数据库本身的 auto commit状态还是直接根据 java 的配置直接发送事务配置命令给数据库
Should the driver refer to the internal values of auto-commit and
transaction isolation that are set by 'Connection.setAutoCommit()'
and 'Connection.setTransactionIsolation()' and
transaction state as maintained by the protocol,
rather than querying the database or blindly sending commands
to the database for 'commit()' or 'rollback()' method calls?
再根据 jdbc 驱动源码验证
Jdbc 创建连接的时候会去看 tidb 里的 autocommit 和 init_connect 的值,但看起来不论如何,都会给 jdbc 内部的 autocommit 变量设置为 true。即 tidb 里的 autocommit 不论设成什么,jdbc 都认为是 true。
Jdbc 通过 uselocalseesionstate、autocommit == autocommitflag 判断是否该发 autocommit 给服务器。
从代码逻辑来看:
- 当 tidb 为 true,jdbc 为 true 时,不重新设置事务状态
- 当 tidb 为 true,jdbc 为 false,显示设置会话事务状态
- 当 tidb 为 false,jdbc 为 true,不重新设置会话事务状态
- 当 tidb 为 false,jdbc 为 false,显示设置会话事务状态
三、结论
当使用useConfigs = maxPerformance 时候,如果关闭 TiDB 数据库层面的 auto commit,同时代码里 con.setAtuCommit(true) 会导致这部分事务一直不提交,数据写入不成功,需要通过在 jdbc 里使用useLocalSessionState=true 来解决。
useConfigs = maxPerformance |
TiDB autocommit 1 |
TiDB autocommit 0 |
---|---|---|
Java setautocommit(true) |
Java 有效 |
Java 无效(不符合代码预期) |
Java setautocommit(false) |
Java 有效 |
Java 有效 |