Mysql 事务并发访问导致的问题--更新丢失、脏读、不可重复读、幻读。
Mysql 数据库事务的四大
特性--原子性、一致性、隔离性、持久性。而锁
是数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性
和一致性
。如果在多用户
并发情况下,他们的事务同时
对相同
的数据进行操作,是会导致更新丢失、脏读、不可重复读、幻读的问题的。
前言
Mysql 事务并发访问导致的问题--更新丢失、脏读、不可重复读、幻读。
Mysql 数据库事务的四大
特性--原子性、一致性、隔离性、持久性。而锁
是数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性
和一致性
。如果在多用户
并发情况下,他们的事务同时
对相同
的数据进行操作,是会导致更新丢失、脏读、不可重复读、幻读的问题的。
更新丢失
- Mysql 所有事务隔离级别在数据库层面上均可避免
更新丢失
问题 - 当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。
- 每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
- 如果在
A事务
完成之后,B事务
才能进行更改,则可以避免该问题。
更新丢失 Demo
取款事务 | 存款事务 |
---|---|
开始事务 | 开始事务 |
查询转账余额为 100 元 | |
查询转账余额为 100 元 | |
存入 20 元,余额变为 120 元 | |
提交事务 | |
取出 10 元,余额改为 90 元 | |
回滚事务,余额恢复为 100 元 | 更新丢失 |
脏读
- READ-COMMITTED 事务隔离级别以上可避免--
脏读
问题 - 脏读就是指当一个事务正在访问数据,并且对数据进行了
修改
,而这种修改还没有提交
到数据库中,这时,另外
一个事务也访问这个数据,然后使用
了这个数据。因为这个数据是还没有提交
的数据,那么另外一个事务读到的这个数据是脏数据
,依据脏数据所做的操作可能是不正确的。 - 如果在
B事务
确定最终更改前,A事务
或者其它事务都不能读取
更改的文档,则可以避免该问题。
脏读 Demo
数据库默认的隔离级别是 REPEATABLE-READ。为了演示脏读效果,需要先将数据库隔离级别设置成 READ-UNCOMMITTED。
select @@tx_isolation;
set session transaction isolation level read uncommitted;
A 事务 | B 事务 |
---|---|
开始事务 | 开始事务 |
1 账户余额 1000 元,转出 100 元 | |
查询 1 账户剩 900 元 | |
查询 1 账户剩 900 元 | |
没有进行事务提交,而是事务回滚,余额恢复 1000 元 | |
不知道 A 事务进行回滚,对 1 账户存入 200 元 | |
提交事务 | |
再次查询 1 账户余额 1100 |
不可重复读
- REPEATABLE_READ 事务隔离级别以上可避免--不可重复读问题
- 不可重复读是指在一个事务内,
多次
读同一
数据。在这个事务还没有结束
时,另外一个事务也访问该同一数据
。那么,在第一个事务中的两次
读数据之间
,由于第二个事务的修改
,那么第一个事务两次读到的的数据可能是不一样
的。 - 如果在
B事务
确定最终更改前
,A事务
或者其它事务都不能读取
更改的文档,则可以避免该问题。
不可重复读 Demo
A 事务 | B 事务 |
---|---|
开始事务 | 开始事务 |
查询 1 账户余额 1300 元 | |
对 1 账户存入 300 元 | |
查询 1 账户剩 1600 元 | |
进行事务提交 | |
查询 1 账户余额 1600 元 |
- 数据库默认的隔离级别是 REPEATABLE-READ。若将数据库隔离级别设置成 REPEATABLE_READ,则
A事务第二次
查询余额还是 1300 元,不会是 1600 元。
select @@tx_isolation;
set session transaction isolation level repeatable read;
幻读
- SERIALIZABLE 事务隔离级别可避免--
幻读
问题 - 幻读与不可重复读看似
类似
,不可重复读侧重于对同一数据的修改
,幻读侧重于新增
或删除
。 - 幻觉读是指当事务不是
独立
执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行
。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入
一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改
的数据行,就好象发生了幻觉一样。
幻读 Demo
A 事务 | B 事务 |
---|---|
开始事务 | 开始事务 |
查询全部账号余额,当前有 3 个账号 | |
新增 4 账号及其余额 | |
进行事务提交 | |
更新所有的账户余额 | |
出现有四个账号幻读的现象 |
- 数据库默认的隔离级别是 REPEATABLE-READ。若将数据库隔离级别设置成 SERIALIZABLE,则
B事务
新增账号 4 需要等待A事务
操作完才能进行。
select @@tx_isolation;
set session transaction isolation level serializable;
总结
事务并发访问引起的问题以及如何避免
事务隔离级别 | 更新丢失 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
未提交读 | 避免 | 发生 | 发生 | 发生 |
已提交读 | 避免 | 避免 | 发生 | 发生 |
可重复读 | 避免 | 避免 | 避免 | 发生 |
串行化 | 避免 | 避免 | 避免 | 避免 |
- 事务级别越高安全性越高,串行化执行越严重,这样就可以降低数据库的并发度,但性能要求也会越高。