[MySQL Bug] bug#65111碎碎念

changelog里的描述:

InnoDB: In a transaction using the  REPEATABLE READ isolation level, an  UPDATE or  DELETE statement for an InnoDB table could sometimes overlook rows recently committed by other transactions. As explained in Section 14.3.9.2, “Consistent Nonlocking Reads”, DML statements within a  REPEATABLE READ transaction apply to rows committed by other transactions, even if a query could not see those rows. (Bug #14007649, Bug #65111)
从bug#65111的描述来看,根据文档 http://dev.mysql.com/doc/refman/5.5/en/innodb-consistent-read.html中的描述:
有两个以以START TRANSACTION WITH CONSISTENT SNAPSHOT开始的session1 和session2,session1 先于session2。如果session 2插入了rec1,并提交了事务,session 1虽然看不到新插入的数据rec1,但可以对其进行Update,并随后这些记录对sesion1变的可见。
bug#65111提供的test case显然不符合上述文档中的定义。
DROP TABLE IF EXISTS `update_bug`;
CREATE TABLE `update_bug` (
  `id` int(11) PRIMARY KEY AUTO_INCREMENT,
  `fk_id` int(11) DEFAULT NULL,
  `start_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `end_time` timestamp NULL DEFAULT NULL,
  KEY `ub_ix1` (`fk_id`,`end_time`),
  KEY `ub_ix2` (`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `update_bug` (fk_id, start_time, end_time) VALUES (1, '1999-12-31 23:59:59', '1999-12-31 23:59:59'), (1, '1999-12-31 23:59:59', NULL);

#Session 1
START TRANSACTION WITH CONSISTENT SNAPSHOT;

#Session 2
START TRANSACTION WITH CONSISTENT SNAPSHOT;
UPDATE update_bug SET end_time = '2000-01-01 00:00:02' WHERE fk_id = 1 AND end_time IS NULL;
INSERT INTO update_bug (fk_id, start_time, end_time) VALUES (1, '2000-01-01 00:00:02', NULL);
COMMIT;

#Session 1
UPDATE update_bug SET end_time = '2000-01-01 00:00:01' WHERE fk_id = 1 AND end_time IS NULL;
INSERT INTO update_bug (fk_id, start_time, end_time) VALUES (1, '2000-01-01 00:00:01', NULL);
COMMIT;
当把表update_bug上的索引移除时,则一切符合文档里的定义。
从官方的patch来看,问题应该出在QUICK_RANGE_SELECT::init_ror_merged_scan
backtrace如下
mysql_update  (sql_update.cc:603)
     |–>QUICK_ROR_INTERSECT_SELECT::reset(opt_range.cc:1485)
          |–>QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan (opt_range.cc:1451, 1457)
             |–>QUICK_RANGE_SELECT::init_ror_merged_scan
                 |–>handler::clone
由于这里session1的update的where条件为fk_id=1 and end_time IS NULL,因此可以使用索引ub_ix1来检索数据。
设置断点
b QUICK_RANGE_SELECT::init_ror_merged_scan
b row_search_for_mysql
b handler::clone
当做ROR Merge Scan时,需要拷贝一个新的handler,使用handler的clone成员函数,但在innodb里clone并没有做实现。file->prebuilt->select_lock_type并没有赋值给新的handler。
挂在table上的handler(LOCK_X)
(gdb) p ((ha_innobase *)(head->file))->prebuilt->select_lock_type
$16 = 3
clone的新handler(LOCK_NONE)
(gdb) p ((ha_innobase *)(file))->prebuilt->select_lock_type
$17 = 5
随后在检索数据时,使用了不同的select_lock_type
rr_quick(records.cc:344)
    |–>QUICK_ROR_INTERSECT_SELECT::get_next(opt_range.cc:8436)
       |–>QUICK_RANGE_SELECT::get_next(opt_range.cc:8711)
             |–>handler::read_multi_range_first(sql/handler.cc:4378)
                |–>handler::read_range_first(sql/handler.cc:4504)
                   |–>ha_innobase::index_read(ha_innodb.cc:6440)
                      |–>row_search_for_mysql
(gdb) p prebuilt->select_lock_type
$4 = 3
   |–> QUICK_ROR_INTERSECT_SELECT::get_next(opt_range.cc:8459)
         ……
                            row_search_for_mysql
(gdb) p prebuilt->select_lock_type
$5 = 5
当select_lock_type=5时。执行的是一致性读,因此无法看到session2插入的记录。
在statement模式下,这可能导致主备数据不一致。
官方的patch是增加了ha_innobase::clone函数,并将之前的select_lock_type赋值给新handler。
patch地址见:
不过比较蛋疼的是,这个Bug引起了一个非常严重的bug(准确的讲,应该是激活了相应的code path):
一条简单的update语句陷入无限循环,导致磁盘满,这个bug直接导致MySQL5.5.25版本被废弃。
已经有人分析了bug#65745,感兴趣的详见 http://hedengcheng.com/?p=278
这里做一些简述:
bug#65745提供的test case,打上上一个patch后就可以重现
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (
  id1 int NOT NULL,
  id2 int NOT NULL,
  a int,
  b int,
  PRIMARY KEY (id1,id2),
  KEY (id1, a)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `t1` VALUES (1,1,NULL,1);
INSERT INTO `t1` VALUES (2,2,1,NULL);
INSERT INTO `t1` VALUES (2,3,2,NULL);
INSERT INTO `t1` VALUES (2,4,3,NULL);
INSERT INTO `t1` VALUES (2,5,4,NULL);
INSERT INTO `t1` VALUES (2,6,NULL,2);
bug的原因在于错误的判断是否修改了用来scan的索引记录(used_key_is_modified)
官方Patch:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值