举例演示了MySQL的并发事务问题,以及四个事务隔离级别的区别,例子直接跳转标题3.
1.并发事务问题
(1)脏读 (Dirty Read)
-
问题描述 :一个事务读取了另一个未提交事务修改过的数据
-
示例:
-
事务A修改了某行数据但未提交
-
事务B读取了事务A修改后的数据
-
事务A回滚,事务B读取的数据就是无效的"脏数据"
-
(2)不可重复读 (Non-repeatable Read)
-
问题描述 :在同一事务内,多次读取同一数据返回不同结果(因为其他事务修改并提交了该数据)
-
示例:
-
事务A第一次读取某行数据
-
事务B修改了该行数据并提交
-
事务A再次读取同一行数据,发现数据已改变
-
(3) 幻读 (Phantom Read)
-
问题描述 :在同一事务内,多次查询返回不同的行集合(因为其他事务新增或删除了数据)
-
示例:
-
事务A查询表中符合某条件的行
-
事务B插入新的符合该条件的行并提交
-
事务A再次查询,发现多出了"幻影行"
-
2.事务隔离级别
我们可以通过给事务设置隔离级别来解决上述并发问题
MySQL 提供了四种隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
---|---|---|---|---|
READ UNCOMMITTED (读未提交) | 可能 | 可能 | 可能 | 最低隔离级别,性能最好但问题最多 |
READ COMMITTED (读已提交) | 可能 | 可能 | 不可能 | 只读取已提交的数据,Oracle默认级别 |
REPEATABLE READ (可重复读) | 可能 | 不可能 | 不可能 | MySQL默认级别,确保同一事务内读取一致 |
SERIALIZABLE (串行化) | 不可能 | 不可能 | 不可能 | 最高隔离级别,性能最差但最安全 |
事务隔离级别的相关操作
查看当前隔离级别
sql
SELECT @@transaction_isolation;
-- 或
SHOW VARIABLES LIKE 'transaction_isolation';
设置隔离级别
可以设置全局级别或会话级别:
sql
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置下一个事务的隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
3.操作演示
(1).准备数据
首先,准备一个简单的数据表做演示
这里是一个简单的账户表
sql
create table account
(
user_name varchar(20) not null,
money double null,
id int auto_increment
primary key
);
# 添加数据
insert into account values ('jack',8000,1),('mary',8000,2);
因为要演示并发问题,所以,我们直接使用cmd打开两个窗口演示

找到 上面表所在的数据库
(2).脏读
我们先演示,脏读:一个事务读取了另一个未提交事务修改过的数据
先设置左窗口的事务隔离级别为 read uncommitted
sql
set session transaction isolation level read uncommitted ;

然后在左窗开启一个事务,查询一下 account表的数据:

接着在右窗开启一个事务,修改 一下 account表的数据:

注意,这时我们并没有commit 提交右窗修改的数据
但是,我们再在左窗查询 数据,却发现查询到的数据已经修改了

这就体现了脏读问题,右窗的事务,访问到了左窗事务还未提交的数据!
演示完成,别忘了结束两边的事务,再进行后续操作

我们把右窗事务隔离级别修改为 readcommited 就可以避免脏读问题
就是下面 不可重复读的操作演示,不再赘述
右窗:

左窗:

(3.1).不可重复读
右窗事务隔离级别设置为 readcommited
开启一个事务,查询一下数据

在左窗开启一个事务,修改表中数据

左窗还未提交zai再次在右窗查询,发现数据没有变化,也就是解决了 脏读问题

左窗提交数据后 ,再次在右窗查询,发现数据变化了,

在右窗的一个事务过程中,出现了多次读取同一数据但返回不同结果的现象,这就是不可重复读
(3.2)解决不可重复读
设置右窗事务隔离级别为REPEATABLE READ (可重复读), 开启一个事务,查询当前表数据

左窗还是开启一个事务,修改表中数据,这次我们直接提交!

右窗再次查询表数据,可以看到,这次左窗事务虽然已经提交了修改,但是右窗查询数据还是没变。 这样一个事务里就不会出现多次读取同一数据但返回不同结果的现象,这就解决了上面 不可重复的的问题

我们结束右窗事务后,再次查询,则看到了最新数据

(4.1).幻读
右窗不改变隔离级别,依旧为 repeatable read
开启新事务,查询id为3的信息,当前没有id为3的人,当然查询不到

此时,左窗开启一个事务,插入id= 3的一条数据 ,并直接提交

由于,并发执行,之前右窗没有查询到id= 3的数据,于是也准备插入一条id= 3的数据,但是由于左窗已经插入完成,所以无法插入。如下图

右窗再次查询id为3的信息,但是还是查询不到

像这样右窗这样,查询时查询不到,插入时又认为数据已经存在不让插入 的现象 就叫 幻读
(4.2).解决幻读
设置右窗事务隔离级别为SERIALIZABLE (串行化),开启事务,查询id= 4的数据,没有

左窗开启事务,插入id= 4的一条数据,但是按回车后,却没有运行,这就是因为 右窗事务隔离级别为SERIALIZABLE (串行化)
- 所有事务只能串行执行,解决了所有并发问题

只能在右窗执行完当前事务的全部操作后,左窗事务才能继续!

左窗:

=================结束==================
文章创作不易
求点赞!
求评论!
求收藏!