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

oschina 投递于 2013/12/13 09:15 (共 2 段, 翻译完成于 12-13)
阅读 4713
收藏 52
3
加载中

在为一个客户排除死锁问题时我遇到了一个有趣的包括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
翻译于 2013/12/13 09:42
2

这是为什么呢,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
翻译于 2013/12/13 10:04
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(7)

iBoxDB
iBoxDB
微软推广了Ctrl+Alt+Del 组合键, MySQL推广了死锁理论
bewdx3
bewdx3
这是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这个隔离级别外,而做法非常粗暴--加上间隙锁,根本不允许你插入,当然没有幻读.
mark35
mark35
细节是魔鬼。mysql的细节无处不在,躲得开myisam躲不过innodb
shinwell
shinwell
@李桂玺
CREATE TABLE innodb_monitor(a INT) ENGINE=INNODB;
SHOW ENGINE INNODB STATUS;
一野夏伊娃
一野夏伊娃
http://www.linuxeden.com/html/news/20131219/146579.html 转的够快
一野夏伊娃
一野夏伊娃
InnoDB状态显示这个UPDATE在主索引记录上持有了一个X锁...............
我想知道文中说的这个“InnoDB状态”怎么显示出来的,求赐教
黄亿华
黄亿华
还真遇到过,不过这文章没有分析原因啊。
返回顶部
顶部
返回顶部
顶部