Java连TiDB报“Unknown system variable”错误,因TiDB旧版本不支持MySQL 8.0+客户端默认探测的系统变量,需在JDBC URL加cacheServerConfiguration=true禁用SELECT @@session.*探测。

Java应用连TiDB报“Unknown system variable”错误
这是最常见接入失败现象,本质是TiDB(尤其旧版本)不支持MySQL 8.0+客户端默认启用的某些系统变量查询。JDBC驱动在握手阶段会主动探测服务端能力,一旦TiDB返回UNKNOWN_SYSTEM_VARIABLE,连接直接中断。
- 用
mysql-connector-java8.0.28+时,必须显式禁用这些探测:在JDBC URL末尾加&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&cacheServerConfiguration=true&useServerPrepStmts=false - 关键参数是
cacheServerConfiguration=true——它让驱动跳过SELECT @@session.*类探测,改用预设兼容模式 - 若用Druid连接池,还需在
initConnectionSqls里删掉任何SET NAMES或SET @@sql_mode语句,TiDB对部分SQL mode值解析不一致
Spring Boot + MyBatis执行INSERT IGNORE报语法错误
TiDB虽兼容MySQL协议,但语法支持有粒度差异。INSERT IGNORE在TiDB 5.4+才完整支持,旧版只认INSERT ... ON DUPLICATE KEY UPDATE;MyBatis默认生成的INSERT IGNORE会直接抛com.mysql.cj.jdbc.exceptions.MysqlDataTruncation异常。
- 检查TiDB版本:
SELECT tidb_version();,低于5.4就别碰INSERT IGNORE - MyBatis XML中把
<insert>标签的useGeneratedKeys设为false,避免驱动额外拼SELECT LAST_INSERT_ID()——TiDB对这个函数返回值和MySQL行为不同 - 批量插入场景下,
rewriteBatchedStatements=true必须开启,否则每条INSERT都走独立网络往返,性能断崖下跌
事务中调用SELECT FOR UPDATE锁行为异常
TiDB的乐观事务模型导致SELECT FOR UPDATE不真正加行锁,而是在COMMIT时检测写冲突。Java应用如果按MySQL悲观锁逻辑写重试逻辑(比如捕获Deadlock found后sleep再重试),在TiDB上反而会因重复提交引发TiKV TxnLockNotFound错误。
- Spring
@Transactional注解需明确指定isolation = Isolation.REPEATABLE_READ,TiDB不支持READ_COMMITTED隔离级别 - 避免在事务里做耗时操作(如HTTP调用、文件读写),TiDB事务TTL默认300秒,超时自动回滚,且不通知客户端
- 真需要强一致性锁,改用
SELECT ... LOCK IN SHARE MODE+ 应用层CAS更新,比依赖FOR UPDATE更可控
Druid连接池配置不当引发连接泄漏
TiDB的连接空闲超时(wait_timeout)默认8小时,但Druid的minIdle连接会持续发送心跳保活,若网络中间件(如LVS、HAProxy)设置了更短的TCP空闲断连时间(常见300秒),Druid未感知连接已断,后续请求就会卡住或报Connection reset。
立即学习“Java免费学习笔记(深入)”;
- Druid配置必须加:
validationQuery="SELECT 1"、testWhileIdle="true"、timeBetweenEvictionRunsMillis="60000" -
maxWait建议设为3000毫秒而非默认-1,防止一个坏连接拖垮整个池 - TiDB侧执行
SET GLOBAL wait_timeout = 600(10分钟),与Druid心跳周期对齐,减少意外断连
TiDB不是MySQL的drop-in替换,协议兼容不等于行为兼容。最易被忽略的是事务生命周期管理和连接池心跳节奏——这两点一旦错位,问题往往出现在高并发压测后期,日志里只看到零星的TxnRetryLimitExceeded或connection closed,得顺着网络设备超时设置和TiDB配置双向核对。










