并发事务演示及隔离级别

引言

在MySQL数据库中,事务是一组不可分割的操作单元,这些操作要么全部成功,要么全部失败。事务的四大特性,通常被称为ACID特性,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。以下是对这些特性以及并发事务问题的详细介绍:

一、事务的四大特性(ACID)

  1. 原子性(Atomicity)

    • 事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
    • 实现原子性的关键在于回滚日志(undo log),它记录了事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
  2. 一致性(Consistency)

    • 事务必须使数据库从一个一致性状态变换到另一个一致性状态。
    • 一致性是事务追求的最终目标,它的实现既需要数据库层面的保障,也需要应用层面的保障。
  3. 隔离性(Isolation)

    • 当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
    • MySQL通过锁机制(如行级锁)和多版本并发控制(MVCC)来实现隔离性。
  4. 持久性(Durability)

    • 一旦事务提交,则其所做的修改就会永久保存到数据库中,即使系统崩溃,修改的数据也不会丢失。
    • 持久性是通过重做日志(redo log)来实现的,它保证了数据在事务提交后不会因宕机等原因而丢失。

二、并发事务问题

  1. 脏读(Dirty Read)

    • 一个事务读取了另一个未提交事务的数据。
    • 例如,事务T1修改了一个数据,事务T2随后读取了这个数据。如果T1撤销了这次修改,那么T2读取的数据就是脏数据。
    • 脏读问题在读未提交(Read Uncommitted)隔离级别下可能发生。
  2. 不可重复读(Non-repeatable Read)

    • 在一个事务范围内,多次查询却返回了不同的数据值。
    • 这通常是因为在查询间隔内,被另一个事务修改并提交了数据。
    • 不可重复读问题在读已提交(Read Committed)和读未提交(Read Uncommitted)隔离级别下可能发生。下面事务隔离演示会介绍
  3. 幻读(Phantom Read)

    • 一个事务读取到的记录在其后续的读取请求中发生变化,造成原本能够查询到的数据在后续查询中消失或改变的现象。
    • 例如,在一个事务中执行查询后,另一个事务插入了新记录,从而导致第一个事务再次执行同样的查询时会看到一个“幻影”记录。
    • 幻读问题在可重复读(Repeatable Read)隔离级别下可能发生,但在串行化(Serializable)隔离级别下可以避免。

三、事务的隔离级别

MySQL数据库提供了四种事务隔离级别,从低到高分别为:

  1. 读未提交(Read Uncommitted)

    • 一个事务可以读取另一个未提交事务的数据。
    • 最低级别,任何情况都无法保证数据的一致性。
  2. 读已提交(Read Committed)

    • 一个事务只能读取已经提交的事务所做的修改。
    • 可避免脏读的发生。
  3. 可重复读(Repeatable Read)

    • 保证在同一个事务中多次读取同样数据的结果是一样的。
    • 可避免脏读和不可重复读的发生。
    • MySQL的默认隔离级别。
  4. 串行化(Serializable)

    • 事务串行化顺序执行,可避免脏读、不可重复读与幻读的发生。
    • 级别最高,执行效率最低。

 这些隔离级别可以解决哪些事务问题:

查看事务隔离级别:

# 查看隔离级别
select @@transaction_isolation; 

设置事务隔离级别: 

# 设置隔离级别为读未提交
set session transaction isolation LEVEL read uncommitted ; 

# 设置隔离级别为读已提交
set session transaction isolation level   read committed ;
 
# 设置隔离级别为可重复读
set session transaction isolation level   repeatable read ; 

# 设置隔离级别为串行化
set session transaction isolation level  SERIALIZABLE ;

四.事务的隔离演示:

开启2个客服端使用同一个表来模拟:启动

开启演示: 

 1) 来演示隔离级别 读未提交:

(未解决 : 脏读 可重复读 幻读)

Mysql默认隔离级别是可重复读,因此

先设置隔离级别为 读未提交(是最低级的,并发事务问题都不能解决,这么就试试脏读)

脏读:一个事务读取了另一个未提交事务的数据。

# 设置隔离级别为读未提交
set session transaction isolation LEVEL read uncommitted ; 

然后开启事务,我们到后面自己手动提交事务

 

 左边我们先查询表的数据,然后在右边我们开启事务,修改了数据,但是并没有提交,然后左边我们再次查询会发现与开始的数据不一致了,导致我们2次查询数据不一样,并且这是在同一个事务下,我还未提交,数据却不一致,这就是脏读,一个事务读取了另一个还未提交的事务的数据。

2) 来演示隔离级别 读已提交:

(解决:脏读  为解决:可重复读 幻读)

再次之前我们恢复好原来数据

同理,我们设置隔离级别

# 设置隔离级别为读已提交
set session transaction isolation level   read committed ;

演示:

然后右边我们提交:

 

 这就虽然解决了脏读,但是没有解决可重复读,每次查询的数据是不一致的!!!

其实这和他生成的readview的时机不同有关,他是我们快照读(就是平常的select (不加锁))SQL执行时MVCC提取数据的依据。他每一次快照读都会生成一个新的ReadView,然后根据新的规则去MVCC的版本控制链中去寻找对应的undo log 日志的,他就是用来记录我们inset update delete 这些操作后记录的日志,用来回滚的。

简单点概括,他会去查询他最近一次提交的数据的。所有我们这里在右边没有提交前,尽管他怎么修改,也是查询的以前数据,没有改动,但是当右边提交后,我们发现数据就变更了,就查询的是这次提交后改动的数据了

3)演示隔离级别 可重复读

(解决:脏读 可重复读 未解决:幻读)

同理:我们恢复数据 ,设置隔离级别

# 设置隔离级别为可重复读
set session transaction isolation level   repeatable read ; 

演示:

解决了脏读 

解决不可重复读,这里我们可以看到,右边经管提交了,但是左边数据查询依然是一致的,这里其实和上面读已提交隔离级别一样的,只是在这种RR(可重复读)隔离级别下他其实只会在第一次执行快照读的时候会生成一个ReadView,后面在执行相同就会复用上的ReadView,规则不变,你每次查询的都是一样,可以看看MVCC的原理就明白了。

但是:并未解决幻读:

比如我们这里左边开启事务,查询id为3的没有,然后右边开启事务插入一条id为3的信息,在去左边插入,发现会提示主键重复了,但是查询会发现又没有,这不就是见鬼了吗??

4)演示隔离级别 串行化

(解决:脏读 不可重复读 幻读)

设置隔离级别:

# 设置隔离级别为串行化
set session transaction isolation level  SERIALIZABLE ;

演示;

 一段时间后可以发现右边超时了!!!

 Lock wait timeout exceeded; try restarting transaction 

即使普通的select查询也会上共享锁,进行当前读,因此右边事务进行insert操作时想要获取排它锁需要一直等待,最终超时

 然后左边提交后:

右边也在执行会发现id为4的已经存在了!!! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何政@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值