mysql锁总结

myisam和innodb共有的

表锁

偏向myisam存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发量低

整张表只能被一个人使用

需要注意的是innodb也有表锁的存在,操作数据库的时候,没有使用索引去操作数据,那么行锁就会变成表锁

select * from 表名

mysql的表锁机制有俩种

查看有没有被锁过show open tables

对表进行解锁unlock tables

读锁(共享锁)

当前连接:可以查看自己不能更新,不可读取别的表

另一个连接:可以查看,当更新时候就会进入阻塞,等待解锁后才能更新

对表加读锁lock table (表名) read

写锁(独占锁)

当前连接:可以查看自己,可以更新,不能读取别的表

另一个连接:进行查询1和读取都会进入等待

对表加写锁lock table (表名) write

MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预

是否兼容none读锁写锁
读锁(共享锁)
写锁(独占锁)

乐观锁与悲观锁

悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作

悲观锁,从字面理解就是很悲观,每次去拿数据的时候都认为别人会修改,所以在每次拿的时候对数据上锁,这样就保证了数据的准确性。比如mysql中的表锁,行锁。

乐观锁: 假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

乐观锁可以通过设置一个版本号或者时间戳进行实现.


我们假设这样一种情况,业务逻辑是获取用户的账户内金钱数量小于300时+100,加完之后去修改数据库

这时俩条线程同时进行了这条逻辑

假如现在用户账户内的金钱数量是100,这时候第一条线程获取到100,并加上了100变为200,去修改这个数据的时候,第二条线程也获取到100,并加上了100变为200.去修改这行数据,我们会发现最后只增加了一次钱

如何解决这个问题呢,除了synchronized之外(锁定粒度太大)

可以在update操作的时候,在进行一次where判断,判断之前的数据有没有被修改过,如果修改过,本次更新逻辑就失败,进行一次自旋.之后再去尝试修改这个数据

mvcc和乐观锁不是一个概念!mvcc是innodb的并发版本控制,是用来以一个隐藏版本号的形式让读写可以并发执行,而乐观锁会出现的版本号,是为了防止出现aba问题.不要看到版本号就乐观锁!!!

乐观锁是要自己通过代码来进行实现的

自增锁

auto-inc自增锁是mysql一种特殊的锁,如果表中存在自增字段,mysql便会自动回复一个自增锁

他是一种特殊的表级别锁,专门针对事务插入自增类的列.

在最简单的情况下,如果一个事务正在向表中插入值,则任何事务都必须等待该表中的进行插入,以便第一个事务插入的行接收连续的主键值.

比如在插入时候另一个事务开启了自增操作抢在了前面,等这条insert语句执行完毕之后,之前开启了事务的insert语句回滚了,这样就会导致插入的时候不是一个连续的主键值了.

innodb特有的锁

行锁(记录锁)

是锁住记录,锁住所以的记录,而不是真正数据记录,顾名思义,记录所就是为某行记录加锁,它封锁该行的索引记录

  • 非主键索引,会在索引加上锁后,去在主键所以上加锁
  • 表上没有索引,会在主键索引上加锁
  • 如果要锁的列没有索引,进行全表的记录加锁

行锁开销大,会出现思索,锁定粒度最小,发生和冲突的概率最低,并发度也最高

间隙锁(gap锁)

当使用范围查询的时候,就会给满足条件的内容加锁,比如:

update employee set name='123' where id>3 and id<7;

以上sql语句执行的时候,就会把3到7中间的内容进行加锁

锁定一个范围,不会阻塞其他的gap锁,但会阻插入间隙锁

间隙锁基于非唯一索引(包括主键),它锁定一段范围内的索引记录。请务必牢记:使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

插入意向锁

插入意向锁是一种gap锁不是意向锁在insert操作的时候产生.

再多事物同时写入不同数据至同一索引时,并不需要等待其他事务完成,会发生锁等待

假设有一个记录包含键值4和7,不同的事务分别插入5-6,每个事务都会产生一个在4-7之间插入意向锁,获取在插入行上的排他(写)锁,但是不会被相互锁住,因为数据行并不冲突.

插入意向锁并不会组织任何锁,对于插入的记录会有一个行锁

临键锁(next-key)

临键锁可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法.通过临键锁可以解决幻读的问题.每个数据行上的非唯一索引都会存在一把临键锁,当某个持有该数据行的临键锁时,会锁住一段左开右闭的区间.

假设某表age字段下有:5,10,15,20

该表中 age列潜在的临键锁有:
(-∞, 5],
(5, 10],
(10, 15],
(15, 20],
(20, +∞],

在事务 A 中修改年龄为5的记录。之后如果在事务 B 中执行插入年龄为8的数据,便会被阻塞。


临键锁时记录锁与间隙所的组合,它包含的范围既包括索引记录,又包含索引区间

在句非唯一索引堆记录进行update/for update(间隙锁)/lock in share mode(共享锁),innodb会获取该记行的临键锁,并同时获取该记录下一个区间的间隙锁

临键锁主要的目的是为了避免幻读,如果把事务隔离界别设置为提交读,临键锁也会失效(只在RR也就是已提交度这个隔离界别),但innodb只在一定程度上避免了幻读,没有真正解决幻读.

那最终我们就可以得知,在根据非唯一索引 对记录行进行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操作时,InnoDB 会获取该记录行的 临键锁 ,并同时获取该记录行下一个区间的间隙锁

意向锁

表锁如何和行锁共存

举个例子事务A中锁住表中的一行,事务b锁住了一整个表

但马上就有一个问题,事务a既然锁住了一行,其他事务就不可能修改这一行.与事务b锁住了一整个表形成了冲突.所以没有意向锁的时候,行锁与表锁之间就会出现问题.

有了意向锁之后前面例子中的事务A,在申请行锁(写锁)之前,数据库会自动线给事务A申请的表加上意向拍它锁

意向所是表还是行锁

意向锁是表级别的锁,原因如下

当我们需要给一个加表锁的时候,我们需要根据意向锁判断表中有没有数据航被锁定,以确定是否能成功.如果意向锁是行锁,那么我们就得遍历表中所有数据来进行判断.如果意向锁是表锁,则我们直接判断一次就真的表中是否有数据被锁定了

IX 意向排它锁,IS 意向共享锁,X 排它锁,S 共享锁

xIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突冲突
S冲突冲突兼容兼容
IS冲突兼容兼容兼容
Last modification:November 17, 2023
如果觉得我的文章对你有用,请随意赞赏