MySQL锁机制

MySQL锁机制

为了保证数据并发访问时的一致性和有效性,任何一个数据库都存在锁机制。锁机制的优劣直接影响到数据库的并发处理能力和系统性能,所以锁机制也就成为了各种数据库的核心技术之一。

锁机制是为了解决数据库的并发控制问题而产生的。如在同一时刻,客户端对同一个表做更新或查询操作,为了保证数据的一致性,必须对并发操作进行控制。同时,锁机制也为实现 MySQL 的各个隔离级别提供了保证。

可以将锁机制理解为使各种资源在被并发访问时变得有序所设计的一种规则。

如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库显得尤其重要,也更加复杂。

按锁级别分类,可分为共享锁、排他锁和意向锁。也可以按锁粒度分类,可分为行级锁、表级锁和页级锁。

共享锁

共享锁的代号是 S,是 Share 的缩写,也可称为读锁。是一种可以查看但无法修改和删除的数据锁。

共享锁的锁粒度是行或者元组(多个行)。一个事务获取了共享锁之后,可以对锁定范围内的数据执行读操作。会阻止其它事务获得相同数据集的排他锁

排他锁

排他锁的代号是 X,是 eXclusive 的缩写,也可称为写锁,是基本的锁类型。

排他锁的粒度与共享锁相同,也是行或者元组。一个事务获取了排他锁之后,可以对锁定范围内的数据执行写操作。允许获得排他锁的事务更新数据,阻止其它事务取得相同数据集的共享锁和排他锁。

  • 如有两个事务 A 和 B,如果事务 A 获取了一个元组的共享锁,事务 B 还可以立即获取这个元组的共享锁,但不能立即获取这个元组的排他锁,必须等到事务 A 释放共享锁之后才可以。
  • 如果事务 A 获取了一个元组的排他锁,事务 B 不能立即获取这个元组的共享锁,也不能立即获取这个元组的排他锁,必须等到 A 释放排他锁之后才可以。

意向锁

为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁。

意向锁是一种表锁,锁定的粒度是整张表,分为意向共享锁(IS)和意向排他锁(IX)两类。

意向共享锁表示一个事务有意对数据上共享锁或者排他锁。“有意”表示事务想执行操作但还没有真正执行。

锁和锁之间的关系,要么是相容的,要么是互斥的。

  • 锁 a 和锁 b 相容是指:操作同样一组数据时,如果事务 t1 获取了锁 a,另一个事务 t2 还可以获取锁 b;
  • 锁 a 和锁 b 互斥是指:操作同样一组数据时,如果事务 t1 获取了锁 a,另一个事务 t2 在 t1 释放锁 a 之前无法释放锁 b。

锁模式的兼容情况

其中共享锁、排他锁、意向共享锁、意向排他锁相互之间的兼容/互斥关系如下表所示,其中 Y 表示相容,N 表示互斥。

参数XSIXIS
X(排他锁)NNNN
S(共享锁)NYNY
IX(意向排他锁)NNYY
IS(意向共享锁)NYYY

如果一个事务请求的锁模式与当前的锁兼容,InnoDB 就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

为了尽可能提高数据库的并发量,需每次锁定的数据范围越小越好,越小的锁其耗费的系统资源越多,系统性能下降。为在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度”的概念。

MySQL表锁、行锁和页锁

MySQL 按锁的粒度可以细分为行级锁、页级锁和表级锁。我们可以将锁粒度理解成锁范围。

表级锁(table lock)

表级锁为表级别的锁定,会锁定整张表,可以很好的避免死锁,是 MySQL 中最大颗粒度的锁定机制。

一个用户在对表进行写操作(插入、删除、更新等)时,需要先获得写锁,这会阻塞其它用户对该表的所有读写操作。没有写锁时,其它读取的用户才能获得读锁,读锁之间是不相互阻塞的。

表级锁最大的特点就是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。当然,锁定颗粒度大带来最大的负面影响就是出现锁定资源争用的概率会很高,致使并发度大打折扣。

不过在某些特定的场景中,表级锁也可以有良好的性能。

  • 例如,READ LOCAL 表级锁支持某些类型的并发写操作。另外,写锁也比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁则不能插入到写锁的前面)。

使用表级锁的主要是 MyISAM,MEMORY,CSV 等一些非事务性存储引擎。

尽管存储引擎可以管理自己的锁,MySQL 本身还是会使用各种有效的表级锁来实现不同的目的。

  • 例如,服务器会为诸如 ALTER TABLE 之类的语句使用表级锁,而忽略存储引擎的锁机制。

页级锁(page lock)

页级锁是 MySQL 中比较独特的一种锁定级别,在其他数据库管理软件中并不常见。

页级锁的颗粒度介于行级锁与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力同样也是介于上面二者之间。另外,页级锁和行级锁一样,会发生死锁。

页级锁主要应用于 BDB 存储引擎。

行级锁(row lock)

行级锁的锁定颗粒度在 MySQL 中是最小的,只针对操作的当前行进行加锁,所以行级锁发生锁定资源争用的概率也最小。

行级锁能够给予应用程序尽可能大的并发处理能力,从而提高需要高并发应用系统的整体性能。虽然行级锁在并发处理能力上面有较大的优势,但也因此带来了不少弊端。

由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也就更多,带来的消耗自然也就更大。此外,行级锁也最容易发生死锁。所以说行级锁最大程度地支持并发处理的同时,也带来了最大的锁开销。

行级锁主要应用于 InnoDB 存储引擎。

随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量也越来越多,实现算法也会越来越复杂。不过,随着锁定资源颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也会随之提升。

MySQL 3 种锁的特性

表级锁行级锁页级锁
开销介于表级锁和行级锁之间
加锁介于表级锁和行级锁之间
死锁不会出现死锁会出现死锁会出现死锁
锁粒度介于表级锁和行级锁之间
并发度一般

从上述特点可见,很难笼统的说哪种锁更好,只能具体应用具体分析。

从锁的角度来说,表级锁适合以查询为主,只有少量按索引条件更新数据的应用,如 Web 应用。而行级锁更适合于有大量按索引条件,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

MySQL InnoDB行锁

在 MySQL 中,InnoDB 行锁通过给索引上的索引项加锁来实现,如果没有索引,InnoDB 将通过隐藏的聚簇索引来对记录加锁。

InnoDB 支持 3 种行锁定方式:

  • 行锁(Record Lock):直接对索引项加锁。
  • 间隙锁(Gap Lock):锁加在索引项之间的间隙,也可以是第一条记录前的“间隙”或最后一条记录后的“间隙”。
  • Next-Key Lock:行锁与间隙锁组合起来用就叫做 Next-Key Lock。 前两种的组合,对记录及其前面的间隙加锁。

默认情况下,InnoDB 工作在可重复读(默认隔离级别)下,并且以 Next-Key Lock 的方式对数据行进行加锁,这样可以有效防止幻读的发生。

Next-Key Lock 是行锁与间隙锁的组合,这样,当 InnoDB 扫描索引项的时候,会首先对选中的索引项加上行锁(Record Lock),再对索引项两边的间隙(向左扫描扫到第一个比给定参数小的值, 向右扫描扫到第一个比给定参数大的值, 然后以此为界,构建一个区间)加上间隙锁(Gap Lock)。如果一个间隙被事务 T1 加了锁,其它事务不能在这个间隙插入记录。

要禁止间隙锁的话,可以把隔离级别降为读已提交(READ COMMITTED),或者开启参数 innodb_locks_unsafe_for_binlog。

注意:以上语句描述的情况,与 MySQL 所设置的事务隔离级别有较大的关系。

开启一个事务时,InnoDB 存储引擎会在更新的记录上加行级锁,此时其它事务不可以更新被锁定的记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值