数据库事务隔离级别及底层原理详解

news2024/9/19 11:04:36

本文详细记录了数据库中事务的隔离级别及其定义,以及每个隔离级别下可能会遇到哪些问题及对应的解决方案和原理,以下内容结合为各大平台的知识点加自己的理解进行的总结,希望大家在读完以后能对事务有个全新的认识~~

1. MySQL事务管理

自动提交

  • 在MySQL中,默认DML指令的执行时自动提交的,当我们执行一个DML指令之后,自动同

步到数据库中。

事务管理

开启事务,就是关闭自动提交

  • 在开始事务第一个操作之前,执行 start transaction 开启事务 。
  • 依次执行事务中的每个DML操作
  • 如果在执行的过程中的任何位置出现异常,则执行 rollback 回滚事务 。
  • 如果事务中所有的DML操作都执行成功,则在最后执行 commit 提交事务。

事务管理示例sql:

create database db_test3;

use db_test3;

# 库存表
create table stock(
  id int primary key auto_increment,
  name varchar(200),
  num int not null
)

# 订单表
create table order_db(
  id int primary key auto_increment,
  name varchar(200) not null,
  price double,
  num int
);

insert into stock(name,num) values('鼠标',10);
insert into stock(name,num) values('键盘',20);
insert into stock(name,num) values('耳机',30);

# 开启事务
start transaction;

# 操作1:扣减库存
update stock set num = num-1 where name = '鼠标';
select aaa; # 此处会执行失败

# 操作2:新增订单
insert into order_db(name,price,num) values('鼠标',20.5,1);

# 事务回滚:清除缓存中的操作,撤销当前事务已经执行的操作
rollback;

# 提交事务: 将缓存中的操作写入数据文件
commit;

2. 事务隔离级别

注:可重复读可以解决大部分的幻读现象,但仍有可能发生幻读(先执行快照读,后执行当前读)

数据库在执行DML语句(update、delete、insert)底层会默认执行一遍当前读

查看与设置隔离级别:

mysql默认的隔离级别:可重复读(REPEATABLE READ)。

  • 查看当前会话的隔离级别:select @@transaction_isolation;
  • 查看全局的隔离级别:select @@gobal.transaction_isolation;

设置事务隔离级别:

  • 会话级:set session transaction isolation level read committed(设置当前会话的隔离级别为读已提交);
  • 全局级:set global transaction isolation level read committed(设置全局会话的隔离级别为读已提交);

不同的现象

脏读(Dirty Read)事务读取了其他事务中未提交的数据,当其他事务将未提交的数据进行修改,导致另一个事务读取到的数据是不正确的,这种现象就是脏读,会导致数据的不一致性。

不可重复读(Non-Repeatable Read)指在一个事务内,不同时刻多次读取同一行的数据得到的结果不同。通常发生在一个事务内多次读取同一行数据,但在这些读取过程中,另一个事务修改了该行数据并且已经提交。因此,第一个事务两次读取同一行数据时得到的结果不一致,这种现象就是不可重复读。不可重复读可以导致事务逻辑错误或数据不一致的问题。

幻读(Phantom Read)指在同一个事务中,多次查询同一个范围的数据时,得到的结果集不一致。这是因为在查询期间,另一个事务插入或删除了符合查询条件的数据。通常发生在一个事务内查询某个范围的数据,但在这两次查询之间,另一个事务插入了新的符合查询条件的数据并且已经提交。因此,第一个事务两次查询同一范围的数据时得到的结果集不同,这种现象就是幻读。幻读可能会导致查询结果不一致或业务逻辑错误。


MySQL数据库事务隔离级别:

  • 读未提交
  • 读已提交 (Oracle数据库默认)
  • 可重复读 (MySQL默认级别)
  • 串行化

读未提交(read uncommitted)

T2可以读取T1执行但未提交的数据;可能会导致出现脏读、不可重复读和幻读。

//设置全局事务隔离级别为读未提交
set global transaction isolation level read uncommitted;

脏读,一个事务读取到了另一个事务中未提交的数据

演示:

T1事务

T2事务

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(4);

mysql> select * from a;

通过以上测试,可以看到,A事务读取到了B事务还没有提交的数据。这种现象就是脏读。


读已提交(read committed)

T2只能读取T1已经提交的数据;避免了脏读,但可能会导致不可重复度、幻读。

//设置全局事务隔离级别为读已提交
set global transaction isolation level read committed;

演示:

T1事务

T2事务

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(4);

mysql> select * from a;

mysql> commit;

mysql> select * from a;

通过以上测试看出,A事务只能读取到B事务提交之后的数据。这种隔离级别解决了脏读问题,但肯定是存在不可重复读和幻读问题。因为只要事务B进行了增删改操作之后并提交了,事务A读取到的数据肯定是不同的。即:不可重复读和幻读都存在。


可重复读(repeatable read)

T2执行第一次查询之后,在事务结束之前其他事务不能修改对应的数据,但可以操作其他数据;避免了不可重复读,解决了大部分幻读问题,但可能会存在少部分幻读现象。

//设置全局事务隔离级别为可重复读
set global transaction isolation level repeatable read;

演示:

A事务

B事务

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select empno,ename,sal

from emp

where empno=7369;

mysql> update emp

set ename='SMITH',sal=8000

where empno=7369;

mysql> commit;

mysql> select empno,ename,sal

from emp

where empno=7369;

通过以上测试得知:当事务隔离级别设置为可重复读时,避免了不可重复读问题。

那么在MySQL当中,当事务隔离级别设置为可重复读时,能够避免幻读问题吗?测试一下:

事务A

事务B

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(5);

mysql> commit;

mysql> select * from a;

通过以上测试得知:当事务隔离级别设置为可重复读时,也避免了幻读问题。是完全避免了幻读问题吗?并不是。请看以下测试:

事务A

事务B

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(6);

mysql> commit;

mysql> select * from a for update;

通过以上测试得知:当事务隔离级别设置为可重复读,MySQL会尽最大努力避免幻读问题,但这种隔离级别无法完全避免幻读问题。


串行化(serializable)

同时只允许一个事务对数据表进行操作;避免了脏读、虚读、幻读问题,缺点是效率低,因为这种隔离级别会导致事务排队处理,不支持并发。

//设置全局事务隔离级别为串行化 
set global transaction isolation level serializable;

演示:

事务A

事务B

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(7);

mysql> select * from a;

mysql> commit;

通过以上测试得知:当事务隔离级别设置为串行化时,事务只能排队执行,不支持并发

3. 可重复读的幻读问题

        通过以上的分析,在MySQL中默认的事务隔离级别为可重复读完全避免了脏读和不可重复读,在很大程度上避免了幻读问题(并不能完全解决),那么可重复读这一隔离级别是如何解决幻读的呢?方案包括两种:

  • 针对快照读(普通的select语句,如:select * from test;),是通过MVCC方式解决了幻读。因为在可重复读隔离级别下,通过快照读查询到的数据跟事务刚启动时查询到的数据是一致的,即使中途进行了DML(insert、update、delete)操作,读出来的结果也是一致的,因此解决了幻读问题。
  • 针对当前读(select * from test for update;),是通过next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行select * from test for update操作时,会为当前查询到的数据加上next-key lock锁,如果有其他的事务想在加了next-key lock锁范围内的数据进行插入、更新或删除时,该语句会被阻塞,无法执行,等释放next-key lock锁后才能继续执行该操作,因此解决了幻读问题。

3.1. 快照读是如何解决幻读的

        什么是快照读?普通的select语句都是采用的快照读。顾名思义:在整个事务的处理过程中,执行相同的一个select语句时,每次都是读取的快照。(快照指的是固定的某个时刻的数据,是数据库的一个一致性视图)。也就是说,当事务隔离级别是可重复读,并且执行的select语句是一个普通的select语句时,都会采用快照读的方式读取数据,底层实现原理是:

底层由 MVCC(多版本并发控制)实现,实现的方式是开始事务后,在执行第一个查询语句后,会创建一个 Read View ,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好的避免了幻读问题。

演示:

test_transaction数据库的事务隔离级别为可重复读

事务A

事务B

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a; //快照读

mysql> insert into a values(5);

mysql> commit;

mysql> select * from a; //快照读

3.2. 出现幻读的两种情况

        在同一个事务处理过程中,如果前后两次都采用快照读,或者都采用当前读,则不会出现幻读问题。如果第一次使用快照读,后面使用了当前读,则会出现幻读问题

3.2.1. 第一种产生幻读的场景

        A事务与B事务。在A事务中第一次查询使用快照读,B事务插入数据。然后在A事务中第二次查询使用当前读。则会产生幻读现象。演示:

事务A

事务B

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(5);

mysql> commit;

mysql> select * from a for update; // 产生了幻读

3.2.2. 第二种产生幻读的场景

        事务A与事务B,在事务A中第一次查询使用快照读,在事务B中插入一条数据,然后在事务A中更新事务B插入的那条记录(执行DML操作,底层默认执行一遍当前读),最后在事务A中再次使用快照读(以当前时刻的数据库状态创建Read View)。则会发生幻读现象

事务A

事务B

mysql> use test_transaction

mysql> use test_transaction

mysql> start transaction;

mysql> start transaction;

mysql> select * from a;

mysql> insert into a values(6);

mysql> commit;

mysql> update a set id=100 where id=6; //主要是因为这个SQL语句的执行触发了当前读

mysql> select * from a; // 产生了幻读

3.3. 总结可重复读的幻读问题

        MySQL的可重复读隔离级别(默认隔离级),根据不同的查询方式,分别提出了避免幻读的方案:

  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读。
  • 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读。

举例了两个发生幻读场景的例子。

  • 第一个例子:如果事务开启后,先执行快照读,然后这期间如果其他事务插入了一条记录,那么事务后续使用当前读进行查询的时候,就会发现两次查询的记录条目就不一样了,所以就发生幻读。
  • 第二个例子:如果事务开启后,先执行快照读,事务B往表中插入一条数据,当事务 A 更新了事务 B 的插入操作(DML操作)时,那么事务 A 前后两次查询的记录条目就不一样了,所以就发生幻读。

        所以,MySQL 可重复读隔离级别并没有彻底解决幻读,只是很大程度上避免了幻读现象的发生。要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Connecting weaviate with langflow across docker containers

题意:在Docker容器之间连接Weaviate与Langflow 问题背景: I am trying to build a local RAG application using Langflow. For my vectore store, I want to use a local Weaviate instance, hosted in a separate docker container on the same netwo…

使用dock构建基于lnmp的WrodPress

项目要求: 1.创建nginx容器环境 上传nginx.conf文件、上传阿里云镜像、上传html目录 2.准备mysql cd /opt mkdir mysql 上传my.conf文件、上传阿里云镜像、写好的Dockfile文件 3.准备php cd /opt mkdir php 上传所需文件: 构建各镜像: …

【艺术向】【素描创作记录】《如何为你的红颜知己创作一幅画像(之二)》

写在前面 之前分析过类似的创作过程,见博客【艺术向】【素描创作记录】《如何为你的红颜知己创作一幅画像》 本人业余时间修习素描多年,在此撰文记录《如何为你的红颜知己创作一幅画像(之二)》,博得对方好感&#xff…

JQuery+HTML+JavaScript:实现地图位置选取和地址模糊查询

本文详细讲解了如何使用 JQueryHTMLJavaScript 实现移动端页面中的地图位置选取功能。本文逐步展示了如何构建基本的地图页面,如何通过点击地图获取经纬度和地理信息,以及如何实现模糊查询地址并在地图上标注。最后,提供了完整的代码示例&…

【proteus经典实战】LCD滚动显示汉字

一、简介 Proteus是一款功能丰富的电子设计和仿真软件,它允许用户设计电路图、进行PCB布局,并在虚拟环境中测试电路功能。这款软件广泛应用于教育和产品原型设计,特别适合于快速原型制作和电路设计教育。Proteus的3D可视化功能使得设计更加直…

Elasticsearch ILM 热节点迁移至冷节点 IO 打满、影响读写解决方案探讨

1、实战问题 ILM(索引生命周期管理) 遇到热数据迁移至冷节点时造成 IO 打满影响读写的情况。 现在采取的方案是调整索引生命周期策略,定时的将Cold phase 开启/关闭。低峰开启,高峰关闭。 就是不知道这里面会有啥坑。 热节点&…

C++笔试强训7

文章目录 一、选择题1-5题6-10题 二、编程题题目一题目二 一、选择题 1-5题 基础知识,函数代码少,频繁调用的时候才适合定义内联函数。 故选C。 在C中,inline关键字是用来向编译器建议将函数体在每个调用点“内联展开”的。这意味着编译器会…

意得辑润色极致优惠方案

祝审稿人们编辑们及他们全家工作顺利身体健康万事如意心想事成🙇🏻🙇🏻🙇🏻 好人一生平安🙏🏻🙏🏻🙏🏻 #accept

探索 io_uring:理解高效异步 IO 的工作原理与实现细节

概述 io_uring 是一个 Linux 内核提供的高性能异步 I/O 框架,最初在 Linux 5.1 版本中引入。它的设计目标是解决传统的异步 I/O 模型(如 epoll 或者 POSIX AIO)在大规模 I/O 操作中效率不高的问题。 关键特点和优势包括: 零拷贝…

驱动框架——CMSIS第一部分 RTE驱动框架介绍

一、介绍CMISIS 什么是CMSIS(cortex microcontrol software interface standard一种软件标准接口),官网地址:https://arm-software.github.io/CMSIS_6/latest/General/index.html 包含的core、driver、RTOS、dsp、nn等部分&…

【C++】位运算:消失的两个数字

1.题目 2.算法思路 众所周知:相同的两个数字异或在一起等于0,而异或运算又遵循交换律和结合律。 所以这道题目的思路就有了: 1.可以将数组和1~N中的所有整数全部异或在一起,就可以得到缺失的两个数(a,b)…

使用Python创建和扫描二维码

二维码(Quick Response code)已成为在物理和数字领域之间架起桥梁的多功能工具。从分享联系信息和网站链接到促进支付和跟踪库存,二维码在各个行业中找到了应用。通过利用Python的功能,用户可以自动化生成个性化的二维码&#xff…

基于SpringBoot+Vue的财务管理系统(带1w+文档)

基于SpringBootVue的财务管理系统(带1w文档) 基于SpringBootVue的财务管理系统(带1w文档) 财务管理系统的开发运用java技术、springboot框架,MIS的总体思想,以及Mysql等技术的支持下共同完成了该系统的开发,实现了财务管理的信息化&#xff0…

C语言函数:编程世界的魔法钥匙(2)-学习笔记

引言 注:由于这部分内容比较抽象,而小编我又是一个刚刚进入编程世界的计算机小白,所以我的介绍可能会有点让人啼笑皆非。希望大家多多包涵!万分感谢!待到小编我学有所成,一定会把这块知识点重新介绍一遍&a…

VB利用API调用系统的通用颜色对话框

Option Explicit 在窗体上添加一个Command1按钮控件 Private Type ChooseColor lStructSize As Long hwndOwner As Long hInstance As Long rgbResult As Long lpCustColors As String Flags As Long lCustData As Long lpfnHook As Long lpTemplateName As String End Type 该…

pcie拓扑结构与层次结构

一 拓扑结构 PCIE 总线与总线共享式通讯方式的 PCI 不同,PCIE 由点到点的链路组成,并采用树形拓扑结构PCIE 拓扑结构体系由 CPU、根复合体(RootComplex,RC)、端点设备(Endpoint,EP)…

Python入门------pycharm加载虚拟环境

pycharm虚拟环境配置: 在按照前面的办法,配置好虚拟环境后,如果我们需要到虚拟环境开发,就需要给编译器配置虚拟环境 1.打开编译器,点击右下角的interpreter选项 2. 点击ADD Interpreter,添加虚拟环境 3. 因为我们使用的是原始…

【LeetCode】二叉树的最大深度

目录 一、题目二、解法完整代码 一、题目 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 示例 2&#x…

vue2学习笔记9 - 通过观察vue实例中的data,理解Vue中的数据代理

接着上一节,学一学vue中的数据代理。学vue这几天,最大的感受就是,名词众多,听得发懵。。不过,深入理解之后,其实说得都是一回事。 在Vue中,数据代理是指在实例化Vue对象时,将data对…

【C++高阶】精通AVL树:全面剖析与深度学习

目录 🚀 前言一: 🔥 AVL树的性质二: 🔥 AVL树节点的定义三: 🔥 AVL树的插入四: 🔥 AVL树的平衡调整(附动图) 五:🔥 AVL树的…