【MySQL技术内幕】6.4-锁的算法

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL DuckDB 分析主实例,集群系列 8核16GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 【MySQL技术内幕】6.4-锁的算法

1、行锁的3种算法

InnoDB存储引擎有3种行锁的算法,其分别是

  • Record Lock:单个行记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
  • Next-Key Lock:Gap Lock+ Record Lock,锁定一个范围,并且锁定记录本身

Record Lock总是会去锁住索引记录,如果 InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。

Next-Key Lock是结合了Gap lock和Record Lock的一种锁定算法,在Next-KeyLock算法下, InnoDB对于行的查询都是采用这种锁定算法。例如一个索引有10,11,13和20这四个值,那么该索引可能被Next-Key Locking的区间为:

(-∞,10]

(10,11]

(11,13]

(13,20]

(20,+∞)

采用Next-Key Lock的锁定技术称为Next-Key Locking。其设计的目的是为了解决Phantom Problem,这将在下一小节中介绍。而利用这种锁定技术,锁定的不是单个值,而是一个范围,是谓词锁(predict lock)的一种改进。除了next-key locking,还有previous-key locking技术。同样上述的索引10、11、13和20,若采用previous-key locking技术,那么可锁定的区间为:

(-∞,10)

[10,11)

[11,13)

[13,20)

[20,+∞)

若事务T1已经通过next-key locking锁定了如下范围:

(10,11]、(11,13]

然而,当查询的索引含有唯一属性时, InnoDB存储引擎会对Next-Key Lock进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。看下面的例子,首先根据如下代码创建测试表t:

DROP TABLE IF EXISTS t;
CREATE TABLE t(a INT PRIMARY KEY );
INSERT INTO t SELECT 1;
INSERT INTO t SELECT 2;
INSERT INTO t SELECT 5;接着来执行下表中的SQL语句。

唯一索引的锁定示例

时间

会话A

会话B

1

BEGIN

2

SELECT * FROM t WHERE a=5 FOR UPDATE

3

BEGIN

4

INSERT INTO t SELECT 4

5

COMMIT #成功,不需要等待

6

COMMIT

表t共有1、2、5三个值。在上面的例子中,在会话A中首先对a=5进行ⅹ锁定。而由于a是主键且唯一,因此锁定的仅是5这个值,而不是(2,5)这个范围,这样在会话B中插入值4而不会阻塞,可以立即插入并返回。即锁定由 Next-Key Lock算法降级为了 Record Lock,从而提高应用的并发性。

正如前面所介绍的,Next-Key Lock降级为Record Lock仅在查询的列是唯一索引的情况下。若是辅助索引,则情况会完全不同。同样,首先根据如下代码创建测试表z:

CREATE TABLE z( a INT, b INT, PRIMARY KEY(a), KEY(b));
INSERT INTO Z SELECT 1,1;
INSERT INTO Z SELECT 3,1;
INSERT INTO Z SELECT 5,3;
INSERT INTO Z SELECT 7,6:
INSERT INTO Z SELECT 10,8;

表z的列b是辅助索引,若在会话A中执行下面的SQL语句:

SELECT FROM z WHERE b=3 FOR UPDATE

很明显,这时SQL语句通过索引列b进行查询,因此其使用传统的Next-Key Locking技术加锁,并且由于有两个索引,其需要分别进行锁定。对于聚集索引,其仅对列a等于5的索引加上Record Lock。而对于辅助索引,其加上的是Next-Key Lock,锁定的范围是(1,3),特别需要注意的是, InnoDB存储引擎还会对辅助索引下一个键值加上gap lock,即还有一个辅助索引范围为(3,6)的锁。因此,若在新会话中运行下面的SQL语句,都会被阻塞:

SELECT FROM z WHERE a = 5 LOCK IN SHARE MODE;
INSERT INTO z SELECT 4,2;
INSERT INTO z SELECT 6,5;

第一个SQL语句不能执行,因为在会话A中执行的SQL语句已经对聚集索引中列a=5的值加上ⅹ锁,因此执行会被阻塞。第二个SQL语句,主键插人4,没有问题,但是插人的辅助索引值2在锁定的范围(1,3)中,因此执行同样会被阻塞。第三个SQL语句,插人的主键6没有被锁定,5也不在范围(1,3)之间。但插入的值5在另一个锁定的范围(3,6)中,故同样需要等待。而下面的SQL语句,不会被阻塞,可以立即执行:

INSERT INTO z SELECT 8,6;
INSERT INTO z SELECT 2,0;
INSERT INTO z SELECT 6,7;

从上面的例子中可以看到, Gap Lock的作用是为了阻止多个事务将记录插入到同范围内,而这会导致Phantom Problem问题的产。例如在上面的例子中,会话A中用户已经锁定了b=3的记录。若此时没有 Gap Lock锁定(3,6),那么用户可以插人索引b列为3的记录,这会导致会话A中的用户再次执行同样查询时会返回不同的记录,即导致Phantom problem问题的产生。

用户可以通过以下两种方式来显式地关闭Gap Lock:

  • 将事务的隔离级别设置为 READ COMMITTED
  • 将参数 innodb_locks_unsafe_for_binlog设置为1

在上述的配置下,除了外键约束和唯一性检查依然需要的 Gap Lock,其余情况仅使用 Record Lock进行锁定。但需要牢记的是,上述设置破坏了事务的隔离性,并且对于replication,可能会导致主从数据的不一致。此外,从性能上来看, READ COMMITTED也不会优于默认的事务隔离级别 READ REPEATABLE。

在 InnoDB存储引擎中,对于INSERT的操作,其会检查插入记录的下一条记录是否被锁定,若已经被锁定,则不允许查询。对于上面的例子,会话A已经锁定了表z中b=3的记录,即已经锁定了(1,3)的范围,这时若在其他会话中进行如下的插入同样会导致阻塞:

INSERT INTO z SELECT 2,2;

因为在辅助索引列b上插入值为2的记录时,会监测到下一个记录3已经被索而将插入修改为如下的值,可以立即执行:

INSERT INTO z SELECT 2,0;

最后需再次提醒的是,对于唯一键值的锁定, Next-Key Lock降级为Record Lock仅存在于查询所有的唯一索引列。若唯一索引由多个列组成,而查询仅是查找多个唯一索引列中的其中一个,那么查询其实是range类型查询,而不是 point类型查询,故InnoDB存储引擎依然使用 Next-Key Lock进行锁定。

2、解决Phantom Problem

在默认的事务隔离级别下,即 REPEATABLE READ下, InnoDB存储引擎采用Next-Key Locking机制来避免 Phantom Problem(幻像问题)。这点可能不同于与其他的数据库,如 Oracle数据库,因为其可能需要在 SERIALIZABLE的事务隔离级别下才能解决 Phantom Problem。

Phantom Problen是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行。下面将演示这个例子,使用前小节所创建的表t。表t由1、2、5这三个值组成,若这时事务T1执行如下的SQL语句:

select * from t where a> 2 FOR UPDATE;

注意这时事务T1并没有进行提交操作,上述应该返回5这个结果。若与此同时,另一个事务T2插入了4这个值,并且数据库允许该操作,那么事务T1再次执行上述SQL语句会得到结果4和5。这与第一次得到的结果不同,违反了事务的隔离性,即当前事务能够看到其他事务的结果。

InnoDB存储引擎采用 Next-Key Locking的算法避免 Phantom Problem。对于上述的SQL语句 SELECT * FROM t WHERE a>2 FOR UPDATE,其锁住的不是5这单个值,而是对(2,+∞)这个范围加了X锁。因此任何对于这个范围的插入都是不被允许的,从而避免 Phantom Problem。

InnoDB存储引擎默认的事务隔离级别是 REPEATABLE READ,在该隔离级别下,其采用 Next-Key Locking的方式来加锁。而在事务隔离级别 READ COMMITTED下,其仅采用 Record Lock,因此在上述的示例中,会话A需要将事务的隔离级别设置为READ COMMITTED。

此外,用户可以通过 InnoDB存储引擎的 Next-Key Locking机制在应用层面实现唯一性的检查。例如

image.png

如果用户通过索引查询一个值,并对该行加上一个SLock,那么即使查询的值不在,其锁定的也是一个范围,因此若没有返回任何行,那么新插入的值一定是唯一的。也许有读者会有疑问,如果在进行第一步 SELECT… LOCK IN SHARE MODE操作时,有多个事务并发操作,那么这种唯一性检查机制是否存在问题。其实并不会,因为这时会导致死锁,只有一个事务的插人操作会成功,而其余的事务会抛出死锁的错误,如下表所示。

通过Next-Key Locking实现应用程序的唯一性检查

时间

会话A

会话B

1

BEGIN

2

SELECT * FROM z WHERE b=4 LOCK IN SHARE MODE

3

SELECT * FROM z WHERE b=4 LOCK IN SHARE MODE

4

INSERT INTO z SELECT 4, 4 #阻塞

5

INSERT INTO Z SELECT 4, 4

ERROR 1213(40001): Deadlock found when trying to get lock; try restarting transaction #抛出死锁异常

6

# INSERT插入成功


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
7月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
7月前
|
SQL AliSQL 关系型数据库
MYSQL的全局锁和表锁
本文介绍了MySQL中的锁机制,包括全局锁、表级锁及其应用场景。全局锁通过`Flush tables with read lock (FTWRL)`实现,主要用于全库逻辑备份,但会阻塞更新和结构变更操作。表级锁分为显式表锁(`lock tables`)和元数据锁(MDL),前者用于控制并发访问,后者自动加锁以确保读写正确性。文章还探讨了如何安全地为小表添加字段,建议通过设置DDL等待时间或使用MariaDB/AliSQL的NOWAIT/WAIT功能避免业务阻塞。这些方法有助于在高并发场景下优化数据库性能与安全性。
180 0
|
5月前
|
缓存 关系型数据库 MySQL
在MySQL中处理高并发和负载峰值的关键技术与策略
采用上述策略和技术时,每个环节都要进行细致的规划和测试,确保数据库系统既能满足高并发的要求,又要保持足够的灵活性来应对各种突发的流量峰值。实施时,合理评估和测试改动对系统性能的影响,避免单一措施可能引起的连锁反应。持续的系统监控和分析将对维护系统稳定性和进行未来规划提供重要信息。
276 15
|
4月前
|
运维 监控 算法
基于 Java 滑动窗口算法的局域网内部监控软件流量异常检测技术研究
本文探讨了滑动窗口算法在局域网流量监控中的应用,分析其在实时性、资源控制和多维分析等方面的优势,并提出优化策略,结合Java编程实现高效流量异常检测。
158 0
|
5月前
|
监控 算法 安全
基于 C# 基数树算法的网络屏幕监控敏感词检测技术研究
随着数字化办公和网络交互迅猛发展,网络屏幕监控成为信息安全的关键。基数树(Trie Tree)凭借高效的字符串处理能力,在敏感词检测中表现出色。结合C#语言,可构建高时效、高准确率的敏感词识别模块,提升网络安全防护能力。
136 2
|
7月前
|
监控 算法 JavaScript
基于 JavaScript 图算法的局域网网络访问控制模型构建及局域网禁止上网软件的技术实现路径研究
本文探讨局域网网络访问控制软件的技术框架,将其核心功能映射为图论模型,通过节点与边表示终端设备及访问关系。以JavaScript实现DFS算法,模拟访问权限判断,优化动态策略更新与多层级访问控制。结合流量监控数据,提升网络安全响应能力,为企业自主研发提供理论支持,推动智能化演进,助力数字化管理。
187 4
|
7月前
|
存储 监控 算法
内网监控桌面与 PHP 哈希算法:从数据追踪到行为审计的技术解析
本文探讨了内网监控桌面系统的技术需求与数据结构选型,重点分析了哈希算法在企业内网安全管理中的应用。通过PHP语言实现的SHA-256算法,可有效支持软件准入控制、数据传输审计及操作日志存证等功能。文章还介绍了性能优化策略(如分块哈希计算和并行处理)与安全增强措施(如盐值强化和动态更新),并展望了哈希算法在图像处理、网络流量分析等领域的扩展应用。最终强调了构建完整内网安全闭环的重要性,为企业数字资产保护提供技术支撑。
203 2
|
8月前
|
存储 监控 算法
基于 Python 哈希表算法的局域网网络监控工具:实现高效数据管理的核心技术
在当下数字化办公的环境中,局域网网络监控工具已成为保障企业网络安全、确保其高效运行的核心手段。此类工具通过对网络数据的收集、分析与管理,赋予企业实时洞察网络活动的能力。而在其运行机制背后,数据结构与算法发挥着关键作用。本文聚焦于 PHP 语言中的哈希表算法,深入探究其在局域网网络监控工具中的应用方式及所具备的优势。
262 7

推荐镜像

更多