数据库中的锁

在很多Mysql文章中都能见到不少“锁”,下面整理下常见的“锁”

1、按照并发策略分类

数据库的并发策略与Java类似,分为了三种

1.1 乐观锁

乐观锁认为一个用户读数据的时候,其他人不会修改它所读的那个数据,只在更新的时候会判断一下在此期间别人有没有更新过这个数据

1.2 悲观锁

悲观锁则相反,只要有操作数据的动作发生,就会加上锁,当操作结束后,才会释放锁。根据锁排他性,悲观锁又可以分为"共享锁"和”独占锁"。

1.3 Version(时间戳)

Version机制在数据表中添加一列"时间戳",每次读数据的时候,都会把这个字段也读进来,写回去时,都会把这个字段加1,当事务提交的时候,比对这个字段,如果比数据库中的值大,那么就可以写入。
该方式也可以认为是乐观锁的实现方式之一。

2、按照锁的颗粒度划分

Mysql按照颗粒度大致可以分为全局锁,表级锁和行锁。

2.1 全局锁

全局锁,就是锁住整个数据库,数据库对外状态呈现“只读”,它的加锁方式是Flush tables with read lock ,也就是常说的FTWRL,解锁方式是unlock tables

这种锁加锁要小心,如果是对主库加全局锁,那么所有业务都会暂停,如果是对从库加全局锁,那从库就会暂停,影响到主从一致的进度。

不过就算如此,全局锁也有他的应用场景,就是做全库逻辑备份,使用全局锁就是为了保证在备份时视图保持一致。

2.2 表级锁

表级锁又分为了元数据锁和表锁。

2.2.1 元数据锁(MetaData Lock)

简称MDL,元数据锁是在Mysql 5.5 版本引入的,它的引入也是源于Mysql一个著名的bug :bug#989

 session1:  
 BEGIN;
 INSERT INTO t ... ;
 COMMIT;  

 session2:  
 DROP TABLE t;
 
 
## binlog
DROP TABLE t; 
BEGIN;  
INSERT INTO t ... ; 
COMMIT;

很明显,如果真这么发生了,那么insert会报出error,本质上是DML和DDL之间发生了冲突,元数据就是为了这个而引入的。

如果对一张表执行DDL,也就是增删改查操作,会自动加MDL读锁;如果对一张表执行DML,也就是改变表的结构语句,会自动加MDL写锁。同样,读读共享,其余不共享。

2.2.2 表锁

表锁顾名思义就是锁住整个表,分为了读锁和写锁。加锁方式是lock tables yourTableName Read/Write,解锁方式是unlock tables

2.3 行锁

Innode引擎支持行锁,但是MyIsAm不支持行锁,这也是Innodb可以后来居上的原因之一。

行锁加锁方式是select 字段1,字段2,... FOR Update/ Lock in Share Mode

行锁又可以细分为三种

2.3.1 Record Lock

Record Lock就是最正常的行锁了,就是锁着表中的一行,加锁方式就是上面的那样。不过要记住,读读共享,其他的排斥。

2.3.2 Gap Lock

间隙锁,是RR级别下innodb还是可以解决部分幻读问题的原因之一。

image-20230328022856088

例如事务ASelect * where id=11 FOR update会产生间隙锁,那么事务B就不能insert id=20的记录。

2.3.3 Next-key Lock

Next-key LOCK是Record lock和gap lock的组合版,是左开右闭的区间,但是间隙锁是左开右开的区间。

3、加锁逻辑

Next-key.drawio

删除之前的例子了,感觉有点乱,翻了很多文章,其实也有不少冲突的,尤其还是知名技术分享者的文章。

在分析如何加锁的时候,还是老老实实使用SQL语句\G分析吧。

不过,根据我看到的文章和自己实践的经历来看还是有规律可言的:

  1. 加锁时以Next-key为单位进行分析
  2. 查找过程中扫描的范围才加锁。
posted @ 2022-11-28 02:35  不要给我歪!  阅读(305)  评论(0编辑  收藏  举报