CR^EATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
在name上建个不唯一的索引。 ======================================= mysql> start transaction; Query OK, 0 rows affected
mysql> select * from test; +----+------+ | id | name | +----+------+ | 1 | ccc | | 2 | ddd | | 3 | eee | | 4 | fff | +----+------+ 4 rows in set
mysql> up^date test set name="ddd1" where name="ddd "; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0
mysql> start transaction; Query OK, 0 rows affected
mysql> up^date test set name="eee1" where name="eee"; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0
mysql> up^date test set name="ccc1" where name="ccc"; 1205 - Lock wait timeout exceeded; try restarting transaction mysql>
mysql> up^date test set name="bbb1" where name="ccc"; Query OK, 1 row affected Rows matched: 1 Changed: 1 Warnings: 0
mysql> up^date test set name="ddd2" where name="eee"; 1205 - Lock wait timeout exceeded; try restarting transaction =================================================================== 间隙锁只锁in^sert语句。
事务A: 更新ddd时,锁住ddd本身, 再往前找比ddd小的记录ccc,锁住<ccc,ddd>的间隙。 再往后找比ddd大的记录eee,锁住<ddd,eee>的间隙。
事务B: 更新eee,锁住eee本身,再锁住<ddd,eee>的间隙(这个不会wait lock吗?),再锁住<eee,fff>的间隙。 记录本身更新为eee1,ok
更新ccc,锁住ccc本身,再锁住ccc前面的大空白间隙,再锁住<ccc,ddd>的间隙(这个不会wait lock吗?应该是这种lock_mode X locks rec but not gap waiting)。 记录本身更新为ccc1,结果挂了,锁超时了,哈哈,为啥呀…… 这么理解吧: 实际up^date是先select出来,修改再in^sert回去,哈…… 由于新name是ccc1,在<ccc,ddd>之间,由于有事务A的gap锁,所以in^sert失败。
要是up^date传bbb1,立马成功,因为ccc前面的空白,其他事务没有锁。 同理,eee更新为eee1是成功的,是因为eee1是在<eee,fff>的间隙中,本事务自己锁的,没事,所以成功。 要是eee更新为ddd2,同样失败,因为<ddd,eee>的间隙由事务A锁着呢……
这样理解还可以吧?哈……疯掉……
delete同理, delete不存在的记录,也会上gap锁的。
|