为了防止幻读,InnoDB使用了一种名为Next-Key锁定的算法,它将记录锁和间隙锁定结合在一起即:InnoDB在执行行级锁的时候,会用这种方式-扫描索引记录,会在符合索引条件的记录上加共享锁或者独占锁。
[Next-Key]=[Record-lock]+[Gap-lock]如果说上面的几种锁机制给人的感觉是昏天暗地,那个这个Next-Key算法就会叫人怀疑人生。
验证案例
这里主要验证Gap-lock间隙锁的存在机制。
CREATE TABLE `dc_gap` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `id_index` int(11) NOT NULL COMMENT 'index', PRIMARY KEY (`id`), KEY `id_index` (`id_index`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='间隙表'; INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('1', '2'); INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('3', '4'); INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('6', '7'); INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('8', '7'); INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('9', '9');会话窗口一
-- 1、开始事务 START TRANSACTION ; -- 3、锁定id_index=7的两条记录 SELECT * FROM dc_gap WHERE id_index=7 FOR UPDATE ; -- 9、提交 COMMIT ;会话窗口二
-- 2、开始事务 START TRANSACTION ; -- 4、写入等待,id_index=6 INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('4', '6'); -- 5、写入等待,id_index=4 INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('4', '4'); -- 6、写入成功,id_index=3 INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('4', '3'); -- 7、写入等待,id_index=9 INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('7', '9'); -- 8、写入成功,id_index=10 INSERT INTO `dc_gap` (`id`, `id_index`) VALUES ('7', '10');7向上到4有间隙,7向下到9有间隙,所以间隙锁定[4,9],且包含首尾值。
5、Dead-Lock锁基础描述
两个或者多个事务在同一个资源上相互占用,并请求锁定对方占用的资源,从而导致死循环现象,也就是死锁。
验证案例
会话窗口一
-- 1、开启事务 START TRANSACTION ; -- 3、占用id=6的资源 SELECT * FROM dc_gap WHERE id=6 FOR UPDATE ; -- 5、占用id=9的资源等待 SELECT * FROM dc_gap WHERE id=9 FOR UPDATE ;会话窗口二
-- 2、开启事务 START TRANSACTION ; -- 4、占用id=9的资源 SELECT * FROM dc_gap WHERE id=9 FOR UPDATE ; -- 6、占用id=6的资源抛死锁 SELECT * FROM dc_gap WHERE id=6 FOR UPDATE ;补刀一句:数据库实现各种死锁检测机制,或者死锁超时等待结束,InnoDB存储引擎在检测到死锁后,会立即返回错误,不然两个事务会隔空对望,一眼万年。
注意:死锁在事务型业务中,是无法绝对避免的,锁定资源少,粒度细,尽量避免该情况出现。
四、源代码地址 GitHub·地址 https://github.com/cicadasmile/mysql-data-base GitEE·地址 https://gitee.com/cicadasmile/mysql-data-base