mysql InnoDB引擎各种隔离级别的加锁机制

2024-08-23 19:44

本文主要是介绍mysql InnoDB引擎各种隔离级别的加锁机制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 概要
    • 前置知识了解
    • 各种隔离锁的验证
    • 小结

概要

我们都知道,mysql的InnoDB引擎在各种隔离级别下的加锁机制都是有差异的,但是对于各种隔离级别下如何加锁大家可能不太了解,今天我就通过一篇文章去带领大家去分析一下各个隔离级别的加锁过程,如果有误,欢迎大家在评论区指正!

今天实验的表结构定义如下,欢迎大家自己去尝试验证。我的mysql版本是5.7。其中id加上了唯一索引为了验证行锁的加锁过程。age加上非唯一索引,给大家演示一下gap的加锁过程。:

-- 学生表,id,姓名,年纪三个字段。
CREATE TABLE `t_stu` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '会员卡名称',`age` int NOT NULL,PRIMARY KEY (`id`) USING BTREE,KEY `name` (`name`),KEY `age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8mb3 COMMENT='会员商品表';INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (1, '小张', 1);
INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (3, '小王', 3);
INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (4, '小美', 4);
INSERT INTO `t_stu` (`id`, `name`, `age`) VALUES (44, '小美', 44);

前置知识了解

读锁和写锁的互斥情况,只有读锁和读锁可以并行执行,其他的情况都是互斥的。

读锁写锁
读锁x
写锁xx
  • mysql的表级锁有哪些,其中我们需要重点关注的就是元数据锁:
    1. 表锁:通常意义上我们是不会给表主动加锁。通过 LOCK TABLES t_stu READ,LOCK TABLES t_stu WRITE去加锁。通过unlock tables去解锁。在实际的业务过程中题主暂时未遇到过使用场景,所以这个锁不在我们的讨论范围。
    2. 元数据锁(MDL):这个锁我们需要关注一下,我们在表结构变更时会加一个MDL写锁,就是当我们操作表做DDL语句时,例如:给表加字段,给表加索引。当我们给做crud语句时会给表加MDL读锁。所以在高并发的场景下,不可以去对表做DDL操作,会锁表。 因为DDL语句不会被事物控制,所以对于小批量的数据题主没办法演示加锁情况,大家可以自行多加点数据去做演示。
    3. 意向锁:题主没关注这个。
    4. AUTO-INC 锁:主键索引自增时加的一个锁。

  • mysql的行锁有哪些:
    1. 行级锁: 加在索引上的锁,如果表上没有任何索引,InnoDB会使用一个内部的行ID来锁定行。在RR隔离级别下,我们在没有索引的表上去修改数据,其实是锁定了所有的行+加上行的间隙,并不是锁表。
    2. 间隙锁 GAP LOCK: 间隙锁是RR隔离级别下加在非唯一索引的两个数据之间的锁,注意是非唯一索引,理论上我们对主键索引去修改数据时是不会加间隙锁的。另外一种情况就是where范围查询时,锁定主键索引的区间。注意间隙锁只有在RR隔离级别下才存在。
    3. next-key lock: 行级锁+间隙锁组成的锁。

各种隔离锁的验证

  • 读未提交 (READ UNCOMMITTED): 按照顺序执行,我们会发现事物2在执行到第四步时被阻塞了,说明在读未提交的情况下是有行级锁存在的。并不是网上说的行级锁只存在于读已提交和可重复读的隔离级别。
-- 事物1 ----------------------------------------------------------
-- 1
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
-- 3
UPDATE `t_stu` SET `name` = 'aa' WHERE `id` = 1;
COMMIT;-- 事物2 ----------------------------------------------------
-- 2
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
-- 4
UPDATE `t_stu` SET `name` = 'aaa' WHERE `id` = 1;
COMMIT;

通过命令查看加锁状态,可以看到数据都被加了X锁,就是排它锁。
在这里插入图片描述
在这里插入图片描述

  • 读已提交 (READ COMMITTED):
-- 事物1 ----------------------------------------------------------
-- 1
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- 3
-- UPDATE `t_stu` SET `name` = 'aa' WHERE `id` = 1;
SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
-- 5
SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
-- 7
COMMIT;-- 事物2 ----------------------------------------------------
-- 2
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- 4
INSERT INTO `jl`.`t_stu` (`id`, `name`, `age`) VALUES (10, '小小美', 4);
-- 6
COMMIT;

注意这块的查询语句我加上了S共享锁,因为mysql默认查询时SELECT语句是默认不加锁的,所以为了方便演示我加上了读共享锁。上述语句我们在执行时,是能成功的插入数据的,通过锁分析也可得知在RC隔离级别下,对于普通索引的数据我们只加了行级锁,但是没有加gap lock的。当我们执行到步骤5,第六步未执行的情况下,我们看一下加锁状态,写请求加了X排它锁,读数据我加了共享锁。所以从分析就可以得知在RC隔离级别下,就算是普通的非唯一索引age,也不会加gap锁。

在这里插入图片描述

  • 可重复读 (REPEATABLE READ):
-- 事物1 ----------------------------------------------------------
-- 1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- 3
-- UPDATE `t_stu` SET `name` = 'aa' WHERE `id` = 1;
SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
-- 5
SELECT * FROM `t_stu` WHERE age = 4 LOCK IN SHARE MODE;
-- 7
COMMIT;-- 事物2 ----------------------------------------------------
-- 2
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- 4
INSERT INTO `jl`.`t_stu` (`id`, `name`, `age`) VALUES (10, '小小美', 4);
-- 6
COMMIT;

当我们在执行步骤4时其实就会被阻塞了,数据是没办法插入成功的,因为此时在age的的非唯一索引上加了age=4的数据的行级别锁,在非主键键索引上在age(3,4)(4,44)之间加了gap lock。导致了数据插入时就会锁冲突,没办法进行数据插入。
在这里插入图片描述
我们在演示一个其他的例子,我们把上面事物2的插入的id为10的age数据改成64。先看一数据库的数据:
在这里插入图片描述

-- 事物2 ----------------------------------------------------
-- 2
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- 4
INSERT INTO `jl`.`t_stu` (`id`, `name`, `age`) VALUES (10, '小小美', 64);
-- 4
-- 6
COMMIT;

最终发现,执行到步骤4时,数据是可以插入成功的,那就是因为,我们age为64的数据不在gap锁锁定age的范围(3,4),(4,44)范围内。

小结

通过上面的各种的案例演示,给大家展示了各种隔离级别下的数据的加锁状态。可能有些知识点是打破可大家对于加锁状态的认知的,大家可以通过本地数据库去模拟一下各种隔离级别的数据加锁状态,会加深大家对于这些知识的认知:行锁什么时候加,gap锁什么时候加,gap锁的锁定范围这些

这篇关于mysql InnoDB引擎各种隔离级别的加锁机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100326

相关文章

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

MySQL中On duplicate key update的实现示例

《MySQL中Onduplicatekeyupdate的实现示例》ONDUPLICATEKEYUPDATE是一种MySQL的语法,它在插入新数据时,如果遇到唯一键冲突,则会执行更新操作,而不是抛... 目录1/ ON DUPLICATE KEY UPDATE的简介2/ ON DUPLICATE KEY UP

MySQL分库分表的实践示例

《MySQL分库分表的实践示例》MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备... 目录一、分库分表的触发条件1.1 数据量阈值1.2 并发压力二、分库分表的核心技术模块2.1 水平分

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp

MySQL 表空却 ibd 文件过大的问题及解决方法

《MySQL表空却ibd文件过大的问题及解决方法》本文给大家介绍MySQL表空却ibd文件过大的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录一、问题背景:表空却 “吃满” 磁盘的怪事二、问题复现:一步步编程还原异常场景1. 准备测试源表与数据

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过