锁的分类
我之前有讲过,锁分为两大类,一是乐观锁,一是悲观锁。这里不再详细讲解。本节主要讲解MySQL在不同的隔离级别情况下,使用的不同粒度的锁机制。
先说明的情况是,无论哪种隔离级别下,如果有索引,加锁都是加在索引上的,只是在不同的隔离级别下,支持的锁粒度不一样而已;锁粒度越小,消耗的资源就越多。如果没有索引,则加锁则是表锁,即锁住聚簇索引中所有行。
隔离级别下的加锁
RR 隔离级别就是,可重复读。REPEATABLE READ。
RC 隔离级别就是,读已提交。READ COMMITTED。
先建表并插入数据,如下。
CREATE TABLE 'blog'(
'id' BIGINT(20) NOT NULL,
'name' VARCHAR(16) COLLATE utf8mb4_bin DEFAULT NULL,
'idcard' BIGINT(20) NOT NULL,
PRIMARY KEY('id')
) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4 COLLATE=utf8mb4_bin;
-- 插入数据
INSERT INTO blog VALUES(1,'test1',100),(5,'blog',200),(8,'blog',300),(10,'test2',400);
如果是精确匹配,比如根据主键更新。不管是在RR还是在RC隔离级别下,都是会命中聚簇索引上的行锁的,这分为两种情况,如果是以下两种情况,则分别是在行的索引上加X锁和S锁。
//在id=1的聚簇索引上加X锁
UPDATE blog SET name = 'x' WHERE id = 1;
//在id=1的聚簇索引上加S锁
SELECT * FROM blog WHERE id = 1 LOCK IN SHARE MODE;
如果是范围查询,且如果在RC隔离级别下,会在命中的行的聚簇索引上加锁,因为在RC隔离级别下是没有间隙锁的,所以会出现幻读的现象。因为在RR隔离模式下,采用间隙锁解决了幻读问题,所以,如果是在RR隔离模式下,会在所有命中的行的聚簇索引上加next-key locks(锁住行和间隙)。最后命中的索引的后一条记录,也会被加上 next-key lock。
//在RC隔离级别下,在id=8和10的聚簇索引上加X锁
//在RR隔离级别下,在id=8、(10,+)的聚簇索引上加X锁
//在(5,8)(8,10)(10,+)加间隙锁
UPDATE blog SET name = 'test3' WHERE id > 7;
//在RC隔离级别下,在id=1的聚簇索引上加X锁
//在RR隔离级别下,在id=1、5的聚簇索引上加X锁
//在RR隔离级别下,在(-,1)(1,5)加间隙锁
UPDATE blog SET name = 'test4' WHERE id <=1;
如果查找的内容为空,即没有命中聚簇索引,那么,在RC隔离级别下,什么也不会锁,但在RR隔离级别下,会锁住查找目标所在的间隙。