InnoDB解决幻读的方法详解
作者:mghio 发布时间:2024-01-15 13:50:53
前言
大部分人在日常的业务开发中,其实很少去关注数据库的事务相关问题,基本上都是 CURD 一把梭。正好最近在看 MySQL 的相关基础知识,其中对于幻读问题之前一直没有理解深刻,今天就来聊聊「InnoDB 是如何解决幻读的」,话不多说,下面进入主题。
事务隔离级别
事务隔离是数据库处理的基础之一,是 ACID 中的 I
。在 MySQL 的 InnoDB 引擎中支持在 SQL:1992 标准中的四种事务隔离级别,如下图所示,其中 P1 表示脏读(Dirty read),P2 表示不可重复读(Dirty read),P3 表示幻读(Phantom)。
为什么需要定义这么多隔离呢?从上图中也能猜出一二了,InnoDB 提供多个隔离级别主要原因是:让使用者可以在多个事务同时进行更改和执行查询时微调性能与结果的可靠性、一致性和可再现性之间的平衡的设置。是一种性能与结果可靠性间的 trade off
。
什么是幻读
在聊「InnoDB 解决幻读方式」前我们需要先了解幻读是什么,官方文档的描述如下:
A row that appears in the result set of a query, but not in the result set of an earlier query.
其中我加粗的「result set」是关键的地方,两次查询返回的是结果集,说明必须是一个范围查询操作。总结下,幻读就是:在同一个事务中,在前后两次查询相同范围时,两次查询得到的结果是不一致的。所以幻读会产生数据一致性问题。
InnoDB 解决幻读方式
为了解决上述的幻读问题,InnoDB 引入了两种锁,分别是「间隙锁」和「next-key 锁」。下面通过一个示例来描述这两种锁的作用分别是什么。假如存在一个这样的 B+ Tree 的索引结构,结构中有 4 个索引元素分别是:9527、9530、9535、9540。
此时当我们使用如下 SQL 通过主键索引查询一条记录,并且加上 X 锁(排它锁)时:
select * from user where id = 9527 for update;
这时就会产生一个记录锁(也就是行锁),锁定 id = 9527
这个索引。
在被锁定的记录(这里是 id = 9527)的锁释放之前,其它事务无法对这条被锁定记录做任何操作。再回忆一下,前面说的幻读定义「在同一个事务中,在前后两次查询相同范围时,两次查询得到的结果是不一致」。注意,这里强调的是范围查询。
InnoDB 要解决幻读问题,就必须得保证在如果在一个事务中,通过如下这条语句进行锁定时:
select * from user where id > 9530 and id < 9535 for update;
此时,另外一个语句再执行一如下这条 insert 语句时,需要被阻塞,直到上面这个获得锁的事务释放锁后才能执行。
insert into user(id, name, age) values(9533, 'Jack', 44);
为此,InnoDB 引入了「间隙锁」,它的主要功能是锁定一段范围内的索引记录。比如上面查询 id > 9530 and id < 9535
的时候,对 B+ Tree 中的(9530,9535)这个开区间范围的索引加间隙锁。
在这种加了间隙锁的情况下,其它事务对这个区间的数据进行插入、更新、删除都会被锁住直到这个获取到锁的事务释放。
这种是在区间之间的情况,你可能想到另外的一种情况:锁定多个区间,如下的一条语句:
select * from user where id > 9530 for update;
上面这条查询语句是针对 id > 9530
这个条件加锁,那么此时它需要锁定多个索引区间,所以在这种情况下 InnoDB 引入了「next-key 锁」机制。其实 next-key 锁的效果相当于间隙锁和记录锁的合集,记录锁锁定存在的记录行,间隙锁锁住记录行之间的间隙,而 next-key 锁它锁住的是两者之和。
在 InnoDB 中,每个数据行上的非唯一索引列上都会存在一把 next-key 锁,当某个事务持有该数据行的 next-key 锁时,会锁住一段左开右闭区间的数据。因此,当通过 id > 9530
这样一种范围查询加锁时,会加 next-key 锁,锁定区间是范围是:
(9530,9535] (9535,9540] (9540,+∞]
间隙锁(也叫 Gap 锁)和 next-key 锁的区别在于加锁的范围,间隙锁只锁定两个索引之间的引用间隙,而 next-key 锁会锁定多个索引区间,它包含「记录锁」和「间隙锁」。所以,当我们使用了范围查询,不仅仅命中了已存在的 Record 记录,还包含了 Gap 间隙。
来源:https://www.cnblogs.com/mghio/p/17289904.html


猜你喜欢
- 数据持久化vuex-persistedstate使用vuex是在中大型项目中必不可少的状态管理组件,刷新会重新更新状态,但是有时候我们并不希
- 今天在网上看到type的一段代码 ,然后查了一下文档,才知道type还有三个参数的用法。http://docs.python.org/2/l
- python库-dotenv包 | .env配置文件背景我们开发的每个系统都离不开配置信息,这些信息都非常敏感,一旦泄露出去后果非常严重,被
- 本文实例讲述了mysql外键的三种关系。分享给大家供大家参考,具体如下:因为有foreign key的约束,使得两张表形成了三种了关系:多对
- 在这篇文章中,我们将分析一个网络爬虫。网络爬虫是一个扫描网络内容并记录其有用信息的工具。它能打开一大堆网页,分析每个页面的内容以便寻找所有感
- 我就废话不多说了,大家还是看代码吧! import PyPDF2 import repdf_file = open('xxx.pdf
- 本人最近在做一个GUI项目时,需要用到matplotlib画图,写完代码运行时,发现所需要画图功能不能正常使用,在触发画图事件后,画布未更新
- MySQL默认编码是latin1,因业务需要把它转到UTF8。1、导出数据导出表结构 mysqldump -d dataname >
- 将表数据生成SQL脚本的存储过程示例:CREATE PROCEDURE dbo.UspOutputData @tablename sysna
- Access数据库被损坏主要分为以下几种情况:1、严重损坏;2、轻度损坏;3、有些表被损坏或有些表的部分记录被损坏。下面就分情况介绍解决办法
- 解决方式一:import matplotlib#1. 获取matplotlibrc文件所在路径matplotlib.matplotlib_f
- 今天做官网,把unslider做成指令,但是一直提示$(el).unslider() no a function,一开始我想复杂了,后来在网
- /** * @Purpose: Mysql数据库访问类 * @Package: * @Author: lisen@sellingclub.c
- 一:安装Anaconda和Tensorflow步骤:1:从官方网站下载Anacondahttps://www.anaconda.com/do
- django常见数据库配置错误出现报错代码为1045的这类几乎都是数据库配置出错报错1django.db.utils.Operational
- Navicat数据存放位置和备份数据库路径设置navicat的数据库存放位置在什么地方?带着这样的疑问,我们去解决问题,navicat是默认
- 在mysql网站时拿到的rpm包只能用root安装,不支持relocate用源码安装后./configure --prefix=/home/
- ASP+Access数据库的18条安全法则:1.首先,我们需要过滤所有客户端提交的内容,其中包括?id=N一类,另外还有提交的html代码中
- openpyxlopenpyxl是⼀个Python库,用于读取/写⼊Excel 2010 xlsx / xlsm / xltx / xltm
- 在类中每次实例化一个对象都会生产一个字典来保存一个对象的所有的实例属性,这样非常的有用处,可以使我们任意的去设置新的属性。每次实例化一个对象