踩坑!MySQL这个参数让应用直接崩了,90%的DBA都忽略了!
你有没有遇到过DBA队友将生成环境与开发测试环境设置的不一样而导致的线上问题的案例?本人就遇到过一次:明明测试环境跑得好好的,一上生产就“原地爆炸”。
今天我们就来复盘一下因MySQL参数sql_generate_invisible_primary_key导致的问题,顺便把整个排查和复盘过程分享出来,帮大家避坑!
一、问题现象
未经审核的脚本发布到线上,监控告警就炸了:核心业务接口报错率飙升至 80%,用户反馈无法提交订单、查询数据。打开应用日志,满屏都是类似的错误:
### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'my_row_id' in 'field list'### The error may exist in com/xxx/mapper/OrderMapper.java (best guess)### The error occurred while setting parameters### SQL: INSERT INTO t_order (order_no, user_id, amount) VALUES (?, ?, ?)### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'my_row_id' in 'field list'奇怪的是:
测试环境、预发环境完全正常,只有生产库报这个错
t_order表我们明明没定义my_row_id字段,代码里也没用到my_row_id,为什么会报 “找不到my_row_id列”
二、排查过程:从代码到数据库的 “抽丝剥茧”
1. 核对代码与表结构
先核对了测试环境的表结构,t_order的建表语句确实没有my_row_id字段:
CREATE TABLE `t_order` ( `order_no` varchar(64) NOT NULL COMMENT '订单号', `user_id` bigint(20) NOT NULL COMMENT '用户ID', `amount` decimal(10,2) NOT NULL COMMENT '订单金额') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';代码里的插入SQL也只涉及order_no、user_id、amount三个字段,完全没提my_row_id。
查看生产环境的表结构:
mysql> show create table t_order\G*************************** 1. row *************************** Table: t_orderCreate Table: CREATE TABLE `t_order` ( `my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */, `order_no` varchar(64) NOT NULL COMMENT '订单号', `user_id` bigint NOT NULL COMMENT '用户ID', `amount` decimal(10,2) NOT NULL COMMENT '订单金额', PRIMARY KEY (`my_row_id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单表'1 row in set (0.00 sec)发现了根源。
2. 核对数据库配置差异
既然代码和表结构都没问题,那大概率是数据库配置的问题。对比测试库和生产库的参数,发现生产库多了一个关键配置:
-- 生产库配置show variables like 'sql_generate_invisible_primary_key';+-----------------------------------+-------+| Variable_name | Value |+-----------------------------------+-------+| sql_generate_invisible_primary_key | ON |+-----------------------------------+-------+-- 测试库配置show variables like 'sql_generate_invisible_primary_key';+-----------------------------------+-------+| Variable_name | Value |+-----------------------------------+-------+| sql_generate_invisible_primary_key | OFF |+-----------------------------------+-------+就是这个sql_generate_invisible_primary_key参数,直接锁定了问题根源!
3. 修复处理
建议将这个参数关闭,保持和开发测试环境的默认值一致。完毕后再将表加上业务主键。
此事件提醒大家:
MySQL数据库的innodb表一定要加上主键
新增的SQL上线前一定要进行审核
开发、测试、生产环境等各个环境的各个参数(和硬件相关的除外)保持一致
数据库一点要标准化建设
三、GIPK 到底是什么?为什么会生成my_row_id?
sql_generate_invisible_primary_key全称是Generated Invisible Primary Key(自动生成隐藏主键),是MySQL8.0.30+为了帮开发者偷懒新增的特性,官方文档定义得非常明确:
当该参数开启时,MySQL会为没有显式定义主键的InnoDB表(且即使表中已经有唯一索引也会生成),自动添加一个名为my_row_id的隐藏列作为主键
这个列类型为BIGINT UNSIGNED,自增、不显式可见
GIPK的my_row_id与InnoDB 底层的my_row_id有差异
部分ORM框架、JDBC驱动会感知到这个隐藏字段,甚至自动拼接进SQL,因此会报错
四、 总结
技术坑不可怕,可怕的是踩了之后不总结。希望这个案例能帮大家避开MySQL这个 “隐形陷阱”,也提醒各位:线上环境的每一个参数配置,都值得反复核对!SQL脚本上线前一定要提供审核,避免有无主键的表!
关注我的微信公众号【数据库干货铺】,后续分享更多数据库运维及优化干货,避开各种开发坑!
