美文网首页
innodb的7种锁

innodb的7种锁

作者: 无聊之园 | 来源:发表于2019-05-03 20:24 被阅读0次

    来自于mysql 官方手册的介绍:innodb locking

    广义来讲,就行锁和表锁,行锁锁的是索引,锁不到索引则会变成表锁。但是其实innodb为了高并发,是有很多类型的锁:

    1、共享锁,排他锁,2、意向共享锁,意向排他锁
    3、记录锁,4、间隙锁,5、next-key临键锁6、自增锁。7、空间索引谓词锁

    大概按类型划分,锁住数据的范围:记录锁,临键锁。锁的类型:共享锁,排他锁。以及其他的锁。

    当然说的都是RR这个默认隔离级别。

    1、共享锁、排他锁。

    共享锁:读读不互斥,读写互斥。
    排他锁:和读写互斥,写写互斥。

    设置共享锁:SELECT ... LOCK IN SHARE MODE;
    设置排他锁:SELECT ... FOR UPDATE;delete,insert,update。

    2、意向共享锁、意向排他锁。

    意向锁:通知数据库将有加什么锁的意向并加锁。

    意向共享锁:需要对表增加共享锁的时候,先告诉数据库加意向共享锁,再加共享送。
    意向排他锁:同理。

    意向锁是系统控制的,人工无法干预页无需干预,主要目的是显示某人正在锁定行

    3、记录锁。

    记录锁:更像我们说的行锁。锁住索引记录。
    update table set id = 3 where id = 2;
    锁住id为2这条记录。

    4、间隙锁。(重点)

    间隙锁:锁住索引的上下间隙。
    间隙锁的目的:防止幻读,保证可重复读。所以,只有在RR隔离级别才有。
    没有间隙锁产生幻读的例子:因为事务在没提交前的操作都是在新版本上操作(原有的记录),快照读读的是旧版本数据(undo日志),另一个事务更新的也是原有记录,故写写同一条记录互斥。如果没有间隙锁,则事务1:update table set name = 'abc' where name = 'a';事务2:insert into table(name) values ('abc');然后事务1读到了name='abc'的数据,产生了幻读现象。

    比如有表:
    6 a
    3 abc
    7 b
    执行select * from test where name = 'a' for update;
    会锁住a上面无穷的间隙,第一条记录和第二条记录的间隙。
    故执行:insert into test values(4, 'a');会堵塞。
    执行insert into test values(7, 'ab')也会堵塞;

    在比如:select * from test where name > 'a' and name < 'b' for update;
    则a和b之间的区间也会被锁住,
    故执行:insert into test values(4, 'a');不会堵塞,因为这条记录是插入到(6,a)这条记录的上面。
    执行insert into test values(7, 'ab')会堵塞,因为这条记录会插入到(6,a)和(3,abc)记录之间;

    update、insert、delete语句也会产生间隙锁。
    比如事务1:update table set name = 'b' where name = 'a'
    事务2:insert into table (name) values ('a')等待。

    注意:唯一索引不会的非范围过滤不会产生间隙锁。
    比如:select * from table where id = 3 for update;不会锁住间隙,只会锁住3这条索引记录。原因是,唯一索引其他事务不可能再插入一条id为3的记录进来,不会产生幻读现象。

    因为有间隙锁的存在:一不小心就会导致死锁的产生。
    比如:
    事务1:执行delete,delete 语句产生一个共享间隙锁。
    事务2:执行delete,delete语句产生一个事务一个共享间隙锁
    事务1:执行insert, 语句插入的位置刚好在事务2锁住的区间,需要获取排他间隙锁。
    事务2:执行,insert语句插入的位置刚好在事务1锁住的区间。需要获取排他间隙锁。
    导致死锁。

    事务1: insert into testinnodb values (4, 'ab');
    事务2: insert into testinnodb values (4, 'ab');
    事务3: insert into testinnodb values (4, 'ab');

    说明
    事务1,获取排他锁。
    事务2:需要先进行主键校验,先获取共享锁,堵塞。
    事务3:需要先进行主键校验,先获取共享锁,堵塞
    事务1:释放锁。
    事务2:获取共享锁。再取获取排他锁,跟事务3的共享锁互斥。
    事务3:获取共享锁。再取获取排他锁,跟事务2的共享锁互斥。
    导致死锁

    还有一种情况和其类似:
    事务1:delete from testinnodb where id = 2;
    事务2:delete from testinnodb where id = 2;
    事务2:insert into testinnodb (id, a) values (2, 3);

    说明
    事务1:获取id=2的排他锁。
    事务2:获取id=2的排他锁,堵塞。
    事务2:需要先进行主键校验,先获取共享锁,因为事务1已经在等待这条记录的排他锁了,所以共享锁排在事务2的后面,而事务2又要等待事务1,如此,购车给一个循环,死锁。

    show engine innodb status;
    这条命令可以查看最新一条死锁的信息。包括:ip,sql,锁详细信息。

    死锁的解决方案:很难保证绝对不产生死锁。解决方案:1、innodb有死锁检测机制,发生死锁,一个事务会自动放弃。2、锁都有超时机制,超时自然会释放锁。3、修改程序执行顺序,尽量避免死锁。4、需要获取的锁一次性全获取。(很难办到,毕竟总有个先后顺序)。5、修改事务隔离级别,如果对幻读不介意的话可以考虑。6、修改配置文件,取消间隙锁。

    5、next-key临键锁

    记录锁和间隙锁都锁住就是临界锁。InnoDB默认加锁方式是next-key 锁。

    6、自增锁。

    自增锁:默认不开启。保证插入的连续性,事务自增插入会产生表级锁,其他事务不能插入。
    比如:
    事务A:insert into table (name) values ('a');插入的记录id=1。
    事务B:insert into table (name) values ('b');插入的记录id=2。
    事务A:insert into table (name) values ('c');插入的记录id=3。事务A会觉得很奇怪。id直接从1跳到了3。
    所以:如果开启了自增锁,事务B堵塞。
    我觉得没什么用。

    相关文章

      网友评论

          本文标题:innodb的7种锁

          本文链接:https://www.haomeiwen.com/subject/wvpznqtx.html