mysql|MySQL 的这个 BUG,坑了多少人?
阅读本文大概需要 6 分钟 。
作者:腾讯数据库技术
来源:cloud.tencent.com/developer/article/1367681
问题描述
近期 , 线上有个重要Mysql客户的表在从5.6升级到5.7后 , master上插入过程中出现"Duplicate key"的错误 , 而且是在主备及RO实例上都出现 。
以其中一个表为例 , 迁移前通过“show create table” 命令查看的auto increment id为1758609 ,迁移后变成了1758598,实际对迁移生成的新表的自增列用max求最大值为1758609 。
用户采用的是Innodb引擎 , 而且据运维同学介绍 , 之前碰到过类似问题 , 重启即可恢复正常 。
内核问题排查
由于用户反馈在5.6上访问正常 , 切换到5.7后就报错 。 因此 , 首先得怀疑是5.7内核出了问题 , 因此第一反应是从官方bug list中搜索一下是否有类似问题存在 , 避免重复造车 。 经过搜索 , 发现官方有1个类似的bug , 这里简单介绍一下该bug 。
背景知识1
Innodb引擎中的auto increment 相关参数及数据结构
主要参数包括:innodb_autoinc_lock_mode用于控制获取自增值的加锁方式 , auto_increment_increment ,auto_increment_offset用于控制自增列的递增的间隔和起始偏移 。
主要涉及的结构体包括:数据字典结构体 , 保存整个表的当前auto increment值以及保护锁;事务结构体 , 保存事务内部处理的行数;handler结构体 , 保存事务内部多行的循环迭代信息 。
这部分网上有篇文章介绍的比较好 , 具体参见:(https://www.cnblogs.com/zengkefu/p/5683258.html) 。
背景知识2
mysql及Innodb引擎中对autoincrement访问及修改的流程
(1) 数据字典结构体(dict_table_t)换入换出时对autoincrement值的保存和恢复 。 换出时将autoincrement保存在全局的的映射表中 , 然后淘汰内存中的dict_table_t 。 换入时通过查找全局映射表恢复到dict_table_t结构体中 。 相关的函数为dict_table_add_to_cache及dict_table_remove_from_cache_low 。
(2) row_import, table truncate过程更新autoincrement 。
(3) handler首次open的时候 , 会查询当前表中最大自增列的值 , 并用最大列的值加1来初始化表的data_dict_t结构体中的autoinc的值 。
(4) insert流程 。 相关对autoinc修改的堆栈如下:
ha_innobase::write_row:write_row的第三步中调用handler句柄中的update_auto_increment函数更新auto increment的值
handler::update_auto_increment: 调用Innodb接口获取一个自增值 , 并根据当前的auto_increment相关变量的值调整获取的自增值;同时设置当前handler要处理的下一个自增列的值 。
ha_innobase::get_auto_increment:获取dict_tabel中的当前auto increment值 , 并根据全局参数更新下一个auto increment的值到数据字典中
ha_innobase::dict_table_autoinc_initialize:更新auto increment的值 , 如果指定的值比当前的值大 , 则更新 。
handler::set_next_insert_id:设置当前事务中下一个要处理的行的自增列的值 。
(5) update_row 。 对于”INSERT INTO t (c1,c2) VALUES(x,y) ON DUPLICATE KEY UPDATE”语句 , 无论唯一索引列所指向的行是否存在 , 都需要推进auto increment的值 。 相关代码如下:
if (error == DB_SUCCESS
&& table->next_number_field
&& new_row == table->record[]
&& thd_sql_command(m_user_thd) == SQLCOM_INSERT
&& trx->duplicates) {
ulonglong auto_inc
……
auto_inc = table->next_number_field->val_int()
auto_inc = innobase_next_autoinc(auto_inc, 1, increment, offset, col_max_value)
推荐阅读
- 智趣科技|放心,这个扎手真不疼!糖护士血糖尿酸测试仪测评
- AMD,英特尔|又一批第十代酷睿CPU来了 这个全新i9价格有点香
- 三星手机|三星手机的oneui2系统真的很垃圾吗?
- 中年|历史上被称作用情最专一的皇帝,却死于不测,这个人是谁?
- 群众网|这个国产科技品牌连续十年蝉联全球第一,却很低调,甚至被人误解
- 小米手机|小米10Pro跳水700,这个价位随便买吧
- 新智元|两图生万物!这个超强图像转换神器,小样本一秒猫狗合体变新物种
- 雷科技|查岗抓奸新工具?高德地图这个新功能,让你的行踪无所遁形
- 数据库|面试官:说说MySQL数据库分库分表,并且会有哪些问题?
- 滋补|猪身上就这个部位最贵夏天应该多吃点,比牛羊肉更滋补