开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
一个需要避免的 InnoDB 间隙锁 - 技术翻译 - 开源中国社区

一个需要避免的 InnoDB 间隙锁 【已翻译100%】

标签: InnoDB
oschina 推荐于 4年前 (共 2 段, 翻译完成于 12-13) 评论 7
收藏  
52
推荐标签: InnoDB 待读

在为一个客户排除死锁问题时我遇到了一个有趣的包括InnoDB间隙锁的情形。对于一个WHERE子句不匹配任何行的非插入的写操作中,我预期事务应该不会有锁,但我错了。让我们看一下这张表及示例UPDATE。

mysql> SHOW CREATE TABLE preferences \G
*************************** 1. row ***************************
       Table: preferences
Create Table: CREATE TABLE `preferences` (
  `numericId` int(10) unsigned NOT NULL,
  `receiveNotifications` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`numericId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT COUNT(*) FROM preferences;
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.01 sec)
mysql> UPDATE preferences SET receiveNotifications='1' WHERE numericId = '2';
Query OK, 0 rows affected (0.01 sec)
Rows matched: 0  Changed: 0  Warnings: 0

InnoDB状态显示这个UPDATE在主索引记录上持有了一个X锁:

---TRANSACTION 4A18101, ACTIVE 12 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7ff2200cd700, query id 35 localhost msandbox
Trx read view will not see trx with id >= 4A18102, sees < 4A18102
TABLE LOCK table `test`.`preferences` trx id 4A18101 lock mode IX
RECORD LOCKS space id 31766 page no 3 n bits 72 index `PRIMARY` of table `test`.`preferences` trx id 4A18101 lock_mode X
Garfielt
 翻译得不错哦!

这是为什么呢,Heikki在其bug报告中做了解释,这很有意义,我知道修复起来很困难,但略带厌恶地我又希望它能被差异化处理。为完成这篇文章,让我证明下上面说到的死锁情况,下面中mysql1是第一个会话,mysql2是另一个,查询的顺序如下:

mysql1> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql1> UPDATE preferences SET receiveNotifications='1' WHERE numericId = '1';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql2> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql2> UPDATE preferences SET receiveNotifications='1' WHERE numericId = '2';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql1> INSERT INTO preferences (numericId, receiveNotifications) VALUES ('1', '1'); -- This one goes into LOCK WAIT
mysql2> INSERT INTO preferences (numericId, receiveNotifications) VALUES ('2', '1');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

现在你看到导致死锁是多么的容易,因此一定要避免这种情况——如果来自于事务的INSERT部分导致非插入的写操作可能不匹配任何行的话,不要这样做,使用REPLACE INTO或使用READ-COMMITTED事务隔离。

Garfielt
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(7)
Ctrl/CMD+Enter

还真遇到过,不过这文章没有分析原因啊。
InnoDB状态显示这个UPDATE在主索引记录上持有了一个X锁...............
我想知道文中说的这个“InnoDB状态”怎么显示出来的,求赐教
http://www.linuxeden.com/html/news/20131219/146579.html 转的够快
@李桂玺
CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;
SHOW ENGINE INNODB STATUS;
细节是魔鬼。mysql的细节无处不在,躲得开myisam躲不过innodb
这是innodb的特性.而innodb所谓的Repeatable Read实际上同标准定义的不同,介于Repeatable Read和Serializable之间.
标准定义可以参考:
http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_Levels.2C_Read_Phenomena_and_Locks
可以看到重复读级别允许幻读,也就是虽然读不到别的事务update做的提交,但是别的事物的insert提交结果是允许读到的.
但是innodb设计过程中为了统一cud3种操作,将幻读也排除在了Repeatable Read这个隔离级别外,而做法非常粗暴--加上间隙锁,根本不允许你插入,当然没有幻读.
微软推广了Ctrl+Alt+Del 组合键, MySQL推广了死锁理论
顶部