相关文章

数据库系列:MySQL慢查询分析和性能优化

数据库系列:MySQL索引优化总结(综合版)

数据库系列:高并发下的数据字段变更

数据库系列:覆盖索引和规避回表

数据库系列:数据库高可用及无损扩容

数据库系列:使用高区分度索引列提升性能

数据库系列:前缀索引和索引长度的取舍

数据库系列:MySQL引擎MyISAM和InnoDB的比较

数据库系列:InnoDB下实现高并发控制

数据库系列:事务的4种隔离级别

数据库系列:RR和RC下,快照读的区别

数据库系列:MySQL InnoDB锁机制介绍

数据库系列:MySQL不同操作分别用什么锁?

数据库系列:业内主流MySQL数据中间件梳理

数据库系列:大厂使用数据库中间件解决什么问题?

数据库系列:索引失效场景总结

1 背景

我们在之前的一篇文章《数据库系列:MySQL InnoDB锁机制介绍》中介绍过InnodB引擎下几种常见锁的机制和原理。而在实际的select...for update操作中,锁影响的范围还是有区别的,下面就详细讨论下select 操作中的加锁规则。

2 回顾常见的锁类型

★InnoDB默认的事务隔离级别为可重复读(Repeated Read, RR),我们当下的所有介绍都是基于这个隔离级别为前提的。

  • 记录锁(Record Locks):锁定单一行记录,InnoDB 使用记录锁来实现行级锁,这样允许多个事务并发访问不同的行。
  • 间隙锁(Gap Locks):InnoDB 的特性,用于锁定一个范围,但不包括实际的记录。这主要用于防止幻读(Phantom Reads)。
  • 临键锁(Next-Key Locks):InnoDB 存储引擎的一种锁定机制,在执行查询语句时,根据查询条件所锁定的一个范围。这个范围中包含有间隙锁和记录锁。它的设计目的是为了解决幻读(Phantom Reads)。

2.1 记录锁(Record Locks)

记录锁一般在使用主键或者唯一索引进行查找时体现

记录锁,它封锁索引记录,例如:

select * from table where id=5 for update;

它会在id=1的索引记录上加锁,以阻止其他事务插入,更新,删除id=1的这一行。

需要说明的是:

select * from table where id=5;

则是快照读(SnapShot Read),它并不加锁,快照读可以参考作者这篇文章:数据库系列:RR和RC下,快照读的区别

2.2 间隙锁(Gap Locks)

间隙锁通常在不使用唯一索引进行范围查找时出现

间隙锁,它封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。

延续上面的那个例子继续演示:

# 表结构
users (Id PK, Name , Company); # 表中包含四条记录
5, Gates, Microsoft
7, Bezos, Amazon
11, Jobs, Apple
14, Elison, Oracle

执行SQL语句如下:

select * from users
where id between 7 and 13
for update; -- 假设我们要删除id在7到13之间的所有用户记录(不包括id=7和id=13)
DELETE FROM users WHERE id BETWEEN 7 AND 13;

这样的话,会封锁数据的区间,以防止其他事务插入id=8的记录。

假设没有间隙锁,则可能够插入成功,而之前的select事务,会发现检索的结果集莫名多了一条记录,即幻影数据。

所以间隙锁主要目的用于防止幻读(Phantom Reads),避免其他事务在间隔中插入数据,导致 『不可重复读』。

如果把事务的隔离级别降级为读提交(Read Committed, RC),对,就是互联网最常用的隔离级别,间隙锁则会自动失效。

2.3 临键锁(Next-Key Locks)

临键锁(Next-Key Locks)是数据库管理系统InnoDB中的一种重要锁定机制。这种锁是查询时根据查询条件锁定的一个范围,这个范围包括间隙锁和记录锁,左开右闭,即不锁住左边界,但会锁住右边界。临键锁的主要设计目的是为了解决所谓的“幻读”问题。

# 左开右闭 示例
(-infinity, 1]
(1, 7]
(7, +infinity)

依然沿用上面的例子,InnoDB引擎,RR隔离级别:

-- 创建一个示例表
CREATE TABLE users (
Id INT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
Company VARCHAR(255) NOT NULL,
); -- 插入一些示例数据
INSERT INTO users (id, name, company) VALUES (1, 'Alice', 'ali');
INSERT INTO users (id, name, company) VALUES (2, 'Brand', 'tencent');
INSERT INTO users (id, name, company) VALUES (3, 'Charlie', 'baidu'); -- 开始一个事务,并使用临键锁查询数据
START TRANSACTION;
SELECT * FROM users WHERE id > 1 FOR UPDATE; -- 在另一个事务中尝试插入新数据,将会被阻塞直到第一个事务释放锁
START TRANSACTION;
INSERT INTO users (id, name, age) VALUES (4, 'David', 30);
COMMIT; -- 第一个事务提交后,第二个事务可以继续执行插入操作
COMMIT;

临键锁的主要目的,也是为了避免幻读(Phantom Read),在事务隔离级别为可重复读的情况下,InnoDB存储引擎默认使用临键锁。这种锁提供了一种有效的机制来保证在并发环境中数据的完整性和一致性。

如果把事务的隔离级别降级为RC,临键锁则也会失效。

3 不同select操作的加锁规则

3.0 前置条件

# 表结构(姓名、公司、工号)
userinfo (Id PK, username, company, usercode); # 表中包含四条记录
5, Gates, Microsoft, 24
7, Bezos, Amazon,35
11, Jobs, Apple,37
14, Elison, Oracle,38

3.1 主键检索

1. 记录存在的情况

# 5是存在的记录,行锁
mysql> select * from userinfo where id=5 for update; mysql> update userinfo set username = "Brand" where id = 5;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # X 排他锁
# RECORD 记录锁
mysql> select * from performance_schema.data_lock_waits;
+---------------+-------------+
| lock_mode | lock_type|
+---------------+-------------+
| X | RECORD |
+---------------+-------------+

2. 记录不存在的情况

# 6是不存在的记录,间隙锁,锁住的区间为(5,7),对应上面的前置条件
mysql> select * from userinfo where id = 6 for update; mysql> insert into user values(6, 'Brand', 'Ali',100);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transactio # X 排他锁 + Gap 间隙锁
# RECORD 记录锁
mysql> select * from performance_schema.data_lock_waits;
+---------------+-------------+
| lock_mode | lock_type|
+---------------+-------------+
| X,GAP | RECORD |
+---------------+-------------+

3.2 唯一索引检索

与主键检索结果一致,因为这两种都是可以唯一确定索引值和区间范围的。

3.3 普通索引检索

1. 记录存在的情况

# 24是存在的记录,更新行锁,插入间隙锁。24要算在内,锁住的区间为 usercode的[24,35),对应上面的前置条件
mysql> select * from userinfo where usercode = 24 for update; mysql> insert into user values(6, 'Brand', 'Ali',25);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # X 排他锁
# RECORD 记录锁 + Gap 间隙锁
mysql> select * from performance_schema.data_lock_waits;
+---------------+-------------+
| lock_mode | lock_type|
+---------------+-------------+
| X,GAP | RECORD |
+---------------+-------------+

2. 记录不存在的情况

# 25是不存在的记录,间隙锁,锁住的区间为 usercode的(24,35),对应上面的前置条件
mysql> select * from userinfo where id = 25 for update; mysql> insert into user values(6, 'Brand', 'Ali',26);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transactio # X 排他锁 + Gap 间隙锁
# RECORD 记录锁
mysql> select * from performance_schema.data_lock_waits;
+---------------+-------------+
| lock_mode | lock_type|
+---------------+-------------+
| X,GAP | RECORD |
+---------------+-------------+

3.4 索引的范围检索

索引包括主键(默认)、唯一索引和其他普通索引

mysql> select * from userinfo where id > 4 for update;

mysql> insert into user values(66, 'Brand', 'Ali',25);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction # X 排他锁 + Gap 间隙锁
# RECORD 记录锁
mysql> select * from performance_schema.data_lock_waits;
+---------------+-------------+
| lock_mode | lock_type|
+---------------+-------------+
| X,GAP | RECORD |
+---------------+-------------+

可以对 id <= 4 的数据进行更新(如果有的话),而且他的数据都会被锁住,锁住的Id字段的范围是为:

(5, 7], (7, 11], (11,14], (14, +infinity)

3.5 普通检索(无索引)

表锁,因为需要扫描整张表。扫描期间所有的操作都不能被获取或变更。

4 总结

  • 事务隔离级别为可重复读(Repeated Read, RR)
  • 以主键或唯一索引作为查询条件,有存在值(记录)时是行锁,不存在值时触发间隙锁。
  • 普通索引作为查询条件,恒定间隙锁。
  • 索引作为查询条件,并以范围取值时,产生间隙锁。
  • 无索引时的普通检索,产生表锁。

InnoDB常用锁总结(行锁、间隙锁、临键锁、表锁)的更多相关文章

  1. 三分钟入门 InnoDB 存储引擎中的表锁和行锁

    各位对 "锁" 这个概念应该都不是很陌生吧,Java 语言中就提供了两种锁:内置的 synchronized 锁和 Lock 接口,使用锁的目的就是管理对共享资源的并发访问,保证数 ...

  2. MySQL锁(表锁,行锁,共享锁,排它锁,间隙锁)使用详解

    锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一 ...

  3. MySQL 行锁 表锁机制

    MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整的很惨!不知坑在何方?没事,我来给你们标记几个坑.遇到了可别乱踩.通过本章内容,带你学习MySQL的行锁 ...

  4. [转]MySQL 表锁和行锁机制

    本文转自:http://www.cnblogs.com/itdragon/p/8194622.html MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整 ...

  5. MySQL学习之——锁(行锁、表锁、页锁、乐观锁、悲观锁等)

    转载. https://blog.csdn.net/mysteryhaohao/article/details/51669741 锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是 ...

  6. MySQL锁(行锁、表锁、页锁、乐观锁、悲观锁等)

    锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是协调多个进程或县城并发访问某一资源的一种机制.在数据库当中,除了传统的计算资源(CPU.RAM.I/O等等)的争用之外,数据也是一 ...

  7. MySQL 避免行锁升级为表锁——使用高效的索引

    文章目录 普通索引 属性值重复率高 属性值重复率低 小结 众所周知,MySQL 的 InnoDB 存储引擎支持事务,支持行级锁(innodb的行锁是通过给索引项加锁实现的).得益于这些特性,数据库支持 ...

  8. 理解 mysql行锁和表锁

    在调用存储过程中,就会涉及到表锁,行锁这一概念:所谓区别:有索引的时候就是行锁,没有索引的时候就是表索. innodb 的行锁是在有索引的情况下,没有索引的表是锁定全表的. 表锁演示(无索引) Ses ...

  9. MySQL中锁详解(行锁、表锁、页锁、悲观锁、乐观锁等)

    原文地址:http://blog.csdn.net/mysteryhaohao/article/details/51669741 锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具.在计算机中,是 ...

  10. MySQL 行锁、表锁

    1. 多个事务操作同一行数据时,后来的事务处于阻塞等待状态.这样可以避免了脏读等数据一致性的问题.后来的事务可以操作其他行数据,解决了表锁高并发性能低的问题 2.InnoDB的行锁是针对索引加的锁,不 ...

随机推荐

  1. 线段树-多个懒标记pushdown

    P3373 [模板]线段树 2 这里需要用到两个懒标记,一个懒标记为add,记录加,另一个懒标记为mul,记录乘. 我们需要规定一个优先级,然后考虑如何将懒标记下传. 这里无非有两种顺序,一种是先乘后 ...

  2. Windows App SDK? C++/WinRT? 狗都不学!

    空荡荡的官网开发文档,打开直接心凉一截! 只写个Hello World教程就敢宣布自己为"跨时代"新产品? 什么"C++桌面开发者的狂欢"?什么Project ...

  3. 基于ADS1292芯片的解决方案之芯片简析

    基本资料: ADS1292芯片是多通道同步采样 24 位 Δ-Σ 模数转换器 (ADC),它们具有内置的可编程增益放大器 (PGA).内部基准和板载振荡器. ADS1292 包含 便携式 低功耗医疗心 ...

  4. python librosa 实例解析

    一 概念 librosa是一个用于音乐和音频分析的python包.它提供了创建音乐信息检索系统所需的构建块. 核心函数:   二 实例解析   实例A,确认是否安装成功: import librosa ...

  5. python 音频通道分离的源码实现

    一 前记 作为一个音频工程师,仅仅依靠鼠标点击,没有一些自己的小工具的话,肯定是不合格的了. 最近用到了一个音频通道分离的功能,这里就用python敲击了一下,这里做个备忘吧,给有需求的小伙伴抛砖引玉 ...

  6. Rancher 2.5.x 证书过期报错 x509: certificate has expired or is not yet valid 解决方案

    Rancher 的证书过期会出现什么状况?不可以继续通过Rancher UI访问你的集群 查看Rancher Server日志报错:x509: certificate has expired or i ...

  7. Android Studio安装插件重启插件消失

    问题 安装插件后,已经提示让重启IDE,但是重启后发现插件是安装失败了 解决方法 原因是自己改了配置,如果下载的插件是jar包,则可以安装,如果是zip压缩文件的插件,则是要我们手动解压一下 我上面的 ...

  8. Python 汇总列数据到行

    Python汇总Excel列数据到行(方法一) import pandas as pd # 读取Excel文件 df = pd.read_excel('C:\\Users\\liuchunlin2\\ ...

  9. FFmpeg开发笔记(五)更新MSYS的密钥环

    ​ <FFmpeg开发实战:从零基础到短视频上线>一书提到:使用MSYS对FFmpeg进行交叉编译时,需要事先安装交叉编译工具链,也就是执行下面命令. pacman -S mingw-w6 ...

  10. win7笔记本、台式机装centos7过程记录

    1.国内镜像网站下载centos的iso文件  链接点我: 2.找个u盘,格式化为NTFS格式,这样才能传4G以上大小的文件 3.iso直接复制到u盘是不行的,必须做启动盘.下载个ultra做,官网地 ...