什么是脏读不可重复读幻读?一文带你快速搞定MySQL事务隔离级别
Posted c.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是脏读不可重复读幻读?一文带你快速搞定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.- The
innodb_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)。
下面就来演示一下什么是脏读,这里涉及到事务的隔离级别,后面会讲,这里可以先不用太关心。
首先我们创建一个表,还有插入一条数据。
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
https://github.com/hongwen1993/all/blob/master/database/Isolation.md
https://dev.mysql.com/doc/refman/5.6/en/innodb-next-key-locking.html
以上是关于什么是脏读不可重复读幻读?一文带你快速搞定MySQL事务隔离级别的主要内容,如果未能解决你的问题,请参考以下文章