1.MySQL面试题之innodb如何解决幻读

1. 写在前面

在数据库系统中,幻读(Phantom Read)是指在一个事务中,两次读取同一范围的数据集时,由于其他事务的插入操作,导致第二次读取结果集发生变化的问题。InnoDB 作为 MySQL 的一个存储引擎,通过多种机制来解决幻读问题,主要包括锁机制和隔离级别。

2. 幻读问题的产生

假设有一个事务 T1,它在某个条件下查询了一批记录。在 T1 进行第一次查询后,如果另一个事务 T2 在 T1 的查询范围内插入了新的记录,那么当 T1 再次查询时,会发现多出了 T2 插入的记录,这就是幻读。

3. InnoDB 如何解决幻读

InnoDB 通过以下两种主要机制来解决幻读问题:

  1. Next-Key Locks(间隙锁)
  2. MVCC(多版本并发控制)

3.1 Next-Key Locks

Next-Key Locks(间隙锁)是 InnoDB 存储引擎在实现可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)隔离级别时使用的一种锁机制。它结合了记录锁和间隙锁,用于锁定一个记录及其前后的间隙,防止其他事务在间隙中插入新的记录,从而避免幻读。

3.1.1 组成

Next-Key Locks 是记录锁(Record Lock)和间隙锁(Gap Lock)的组合。具体来说:

  • 记录锁(Record Lock):锁定单个记录,防止其他事务对该记录进行修改。
  • 间隙锁(Gap Lock):锁定记录之间的间隙,防止其他事务在间隙中插入新的记录。

3.1.2 工作原理

Next-Key Locks 的工作原理是通过锁定一个记录及其前后的间隙,确保在一个事务中,任何插入操作都不会影响到该事务已经读取的数据范围,从而避免幻读。
假设有一个表 employees,包含以下数据:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO employees (id, name) VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');

在可重复读隔离级别下,事务 T1 和 T2 的操作如下:

-- 事务 T1
START TRANSACTION;
SELECT * FROM employees WHERE id BETWEEN 1 AND 3;-- 事务 T2
START TRANSACTION;
INSERT INTO employees (id, name) VALUES (4, 'David');
COMMIT;-- 事务 T1
SELECT * FROM employees WHERE id BETWEEN 1 AND 3;
COMMIT;

在上述操作中,T1 在第一次查询时会锁定 id 在 1 到 3 之间的记录及其前后的间隙:

  • 锁定记录 id=1 及其前后的间隙 (-∞, 1]
  • 锁定记录 id=2 及其前后的间隙 (1, 2]
  • 锁定记录 id=3 及其前后的间隙 (2, 3]
  • 锁定记录 id=4 及其前后的间隙 (3, +∞)

由于 T1 使用的是可重复读隔离级别,InnoDB 通过 Next-Key Locks 确保 T1 在第二次查询时,读取的结果集不会受到 T2 插入操作的影响,从而避免了幻读。

3.1.3 Next-Key Locks 的应用场景

Next-Key Locks 主要应用于以下隔离级别:

  • 可重复读(REPEATABLE READ):在该隔离级别下,InnoDB 使用 Next-Key Locks 确保在一个事务中,读取的数据集在整个事务期间保持一致,避免幻读。
  • 串行化(SERIALIZABLE):在该隔离级别下,InnoDB 通过 Next-Key Locks 确保所有读取操作都加锁,事务之间完全隔离,避免幻读。

3.1.4 Next-Key Locks 的优缺点

优点:

  • 避免幻读:通过锁定记录及其前后的间隙,Next-Key Locks 可以有效避免幻读问题。
  • 数据一致性:在高并发环境下,Next-Key Locks 可以确保数据的一致性

缺点:

  • 锁粒度较大:由于 Next-Key Locks 锁定了记录及其前后的间隙,锁粒度较大,可能会影响并发性能。
  • 死锁风险:在高并发环境下,Next-Key Locks 可能会导致死锁,需要进行死锁检测和处理。
    死锁的详细原因下面我们展开说。

3.2 间隙锁(Gap Lock)

间隙锁是 Next-Key Locks 的一个重要组成部分,用于锁定记录之间的间隙,防止其他事务在间隙中插入新的记录。间隙锁的范围包括:

  • 起始记录之前的间隙,例如 (-∞, 1)
  • 两条记录之间的间隙,例如 (1, 2)
  • 结束记录之后的间隙,例如 (3, +∞)
    通过锁定这些间隙,InnoDB 可以确保在一个事务中,任何插入操作都不会影响到该事务已经读取的数据范围,从而避免幻读。

3.3 MVCC(多版本并发控制)

多版本并发控制(MVCC, Multi-Version Concurrency Control)是一种用于管理数据库并发访问的技术。MVCC 通过为每个事务提供一个一致的视图,确保在高并发环境下,事务可以独立地进行读写操作,而不会相互干扰。InnoDB 存储引擎在实现可重复读(REPEATABLE READ)和读已提交(READ COMMITTED)隔离级别时,广泛使用了 MVCC 技术。

3.3.1 基本原理

MVCC 的核心思想是为每个数据行维护多个版本,并通过版本号或时间戳来区分这些版本。每个事务在读取数据时,会根据事务开始时的快照视图,读取符合其版本号或时间戳的数据。这样,不同事务可以同时读取和写入数据库,而不会相互阻塞。
数据版本
在 InnoDB 中,每行数据都有两个隐藏的列,用于实现 MVCC:

  • 事务 ID(Transaction ID):表示创建或最后修改该行数据的事务 ID。
  • 回滚指针(Rollback Pointer):指向数据行的前一个版本,用于实现回滚操作。

当一个事务对数据行进行修改时,会创建该数据行的一个新版本,并更新事务 ID 和回滚指针。

3.3.2 实现细节

MVCC 主要通过以下两个操作来实现:

  • 快照读(Snapshot Read)
  • 当前读(Current Read)
3.3.2.1 快照读(Snapshot Read)

快照读是指事务读取数据时,读取的是数据的快照版本,而不是当前最新的数据。快照版本是事务开始时的数据状态。快照读不会加锁,因此可以实现高效的并发访问。
快照读的典型操作包括:
SELECT 语句(不带 FOR UPDATE 或 LOCK IN SHARE MODE)
这个面试被问过,大家注意

3.3.2.2 当前读(Current Read)

当前读是指事务读取数据时,读取的是当前最新的数据,并且会对读取的数据加锁,以确保数据一致性。当前读通常用于更新操作。

当前读的典型操作包括:

  • SELECT … FOR UPDATE
  • SELECT … LOCK IN SHARE MODE
  • UPDATE
  • DELETE
  • INSERT

3.3.3 MVCC 在不同隔离级别下的表现

MVCC 在不同的隔离级别下有不同的表现:

  1. 读未提交(READ UNCOMMITTED):在该隔离级别下,事务可以读取其他事务未提交的数据,不使用 MVCC。
  2. 读已提交(READ COMMITTED):在该隔离级别下,事务每次读取数据时,读取的是当前最新的已提交版本。MVCC 确保事务读取的数据是已提交的最新版本。
  3. 可重复读(REPEATABLE READ):在该隔离级别下,事务在整个生命周期内,读取的是事务开始时的一致性视图。MVCC 确保事务读取的数据在整个事务期间保持一致。
  4. 串行化(SERIALIZABLE):在该隔离级别下,事务之间完全隔离,所有读取操作都加锁,不使用 MVCC。

3.3.4 MVCC 的优缺点

优点:

  • 高并发性能:通过快照读,事务可以在不加锁的情况下读取数据,提高了并发性能。
  • 减少锁争用:MVCC 避免了读写锁争用问题,提高了系统的吞吐量。
  • 数据一致性:通过为每个事务提供一致性视图,MVCC 确保了数据的一致性和隔离性。

缺点:

  • 存储开销:由于每行数据需要维护多个版本,MVCC 会增加存储开销。
  • 垃圾回收:需要定期清理过期的版本数据,以防止存储空间的浪费。
  • 实现复杂:MVCC 的实现需要维护复杂的数据结构和版本管理逻辑。

假设有一个表 employees,包含以下数据:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO employees (id, name) VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');

在可重复读隔离级别下,事务 T1 和 T2 的操作如下:

-- 事务 T1
START TRANSACTION;
SELECT * FROM employees WHERE id = 1;-- 事务 T2
START TRANSACTION;
UPDATE employees SET name = 'Bob Updated' WHERE id = 2;
COMMIT;-- 事务 T1
SELECT * FROM employees WHERE id = 2;
COMMIT;

在上述操作中,T1 在第一次查询时读取了 id=1 的记录。此时,T2 更新了 id=2 的记录,并提交了事务。由于 T1 使用的是可重复读隔离级别,InnoDB 通过 MVCC 确保 T1 在第二次查询时,读取的 id=2 的记录仍然是事务开始时的一致性视图,而不是 T2 更新后的数据。

4. 高并发环境下,Next-Key Locks 死锁分析

在高并发环境下,Next-Key Locks(间隙锁)可能会导致死锁的原因主要包括以下几个方面:

4.1 锁竞争

在高并发环境中,多个事务可能会同时尝试锁定相同的记录或间隙。由于 Next-Key Locks 锁定的范围较大,锁竞争的概率增加。例如,两个事务可能会同时尝试插入不同的记录,但由于间隙锁的存在,它们可能会互相等待对方释放锁,从而导致死锁。
假设有一个表 employees,包含以下数据:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO employees (id, name) VALUES (1, 'Alice'), (3, 'Charlie');

在高并发环境下,事务 T1 和 T2 的操作如下:

-- 事务 T1
START TRANSACTION;
SELECT * FROM employees WHERE id = 1;
-- 锁定记录 id=1 及其前后的间隙 (-∞, 1] 和 (1, 3)-- 事务 T2
START TRANSACTION;
SELECT * FROM employees WHERE id = 3;
-- 锁定记录 id=3 及其前后的间隙 (1, 3] 和 (3, +∞)-- 事务 T1
INSERT INTO employees (id, name) VALUES (2, 'Bob');
-- 尝试锁定间隙 (1, 3),但被事务 T2 锁定-- 事务 T2
INSERT INTO employees (id, name) VALUES (2, 'David');
-- 尝试锁定间隙 (1, 3),但被事务 T1 锁定

在上述操作中,T1 和 T2 互相等待对方释放间隙锁,从而导致死锁。

4.2 锁顺序不一致

如果不同事务获取锁的顺序不一致,也可能导致死锁。例如,一个事务先锁定记录 A 再锁定记录 B,而另一个事务先锁定记录 B 再锁定记录 A,这种锁顺序的不一致可能导致两个事务互相等待对方释放锁,从而导致死锁。
假设有一个表 employees,包含以下数据:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO employees (id, name) VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');

在高并发环境下,事务 T1 和 T2 的操作如下:

-- 事务 T1
START TRANSACTION;
SELECT * FROM employees WHERE id = 1;
-- 锁定记录 id=1 及其前后的间隙 (-∞, 1] 和 (1, 2)-- 事务 T2
START TRANSACTION;
SELECT * FROM employees WHERE id = 3;
-- 锁定记录 id=3 及其前后的间隙 (2, 3] 和 (3, +∞)-- 事务 T1
SELECT * FROM employees WHERE id = 3;
-- 尝试锁定记录 id=3 及其前后的间隙 (2, 3] 和 (3, +∞),但被事务 T2 锁定-- 事务 T2
SELECT * FROM employees WHERE id = 1;
-- 尝试锁定记录 id=1 及其前后的间隙 (-∞, 1] 和 (1, 2),但被事务 T1 锁定

在上述操作中,T1 和 T2 获取锁的顺序不一致,导致互相等待对方释放锁,从而导致死锁。

4.3 锁粒度较大

Next-Key Locks 锁定的范围较大,包括记录及其前后的间隙,这增加了锁冲突的概率。在高并发环境下,锁粒度较大的情况下,多个事务可能会同时尝试锁定相同的间隙,从而导致死锁。
假设有一个表 employees,包含以下数据:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100)
);INSERT INTO employees (id, name) VALUES (1, 'Alice'), (3, 'Charlie');

在高并发环境下,事务 T1 和 T2 的操作如下:

-- 事务 T1
START TRANSACTION;
SELECT * FROM employees WHERE id BETWEEN 1 AND 3;
-- 锁定记录 id=1 和 id=3 及其前后的间隙 (-∞, 1]、(1, 3] 和 (3, +∞)-- 事务 T2
START TRANSACTION;
INSERT INTO employees (id, name) VALUES (2, 'Bob');
-- 尝试锁定间隙 (1, 3),但被事务 T1 锁定-- 事务 T1
INSERT INTO employees (id, name) VALUES (4, 'David');
-- 尝试锁定间隙 (3, +∞),但被事务 T2 锁定

在上述操作中,T1 和 T2 由于锁粒度较大,互相等待对方释放锁,从而导致死锁。

4.4 解决死锁的方法

  • 合理设计事务:尽量减少事务的执行时间,避免长时间持有锁。
  • 统一锁定顺序:确保不同事务获取锁的顺序一致,避免锁顺序不一致导致的死锁。
  • 分解大事务:将大事务分解为多个小事务,减少锁粒度和锁冲突的概率。
  • 死锁检测和回滚:InnoDB 内置了死锁检测机制,可以自动检测到死锁并回滚其中一个事务。应用程序可以捕获死锁异常并重试操作。

粉丝福利

博主经营的脱单圈子,定期组织线下免费活动,有兴趣的单身小伙伴可以添加
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392506.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【期货】收盘点评。昨天说的,p2409棕榈油在今天或者周一会走出行情

收盘点评 昨天说的,p2409棕榈油在今天或者周一会走出行情。事实就是如此。震荡了几天了,波幅不大的来回震荡,其实主力是不想震荡的,但是不震荡自己的货和行情走不出来。所以我昨天就说,应该就是这一两天会走出一波小行…

Linux中的无人值守安装脚本Kickstart

目录 一.kickstart自动安装脚本的作用 在企业中安装多台操作系统时面临的问题 如何解决以上问题? 二.实验环境 三.kickstart自动安装脚本的制作 通过模板生成kickstart文件 1.安装图形化生成kickstart自动安装脚本的工具 2.图形化工具配置流程 3.配置文件详…

力扣热题100_二叉树_94_二叉树的中序遍历

文章目录 题目链接解题思路解题代码 题目链接 94. 二叉树的中序遍历 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root [] 输…

C#中重写tospring方法

在C#中,重写ToString方法允许你自定义对象的字符串表示形式。当你想要打印对象或者在调试时查看对象的状态时,重写ToString方法非常有用。 默认情况下,ToString方法返回对象的类型名称。通过重写这个方法,你可以返回一个更有意义…

sqli 1- 10

sql靶场 第一关 首先我们需要判断是否存在sql注入点,前端界面提示我使用ID作为参数,在url地址栏输入?id1 通过输入不同的id值查询数据库相对应的内容,之后判断为数字型还是字符型 根据查询内容判断为字符型且有注入点,再通过联合查询&…

#71结构体案例2(三国游戏,冒泡排序)

效果&#xff1a; 代码&#xff1a; #include <iostream> #include <string> using namespace std;//英雄结构体 struct Hero {string name;int age;string gender; };//冒泡排序 void bubbleSort(struct Hero hArray[],int len) {for(int i0;i<len-1;i){for(i…

首届中文NL2SQL挑战赛亚军比赛攻略_BugCreater

关联比赛: 首届中文NL2SQL挑战赛 1. 任务描述 本次比赛的任务&#xff1a;根据Question、表格信息&#xff08;包含列名、列类型、内容&#xff09;&#xff0c;预测对应的SQL语句&#xff08;下图黄色部分&#xff09;。比赛只涉及单表查询&#xff0c;需要预测的有4部分&a…

什么是DDoS攻击?怎么防御DDoS攻击?

在网络安全领域&#xff0c;DDoS 攻击一直是热门话题&#xff0c;随着网络技术的不断发展和网络环境的复杂化演变&#xff0c;DDoS 攻击变得愈加频繁、更具破坏性。根据 2023 年网络安全态势研判分析年度综合报告&#xff0c;全年全网网络层的 DDoS 攻击次数达 2.51 亿次&#…

【Kubernetes】k8s集群的pod基础

目录 一.Pod概述 1.什么是pod 2.k8s中pod的两种使用方式 3.pod容器的常规使用流程 4.创建pod的3种方式 二.Pod中容器概述 1.容器分类 2.Pod容器的3种镜像拉取策略 3.Pod容器的3种重启策略 4.pod的dns策略 一.Pod概述 1.什么是pod Pod是kubernetes中最小的资源管理组…

MySQL深分页和浅分页

前言 最近有面试过也遇到了问关于深分页问题&#xff0c;在这里简单从MySQL、ES等方面分享一下自己对该问题认识和总结。 一、深分页定义 可以从ES定义上来划分浅分页和深分页的边界&#xff0c;即页数超过10000页为深分页&#xff0c;少于10000页为浅分页。 二、MySQL深分…

初学Ubuntu及遇到的问题

初学Ubuntu及遇到的问题 Ubuntu的安装 1.安装VMware Workstation Pro&#xff08;网上有很多资料&#xff0c;本文就不细说啦~~~&#xff09; 2.在Ubuntu官网下载iso文件>https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/20.04/ iso文件随便放在哪个位置&#xff…

内网穿透原理,免费内网穿透(简单使用),公网主动访问内网。

内网穿透的本质就是流量转发&#xff0c;把一个电脑的网卡上的流量数据&#xff0c;转发到另一个网卡的流量数据。 实现原理 网络协议分层就是&#xff0c;物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;应用层。我简单解释一下这些分层协议…

消灭星星游戏程序设计【连载十】——小星星的残影轨迹

消灭星星游戏程序设计【连载十】——小星星的残影轨迹 大家每次都可以在页面中下载本节内容的实现代码&#xff0c;一步一步从简单开始&#xff0c;逐步完成游戏的各种功能&#xff0c;如果大家有任何问题也欢迎留言交流。 游戏整体效果展示&#xff1a; 1、本节要达到的效果 …

MyBatis快速学习

目录 前言 MyBatis的具体使用 一些小工具&#xff1a;MyBatisX 常见问题&#xff1a; 1.表中字段名和实体属性名不一致 2.按条件查询&#xff08;单条件&#xff09;时的&#xff0c;查询条件怎么编写 3.按条件查询&#xff08;多条件&#xff09; 4.多条件查询时&…

pywinauto:Windows桌面应用自动化测试(七)

前言 上一篇文章地址&#xff1a; pywinauto&#xff1a;Windows桌面应用自动化测试&#xff08;六&#xff09;-CSDN博客 下一篇文章地址&#xff1a; 暂无 一、实战常用方法 1、通过Desktop快速获取窗口 通过之前章节我们了解到控制应用的方法为Application&#xff0…

10、springboot3 vue3开发平台-前端-elementplus, axios配置及封装使用, 包含token 存储

1. 准备工作 1.1 清除项目自带页面 删除views和components目录下所有东西&#xff1a; 1.2 修改App.vue <script setup lang"ts"></script><template><router-view></router-view> </template><style scoped></st…

基于R语言绘制GGE双标图2

参考资料&#xff1a; 严威凯等: 双标图分析在农作物品种多点试验中的应用【作物学报】 https://cran.r-project.org/web/packages/GGEBiplots/GGEBiplots.pdf 1、如何判断双标图是否充分体现数据中的规律 在对双标图的解释中&#xff0c;有一个隐含的假设&#xff0c;就是所…

COFFEE AI PARTNER -- 神奇的AI工具,相当我雇佣了一个AI员工,淘汰你的是会使用AI的人

COFFEE AI PARTNER介绍 COFFEE AI PARTNER是由 AI JAVA开发的一款生成式人工智能工具&#xff08;又名AI助手&#xff09;&#xff0c;尝试一下。 首先域名似乎正在备案中&#xff0c;企业邮箱似乎正在采购&#xff0c;目前服务地址是&#xff1a;COFFEE AI PARTNER-官网 官网…

git安装图文

1.下载 通过百度网盘分享的文件&#xff1a;git安装图文 链接&#xff1a;https://pan.baidu.com/s/17ZMiWUIULtrGGba5n-WLeA 提取码&#xff1a;anjm --来自百度网盘超级会员V3的分享 2.安装

【C语言篇】猜数字游戏(赋源码)

文章目录 猜数字游戏前言随机数生成randsrandtime设置随机数生成范围 猜数字游戏的实现 猜数字游戏 前言 在前两篇博客对于分支和循环语句进行了详细的介绍&#xff1a; 分支语句详解 循环语句详解 我们就可以写一写稍微有趣的代码了&#xff0c;比如&#xff1a; 写一个…