(bugfix in 5.6.21) DUPLICATES IN UNIQUE SECONDARY INDEX BECAUSE OF FIX OF BUG#68021

(bugfix in 5.6.21) DUPLICATES IN UNIQUE SECONDARY INDEX BECAUSE OF FIX OF BUG#68021

在MySQL5.6.21版本里,fix了这样一个有趣(坑爹)的bug,影响5.6.12之后及5.6.21之间的版本
InnoDB: With a transaction isolation level less than or equal to READ COMMITTED, gap locks were not taken when scanning a unique secondary index to check for duplicates. As a result, duplicate check logic failed allowing duplicate key values in the unique secondary index.

参考的bug号为:
http://bugs.mysql.com/bug.php?id=68021 (由于fix bug#68021导致的上述问题)
http://bugs.mysql.com/bug.php?id=73170 (在bug#73170中移除了对应的fix)

根据上述信息,我有意识的在里面加了些DEBUG SYNC点,并重现了该过程

root@sb 03:45:16>select * from t1 where b >=7;
+—-+——+——+
| a | b | c |
+—-+——+——+
| 7 | 8 | 9 |
| 16 | 7 | 8 |
| 26 | 7 | 8 |
+—-+——+——+
3 rows in set (0.00 sec)

可以看到,这里b为uk,却插入了两条相同的记录。

root@sb 03:45:27>show create table t1;
+——-+————————————————————————————————————————————————————————————–+
| Table | Create Table |
+——-+————————————————————————————————————————————————————————————–+
| t1 | CREATE TABLE `t1` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
UNIQUE KEY `b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+——-+————————————————————————————————————————————————————————————–+
1 row in set (0.00 sec)

root@sb 10:34:57>check table t1;
+——-+——-+———-+——————————————+
| Table | Op | Msg_type | Msg_text |
+——-+——-+———-+——————————————+
| sb.t1 | check | Warning | InnoDB: Index “b” is marked as corrupted |
| sb.t1 | check | error | Corrupt |
+——-+——-+———-+——————————————+
2 rows in set (0.15 sec)

简单的解释下,我使用的是READ COMMIT隔离级别。

step 1:
在某个session 执行flush tables tbname for export….这会使purge操作停下来

step 2:
删除某条记录,其二级索引为uk1, 执行的是标记删除,由于purge被我们人为的停止,因此这条记录不会立刻被清理掉

step 3:
插入记录,包含唯一索引记录uk1,由于step2的记录还在(没被purge),因此需要检查唯一性,在函数row_ins_scan_sec_index_for_duplicate中,根据隔离级别在记录上加S NOT GAP 锁.唯一性检查后commit mini transaction

step 4
和step 3 类似,另外一个session也插入uk1, 同样加上S NOT GAP锁,并commit mini transaction

step 5
两个session现在可以进行插入,因为受block x锁限制,插入过程是顺序的。但两次插入都能成功,原因是在做插入锁检查时,会检查相邻记录是否存在LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION)相冲突的锁, 而GAP锁和NOT GAP的S锁是不冲突的(参考lock_rec_has_to_wait), 因此两次插入都能顺利进行下去。

ref: btr_cur_optimistic_insert->lock_rec_insert_check_and_lock

Fix:
官方的fix是把对bug#68021的补丁给恢复掉。也就是说,在检查duplicate key时,总是加GAP S 锁,也就是LOCK_ORDINARY S锁
这样过程归纳为:
session 1 hold LOCK_ORDINARY S LOCK
session 2 hold LOCK_ORDINARY S LOCK
session 1 INSERT RECORD…CONFLICT, ENQUEUE (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION) ——> WAIT
session 2 INSERT RECORD…CONFLICT, ENQUEUE LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION ——> DEAD LOCK HAPPEN

如上描述,这会有一定的几率发生死锁,并且通常会让人摸不着头脑。。。当然,死锁肯定比让二级索引corruption掉要好多了…

时间: 2025-01-15 12:02:04

(bugfix in 5.6.21) DUPLICATES IN UNIQUE SECONDARY INDEX BECAUSE OF FIX OF BUG#68021的相关文章

oracle index unique scan/index range scan和mysql range/const/ref/eq_ref的区别

关于oracle index unique scan/index range scan和mysql range/const/ref/eq_ref type的区别    关于ORACLE index unique scan和index range scan区别在于是否索引是唯一的,如果=操作谓词有唯一索引则使用unique scan否则则使用range scan 但是这种定律视乎在MYSQL中不在成立 如下执行 kkkm2 id为主键 mysql> explain extended select

'dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique

  2016-10-09 23:14:43.177 DEBUG [restartedMain][org.springframework.core.type.classreading.AnnotationAttributesReadingVisitor] Failed to class-load type while reading annotation metadata. This is a non-fatal error, but certain annotation metadata may

mysql中key 、primary key 、unique key 与index区别_Mysql

mysql中索引是非常重要的知识点,相比其他的知识点,索引更难掌握,并且mysql中的索引种类也有很多,比如primary key .unique key 与index等等,本文章向大家介绍mysql中key .primary key .unique key 与index区别.  一.key与primary key区别 CREATE TABLE wh_logrecord ( logrecord_id int(11) NOT NULL auto_increment, user_name varch

[20171211]UNIQUE LOCAL(Partitioned)Index

[20171211]UNIQUE LOCAL (Partitioned) Index.txt --//如何在分区表中建立local unique index呢?自己对分区表这部分内容了解很少,参考链接: --//https://hemantoracledba.blogspot.com/2017/11/unique-local-partitioned-index.html --//重复测试,一些内容直接转抄,不自己写了. 1.环境: SCOTT@book> @ &r/ver1 PORT_STR

mysql数据库入门教程之学习笔记

mysql复习  一:复习前的准备  1:确认你已安装wamp  2:确认你已安装ecshop,并且ecshop的数据库名为shop     二   基础知识:  1.数据库的连接  mysql -u -p -h  -u 用户名  -p 密码  -h host主机  2:库级知识  2.1 显示数据库: show databases;  2.2 选择数据库: use dbname;  2.3 创建数据库: create database dbname charset utf8;  2.3 删除数

Oracle 隐含参数

Oracle 隐含参数 点击(此处)折叠或打开 set pagesize 9999 set line 9999 col NAME format a40 col KSPPDESC format a50 col KSPPSTVL format a20 SELECT a.INDX,        a.KSPPINM NAME,        a.KSPPDESC,        b.KSPPSTVL FROM x$ksppi a,        x$ksppcv b WHERE a.INDX = b.

while creating a secondary index with plugin, queries are blocked until it ends

在Pulgin的官方文档中这样描述到: While a secondary index is being created or dropped, the table is locked in shared mode. Any writes to the table are blocked, but the data in the table can be read. read. When you alter the clustered index of a table, the table is

添加mysql索引的3条原则

一,索引的重要性 索引用于快速找出在某个列中有一特定值的行.不使用索引,MySQL必须从第1条记录开始然后读完整个表直到找出相关的行.表越大,花费的时间越多.如果表中查询的列有一个索引,MySQL能快速到达一个位置去搜寻到数据文件的中间,没有必要看所有数据.注意如果你需要访问大部分行,顺序读取要快得多,因为此时我们避免磁盘搜索. 假如你用新华字典来查找"张"这个汉字,不使用目录的话,你可能要从新华字典的第一页找到最后一页,可能要花二个小时.字典越厚呢,你花的时间就越多.现在你使用目录来

MySQL 4.1.0 中文参考手册 --- 6.5 数据定义: CREATE、DROP、ALTER

mysql|参考|参考手册|数据|中文 MySQL 4.1.0 中文参考手册 --- 犬犬(心帆)翻译 MySQL Reference Manual for version 4.1.0-alpha. 6.5 数据定义: CREATE.DROP.ALTER6.5.1 CREATE DATABASE 句法 CREATE DATABASE [IF NOT EXISTS] db_name CREATE DATABASE 以给定名字创建一个数据库.允许的数据库名规则在章节 6.1.2 数据库.表.索引.列