悲剧啊!Mysql的上古BUG!!!
| 导读 | 这是MySQL8.0修复的上古bug之一,在2003年由Percona的CEO(当时应该还没Percona吧)提出的bug#199,光看这bug号就扑面而来一股上古时代的沧桑气息。 |

问题的本质在于InnoDB初始化AUTO_INCREMENT的方式,在每次重启时,总是算出表上最大的自增值作为最大值,下一次分配从该值开始。这意味着如果在btree右侧叶节点大量删除记录,重启后,自增值可能被重用。这在很多场景下可能导致问题,包括但不限于:主备切换、历史数据迁移等场景。在bug#199下面一大堆的回复里,可以看到大量的同行抱怨。
官方的修复就比较优雅了,不改变任何现有的存储,而是通过redo log来进行恢复。该补丁基于WL#7816的框架实现的,要想搞懂这个补丁,得先看看WL#7816做了哪些改动,为了解决这个问题,InnoDB使用一个引擎私有的系统表+特殊redo log的方式,在引擎内部自己解决corruption标记持久化的问题。其大概思路为:
- 当发现索引损坏时,写入一条redo log,但不更新数据词典
- 引入一个innodb引擎私有的系统表,称为DD Buffer Table,每次checkpoint之前会将索引corruption bit存入其中。
- 在崩溃恢复时,同时从redo log和DD Buffer Table中读取索引 corruption bit, 合并结果,并标记内存中的表和索引对象。
目前Persister的类型仅有两种,一个用于corruption bit的持久化,一个用于自增列的持久化,对应的类为:
Persister:
|-- CorruptedIndexPersister
|-- AutoIncPersister
Persister对应全局对象dict_persist_t::persisters,可以通过类型persistent_type_t来找到对应的Persister,目前仅有PM_INDEX_CORRUPTED及PM_TABLE_AUTO_INC,但从注释来看,未来肯定会做更多的扩展,Persister在启动时调用函数dict_persist_init进行初始化。
新的系统表名为SYS_TABLE_INFO_BUFFER,对应管理类为DDTableBuffer,指针存储在dict_persist->table_buffer中。
系统表包含两个列:TABLE_ID及BLOB类型的METADATA(ref DDTableBuffer::init),METADATA列包含了所有需要持久化的元数据。
有几种情况会将内存修改回写到DDTableBuffer中:
- 在做checkpoint(log_checkpoint)之前,所有在dirty_dict_tables链表上的表对象,对应persist metadata都需要回写到DDTableBuffer中(dict_persist_to_dd_table_buffer)
- 从内存中驱逐一个表对象时(dict_table_remove_from_cache_low),如果需要的话也会去尝试回写。
- 在对包含自增列的表做DDL后,需要持久化counter,在如下函数中,会调用dict_table_set_and_persist_autoinc:
ha_innobase::commit_inplace_alter_table
create_table_info_t::initialize_autoinc()
// for example: alter table..auto_increment = ??
row_rename_table_for_mysql;
// rename from temporary table to normal table
回写的过程也比较简单(dict_table_persist_to_dd_table_buffer_low):
- 通过表对象初始化需要回写的Metadata数据: corrupt index及autoinc值(dict_init_dynamic_metadata)
- 构建记录值,插入DDTableBuffer系统表(DDTableBuffer::replace(), 如果记录存在的话,则进行悲观更新操作
- 表对象的diry_status修改成 METADATA_BUFFERED,表示有buffer的元数据
在崩溃恢复时,当解析到日志MLOG_TABLE_DYNAMIC_META时(MetadataRecover::parseMetadataLog),会进行解析并将解析得到的数据存储到集合中(MetadataRecover::m_tables),如果存在相同table-id的项,就进行替换,确保总是最新的。
在完成recovery后,搜集到的meta信息暂时存储到srv_dict_metadata中, 随后进行apply(srv_dict_recover_on_restart), apply的过程也比较简单,载入表对象,然后对表对象进行更新(MetadataRecover::apply),例如对于autoinc列,就总是选择更大的那个值。
这个bug已经挂了相当长的时间,不排除把这个bug当作InnoDB的“特性”的同学,一定要注意到这个改动...

悲剧啊!Mysql的上古BUG!!!的更多相关文章
- 一个MySQL JDBC驱动bug引起的血案
1.1 问题背景 公司是做电商系统的,整个系统搭建在华为云上.系统设计的时候,考虑到后续的用户和订单数量比较大,需要使用一些大数据库的组件.关系型数据库这块,考虑到后续数据量的快速增长,不是 ...
- "Cannot convert value '0000-00-00' from column 2 to TIMESTAMP"mysql时间转换bug
今天在项目中遇到这样的一个bug,Cannot convert value '0000-00-00' from column 2 to TIMESTAMP 仔细一查,经过http://blog.csd ...
- Spring+SpringMVC+MyBatis+easyUI整合基础篇(八)mysql中文查询bug修复
写在前面的话 在测试搜索时出现的问题,mysql通过中文查询条件搜索不出数据,但是英文和数字可以搜索到记录,中文无返回记录.本文就是写一下发现问题的过程及解决方法.此bug在第一个项目中点这里还存在, ...
- mysql 5.6 bug
https://dev.mysql.com/doc/refman/5.6/en/account-management-sql.html USE mysql; SELECT Host,User FROM ...
- 【MySQL 线上 BUG 分析】之 多表同字段异常:Column ‘xxx’ in field list is ambiguous
一.生产出错! 今天早上11点左右,我在工作休息之余,撸了一下猫.突然,工作群响了,老大在里面说:APP出错了! 妈啊,这太吓人了,因为只是说了出错,但是没说错误的信息.所以我赶紧到APP上看看. 这 ...
- 分享2个近期遇到的MySQL数据库的BUG案例
近一个月处理历史数据问题时,居然连续遇到了2个MySQL BUG,分享给大家一下,也欢迎指正是否有问题. BUG1: 数据库版本: MySQL5.7.25 - 28 操作系统: Centos 7.7 ...
- MySQL 的一个Bug?
在 my.ini 中的 [mysqld] 段设置 mysql basedir 和 datadir,如下: #Path to installation directory. All paths are ...
- Powser Design 16.5 导入Mysql数据库的bug
在Power Designer 16.5中,想导入mysql的某个数据库,选择导入后会导入全部数据库. 解决方案: 打开powerdesigner.选择file--->reverse engin ...
- MySql 参数赋值bug (MySql.Data, Version=6.9.6.0 沙雕玩意)
直接将参数赋值为常量0则参数值为null,出现异常:MySql.Data.MySqlClient.MySqlException (0x80004005): Column 'PayType' canno ...
随机推荐
- Sort Colors
Given an array with n objects colored red, white or blue, sort them so that objects of the same colo ...
- [LeetCode] House Robber 打家劫舍
You are a professional robber planning to rob houses along a street. Each house has a certain amount ...
- ASP.NET Core MVC 在linux上的创建及发布
前言 ASP.NET core转眼都发布半月多了,社区最近也是非常活跃,虽然最近从事python工作,但也一直对.NET念念不忘,看过了园区大神们搭建的Asp.net core项目之后,自己也是跃跃欲 ...
- Mysql查询——深入学习
1.开篇 之前上一篇的随笔基本上是单表的查询,也是mysql查询的一个基本.接下来我们要看看两个表以上的查询如何得到我们想要的结果. 在学习的过程中我们一起进步,成长.有什么写的不对的还望可以指出. ...
- Android开发之import org.apache.http
在build.gradle(app)中添加一下依赖: android { useLibrary 'org.apache.http.legacy' } dependencies { compile 'o ...
- TCP/IP是一种十一状态
1.建立连接协议(三次握手) 三次握手过程说明: 1. 在最开始,客户端和服务器都是处于CLOSED状态 2.服务器会创建sockert开始监听,服务器状态LISTEN 3.客户端向服务器端发送SY ...
- Django session cookie 上传文件、详解
session 在这里先说session 配置URL from django.conf.urls import patterns, include, url from django.contrib i ...
- JAVA当中变量什么时候需要初始化
1. 对于类的成员变量,不管程序有没有显式的进行初始化,Java虚拟机都会先自动给它初始化为默认值. 默认值如下: Boolean false ...
- MYSQL批量修改表前缀与表名sql语句
修改表名 ALTER TABLE 原表名 RENAME TO 新表名; 一句SQL语句只能修改一张表 show tables; 1. SELECT CONCAT( 'ALTER TABLE ', ta ...
- 编译CM14.1(sudmod71.1)过程记录
编译CM14.1内存要求很高,至少8G以上,我的6G也可以搞定,交换空间分配大一点. 1.安装平台 建议安装Deepin 15.3桌面版系统,系统UI好看. 2.配置环境 (1)安装编译依赖库 sud ...