文章目录
相关文章:
MySQL中的锁原理(表锁、行锁、间隙锁、共享锁、排他锁)
表锁、行锁、间隙锁、共享锁(读锁)、排他锁(写锁)、Next-Key Locks 之间的关系
1. 前言
mysql的锁概念很多,容易混淆,本文进行汇总并介绍联系和区别,锁分类:
-
从
性能
上分为乐观锁
(用版本对比来实现)和悲观锁
-
从对数据库操作的类型分,分为
读锁
和写锁
(都属于悲观锁)- 读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响
- 写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁
-
从对数据操作的粒度分,分为
表锁
和行锁
虽然我们从不同角度对锁进行了分类,但是他们之间仍是有交叉的,例如:
读锁
和写锁
(都属于悲观锁)- 表锁下有读锁和写锁,行锁下也有读锁和写锁
- 不同的存储引擎,锁的实现会不同,甚至有的引擎不支持某种锁,例如MyISAM 不支持行级锁,innodb同时支持行锁和表锁。
1.1 锁是存储引擎层的,不是服务器层的
MySQL 不同的存储引擎支持不同的锁机制,所有的存储引擎都以自己的方式显现了锁机制,服务器层完全不了解存储引擎中的锁实现:
- InnoDB 存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。
- MyISAM 和 MEMORY 存储引擎采用的是表级锁(table-level locking),不支持行级锁
2. 什么是表锁
表锁就是锁住整张表,其他的用户对该表的任何操作都受影响,具体影响取决于锁的形式。
MySQL的表锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock):
表共享读锁
(Table Read Lock):不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;表独占写锁
(Table Write Lock):会阻塞其他用户对同一表的读和写操作;
语法:
#加锁,多个表通过逗号分隔
LOCK TABLES tbl_name {READ | WRITE},[ tbl_name {READ | WRITE},……]
#解锁
unlock tables;
2.1 表锁细节需要区分是MyISAM还是InnoDB
MyISAM
MyISAM在执行查询语句
(SELECT)前,会自动
给涉及的所有表加读锁
,在执行更新操作
(UPDATE、DELETE、INSERT等)前,会自动
给涉及的表加写锁
,这个过程并不需要用户干预
InnoDB
必须显示加表锁语句
注意:如果没有索引
,innodb的行锁会升级为表锁
,效果和表锁一样,锁住全部索引
3. 什么是行锁(record lock)
行锁就是对具体的索引数据进行加锁,与表所相比,粒度更小,减少竞争。
行锁不是只能在一行上加锁,可以是若干行,可以是连续的,也可以是跳跃的
对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会任何锁。
4. 什么是共享锁(读锁)和排他锁(写锁)
InnoDB实现了以下两种类型的行锁:
共享锁
(s):共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。排他锁
(X):排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
共享锁=读锁、排他锁=写锁 ,确切说mysql没有读锁和写锁概念的,但是在实际应用中,读锁表示并发操作,写锁不支持并发,必须阻塞,因此可以这样划等号。但是严格来说,在计算机概念中,不是所有的读锁都是支持并发的。
事务可以通过以下语句显示给记录集加共享锁或排锁:
- 共享锁(S):SELECT * FROM table_name WHERE …
LOCK IN SHARE MODE
- 排他锁(X):SELECT * FROM table_name WHERE …
FOR UPDATE
InnoDB引擎默认的修改数据语句,update,delete,insert
都会自动
给涉及到的数据加上排他锁
,select语句默认不会加任何锁类型
5. 意向锁
意向锁是InnoDB自动加的,不需用户干预。
为了允许行锁和表锁共存,实现多粒度锁机制
,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁
:
意向共享锁
(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。意向排他锁
(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
事务在请求行锁中的共享锁和排他锁前,会先自动获得意向锁。
6. 间隙锁(Gap锁)
间隙锁是行锁底层实现中的概念。
当我们用范围条件
而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是是所谓的间隙锁(GAP锁)。
例如,“ SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE
;”。字面上意思是锁住10-15的数据,如果id=10的数据已存在,那么别的用户不可以修改该条数据,但是如果id=15的数据并不存在,那么可以插入id=15的数据吗?
答案是否定的:不可以,因为无论该列中是否已有这样的值,因为该范围中(id 10-15)所有现有值之间的间隙是锁定的。
InnoDB使用间隙锁的目的,一方面是为了防止幻读
,以满足相关隔离级别的要求,对于上面的例子,要是不使用间隙锁,如果其他事务插入了(10,20)闭区间的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;另一方面,是为了满足其恢复和复制的需要
。有关其恢复和复制对机制的影响,以及不同隔离级别下InnoDB使用间隙锁的情况。
防止幻读,这个作用很大。
7. Next-Key Locks
Next-Key Locks 是行锁底层实现中的概念。
Next-Key锁是索引记录上的行锁定和索引记录之前的间隙上的间隙锁定的组合。
行锁与间隙锁组合起来用就叫做Next-Key Lock。组合起来就形成了一个区间,那么就会以区间为单位进行锁定,而不是单纯的以where 条件中的范围来定。例如你去买香蕉,要么买一整串,要么不买,不会让你仅买一根或2根的。
假定索引包含值10、11、13和20。此索引的可能的下一键锁定涵盖以下间隔,其中,圆括号表示排除区间端点,方括号表示包括端点,间隔集合:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
比如(13,20)是间隙,20是行记录,他们的Next-Key组合就是(13,20]
Next-Key锁有2个特点:
-
开区间和闭区间
前开后闭 -
间隙的扩大化
假设有个查询条件“SELECT c1 FROM t WHERE c1 BETWEEN 15 and 18 FOR UPDATE”,此时Next-Key锁的区间是(13,20],不是(15,18),这时不再是《3.7.1 间隙锁(Gap锁)》单纯的间隙概念了,而是会把覆盖的间隔集合完整的看做目标区间。
如果跨了多个间隔集合怎么办?都算在内,比如“SELECT c1 FROM t WHERE c1 BETWEEN 12 and 18 FOR UPDATE”,此时的Next-Key锁区间是(11,20]
参考
《【mysql】MySQL中的锁(表锁、行锁、间隙锁、共享锁、排他锁)》
《InnoDB Locking》mysql官网锁介绍
Mysql共享锁和排他锁
mySQL数据库间隙锁(mysql是如何解决幻读的)
深入了解mysql–gap locks,Next-Key Locks