数据库中的锁
在很多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还是可以解决部分幻读问题的原因之一。
例如事务ASelect * where id=11 FOR update
会产生间隙锁,那么事务B就不能insert id=20的记录。
2.3.3 Next-key Lock
Next-key LOCK是Record lock和gap lock的组合版,是左开右闭的区间,但是间隙锁是左开右开的区间。
3、加锁逻辑
删除之前的例子了,感觉有点乱,翻了很多文章,其实也有不少冲突的,尤其还是知名技术分享者的文章。
在分析如何加锁的时候,还是老老实实使用SQL语句\G
分析吧。
不过,根据我看到的文章和自己实践的经历来看还是有规律可言的:
- 加锁时以Next-key为单位进行分析
- 查找过程中扫描的范围才加锁。