什么是脏读、不可重复读、幻读?一文带你快速搞定MySQL事务隔离级别

什么是脏读、不可重复读、幻读?一文带你快速搞定MySQL事务隔离级别

事务

既然要讲事务隔离级别,那我们肯定要来先说一下事务这个概念。

那什么是事务呢?

我们可以看到oracle的解释是:

A transaction is a logical, atomic unit of work that contains one or more SQL statements.

参考:https://docs.oracle.com/database/121/CNCPT/transact.htm#CNCPT117

我们一提到事务就会想到ACID这四个特性,但事务的定义本身并不具备这四个特性。事务只是一个改变,是一些的操作集合;用专业的术语去解释,就是一个程序的执行单元,只是保证了这个执行单元中的SQL要么都执行要么都撤销;我们需要通过某些手段,尽可能让这个执行单元满足这四个特性,那么,我们就可以称它是一个事务,或者说是一个正确的,完美的事务。(这里先举个栗子,比如在读未提交的隔离级别下,它不满足隔离性,但是它确实是一个事务,最完美的事务应该就是串行化隔离级别的事务了,这是对事务的隔离特性的完美实现,也是事务隔离特性的要求)

ACID四个特性

原子性(Atomicity)

All tasks of a transaction are performed or none of them are. There are no partial transactions. For example, if a transaction starts updating 100 rows, but the system fails after 20 updates, then the database rolls back the changes to these 20 rows.

满足原子操作单元,对数据的操作,要么全部执行,要么全部不执行。

ACID 模型的原子性方面主要涉及InnoDB 事务。

The atomicity aspect of the ACID model mainly involves InnoDB transactions. Related MySQL features include:

  • The autocommit setting.
  • The COMMIT statement.
  • The ROLLBACK statement.

一致性(Consistency)

The transaction takes the database from one consistent state to another consistent state. For example, in a banking transaction that debits a savings account and credits a checking account, a failure must not cause the database to credit only one account, which would lead to inconsistent data.

事务应确保数据库的状态从一个一致状态转变为另一个一致状态。即事务开始和完成时,数据都必须保持一致。

ACID 模型的一致性方面主要涉及InnoDB保护数据免受崩溃的内部处理

The consistency aspect of the ACID model mainly involves internal InnoDB processing to protect data from crashes. Related MySQL features include:

  • The InnoDB doublewrite buffer.
  • InnoDB crash recovery.

隔离性(Isolation)

The effect of a transaction is not visible to other transactions until the transaction is committed. For example, one user updating the hr.employees table does not see the uncommitted changes to employees made concurrently by another user. Thus, it appears to users as if transactions are executing serially.

多个事务并发执行时,一个事务的执行不应影响其他事务的执行。事务之间相互独立,中间状态对外部不可见。

ACID 模型 的隔离方面主要涉及InnoDB 事务的隔离级别。

The isolation aspect of the ACID model mainly involves InnoDB transactions, in particular the isolation level that applies to each transaction. Related MySQL features include:

  • The autocommit setting.
  • Transaction isolation levels and the SET TRANSACTION statement

持久性(Durability)

Changes made by committed transactions are permanent. After a transaction completes, the database ensures through its recovery mechanisms that changes from the transaction are not lost.

已被提交的事务对数据库的修改应该永久保存在数据库中。数据的修改是永久性的,即使系统出现任何故障都能够保持。

The durability aspect of the ACID model involves MySQL software features interacting with your particular hardware configuration. Because of the many possibilities depending on the capabilities of your CPU, network, and storage devices, this aspect is the most complicated to provide concrete guidelines for. (And those guidelines might take the form of “buy new hardware”.) Related MySQL features include:

  • The InnoDB doublewrite buffer.
  • The innodb_flush_log_at_trx_commit variable.
  • The sync_binlog variable.
  • Theinnodb_file_per_table variable.
  • The write buffer in a storage device, such as a disk drive, SSD, or RAID array.
  • A battery-backed cache in a storage device.
  • The operating system used to run MySQL, in particular its support for the fsync() system call.
  • An uninterruptible power supply (UPS) protecting the electrical power to all computer servers and storage devices that run MySQL servers and store MySQL data.
  • Your backup strategy, such as frequency and types of backups, and backup retention periods.
  • For distributed or hosted data applications, the particular characteristics of the data centers where the hardware for the MySQL servers is located, and network connections between the data centers.

可以参考一下MySQL官方文档:https://dev.mysql.com/doc/refman/8.0/en/mysql-acid.html

按照严格的标准,只有同时满足ACID特性才是事务;但是在各大数据库厂商的实现中,真正满足ACID的事务少之又少。例如MySQL的NDB Cluster事务不满足持久性和隔离性;InnoDB默认事务隔离级别是可重复读,不满足隔离性;Oracle默认的事务隔离级别为READ COMMITTED,不满足隔离性……因此与其说ACID是事务必须满足的条件,不如说它们是衡量事务的四个维度。

所以我在开头也说了,事务并不是满足这四个条件才叫做事务,这一点希望大家也能够明白。

事务隔离级别

前面提到了可串行化是最完美的隔离级别,但是串行的并发能力是最差的,
但是如果提供并发能力(破坏隔离性)往往会破坏数据的一致性,因此需要在并发量和一致性之间找到一个平衡。最后大佬们根据对数据一致性破坏的程度将事务的隔离性区分了4个级别,一致性破坏造成的影响区分为: 脏读、幻读、不可重复读,这就是ANSI SQL-92标准

下面就讲一下提高并发能力,在多个事务并发执行的时候会引发哪些问题?

多个事务并发执行时引发的问题

一般情况下,多个单元操作(事务,这里的事务,并不是完美的事务,文章最开始提到的)并发执行,会出现这么几个问题。

脏读

脏读:A事务还未提交,B事务就读到了A操作的结果。

1、在事务A执行过程中,事务A对数据资源进行了修改,事务B读取了事务A修改后的数据。

2、由于某些原因,事务A并没有完成提交,发生了RollBack操作,则事务B读取的数据就是脏数据。

这种读取到另一个事务未提交的数据的现象就是脏读(Dirty Read)。

参考:https://www.zhihu.com/question/458275373

在这里插入图片描述

下面就来演示一下什么是脏读,这里涉及到事务的隔离级别,后面会讲,这里可以先不用太关心。

首先我们创建一个表,还有插入一条数据。

create table test_tb(
     id varchar(60) primary key  ,
     name varchar(30)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into test_tb value (1,'Kevin');

然后我们打开一个新的窗口, 我们查看当前窗口的事务隔离级别

SELECT @@GLOBAL.tx_isolation, @@tx_isolation; -- MYSQL8之前

SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation; -- MYSQL8之后

可以看到我们的隔离级别是REPEATABLE-READ

在这里插入图片描述

然后我们把当前的窗口,也就是当前的session的隔离级别改成READ-UNCOMMITTED

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 修改当前session的隔离级别为读未提交

然后我们再看看当前session的事务隔离级别

在这里插入图片描述

现在我们的准备工作做好了,我们再打开一个新的窗口,这个窗口的隔离级别是REPEATABLE-READ

首先我们在REPEATABLE-READ这个窗口开启一个事务,然后执行一个update操作,但是我们不提交数据。

-- REPEATABLE-READ
start transaction ;
update test_tb set name = 'KEVIN' where id = 1;

正常来说,如果我们再打开一个REPEATABLE-READ的新窗口,这个时候是读不到未提交的update后的数据。只有执行update操作的那个事务里面才能读取到。

但是在READ-UNCOMMITTED级别下,是能够读取到未提交的数据。

在这里插入图片描述

所以这个时候就出现了脏读,读取到了未提交的数据,这个很显然就是破坏了ACID四个特性中的隔离性了。

不可重复读

不可重复读:A事务在本次事务中,对自己未操作过数据,进行多次读取,结果数据出现不一致(重点是update)

事务B读取了两次数据资源,在这两次读取的过程中事务A修改了数据,导致事务B在这两次读取出来的数据不一致。

这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读(Nonrepeatable Read)。

在这里插入图片描述

现在我们演示一下不可重复读的情况。

首先我们还是打开一个新的窗口,我们把当前的session的隔离级别改为读已提交,在读已提交的隔离级别下是不会出现脏读的,可以参考上面脏读的例子去试一试,这里就不展示了。但是在读已提交的隔离级别下,还是会出现不可重复读的情况。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED ; -- 修改当前session的隔离级别为读已提交

然后我们开启事务并且查看数据。

start transaction ;
select * from test_tb; -- READ-COMMITTED

在这里插入图片描述

然后我们打开另一个新的窗口,这个窗口依然还是REPEATABLE-READ,然后我们修改数据并提交,这里跟脏读的区别是,我们开启了事务并且进行了提交。

-- REPEATABLE-READ
start transaction ;
update test_tb set name = 'KEVIN' where id = 1;
commit;

然后我们回到我们之前READ-COMMITTED的窗口,在事务中继续查看数据,然后我们发现我们两次读取的数据不一样了,第一次读取到的是Kevin,第二次读取到的是KEVIN

在这里插入图片描述

所以这种在同一个事务中,前后两次读取的数据不一致的现象就是不可重复读。

幻读

幻读:A事务在本次事务中,前后两次读取的数据记录条数不一致(主要是新增和删减)

可以看看MySQL官方的定义:

The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.

参考:Phantom Rows

在这里插入图片描述

我们来演示一下幻读的情况,首先我们还是打开窗口,设置当前的隔离级别为REPEATABLE-READ

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ; -- 修改当前session的隔离级别为可重复读

在可重复读的隔离级别下是不会出现不可重复读的情况,可以参照上面不可重复读的例子操作一下,结果是读取的还是原来的数据,这里就不演示了,我们来演示幻读的情况。

首先我们DB里面一样只有一条数据。

在这里插入图片描述

我们在REPEATABLE-READ的窗口中开启一个事务,然后查看数据,确实是一条数据

start transaction ;
select * from test_tb; -- REPEATABLE-READ
select count(*) from test_tb; -- REPEATABLE-READ

然后我们开启一个新的窗口,默认也是REPEATABLE-READ的隔离级别。

然后我们开启事务并插入一条数据

-- REPEATABLE-READ
start transaction ;
insert into test_tb value (2,'Kevin');
select count(*) from test_tb;
commit;

可以看到现在是两条数据了。

然后我们回到之前的窗口,也就是最开始还没提交事务的窗口。这个时候我们查看一下数量。

在这里插入图片描述

可以发现我们的数据条数还是一条,但是我们这个时候执行一个update语句。

update test_tb set name = 'KEVIN' where name = 'Kevin';
start transaction ;
select * from test_tb; -- REPEATABLE READ
select count(*) from test_tb; -- REPEATABLE READ
update test_tb set name = 'KEVIN' where name = 'Kevin';

在这里插入图片描述

可以发现执行的时候却是影响了两条记录,就好像出现了幻觉一样,所以这个现象就是幻读

MySQL四个事务隔离级别

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)-
可重复读(repeatable-read)--
串行化(serializable)---

这里需要注意的一个点是,在SQL标准中,RR是无法避免幻读问题的,但是InnoDB实现的RR避免了一些幻读问题。(使用MVCC和next-key lock)。RR虽然避免了部分幻读问题,但是毕竟不是Serializable,不能保证完全的隔离。

关于这部分的问题,到时候我会来详细的讲一讲MVCC还有InnoDB的RR是如何解决幻读问题的,给自己挖一个坑先。

参考

https://docs.oracle.com/database/121/CNCPT/transact.htm#CNCPT117

什么是脏读、不可重复读、幻读?

搞懂MySQL对事务的破坏与补救措施,扫清对事务的误区

https://github.com/hongwen1993/all/blob/master/database/Isolation.md

【MySQL】事务与隔离级别(99%的人存在误区)

直接写SQL给你演示什么是脏读 幻读 可重复读,思路清晰

四个案例看懂 MySQL 事务隔离级别

ANSI SQL-92标准

面试官一上来就问Mysql:幻读到底是什么?

mysql幻读(幽灵问题 phantom problem)

https://dev.mysql.com/doc/refman/5.6/en/innodb-next-key-locking.html

MySQL逻辑架构及性能优化原理

深入学习MySQL事务:ACID特性的实现原理

mySQL数据库间隙锁(mysql是如何解决幻读的)

InnoDB Multi-Versioning

InnoDB Locking

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
脏读是指一个事务可以读取到另一个事务未提交的数据。不可重复读是指在一个事务内多次读取同一数据时,由于其他事务的修改导致每次读取的结果不一致。幻读是指在一个事务内多次执行相同的查询,由于其他事务的插入或删除导致每次查询的结果不一致。 在MySQL中,事务隔离级别对应的脏读不可重复读幻读的情况如下: - 读未提交(READ UNCOMMITTED)级别下存在脏读不可重复读幻读的问题。 - 读已提交(READ COMMITTED)级别下不存在脏读的问题,但仍可能存在不可重复读幻读的问题。 - 可重复读(REPEATABLE READ)级别下不存在脏读不可重复读的问题,但仍可能存在幻读的问题。 - 串行化(SERIALIZABLE)级别下不存在脏读不可重复读幻读的问题。 因此,在MySQL中,脏读不可重复读幻读都是与事务隔离级别密切相关的读一致性问题。根据需求和业务场景,可以选择合适的事务隔离级别来解决这些问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Mysql-详解脏读不可重复读幻读](https://blog.csdn.net/ahuangqingfeng/article/details/124407846)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一文搞懂MySQL脏读,幻读不可重复读](https://blog.csdn.net/liuqinhou/article/details/126360614)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值