跳至主要內容

事务并发存在的问题(脏读、不可重复读和幻读)

张威大约 4 分钟mysql事务

事务并发存在的问题(脏读、不可重复读和幻读)

事务处理如果并发执行事务时通常会发生以下问题:

  • 脏读(Dirty Read):

    • 例如当事务A和事务B并发执行时,当事务A更新后,事务B查询读取到A尚未提交的数据,此时如果事务A rollback了,那事务B读到的数据就不是数据库所存放的数据了,而是无效的脏数据(脏读必须杜绝,因为事务没有commit而不可重复读和幻读不一定出问题,因为事务已经commit
  • 不可重复读(NonRepeatable Read):一个事务的操作导致另一个事务

    • 例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A 更改事务B查询到的数据,此时事务B再次去读该数据,发现前后两次读的数据不一样(事务B读取了事务A已commit的数据)

  • 幻读(Phantom Read):一个事务的操作导致另一个事务前后

    • 例如 当事务A和事务B并发执行时,当事务B查询读取数据后,事务A或者了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了(事务B读取了事务A新增加的数据或者读到事务A删除的数据)

在有些场景下,不可重复读和幻读一定程度上是可以允许的,不一定非要杜绝(通过设置不同的隔离级别解决),由应用场景需求决定

  • 脏读举例:张三的账户还剩100块,他同时发起两个事务,事务A转账50,事务B购买价格为80的水杯,事务A现对张三的余额减50,然后给另一个人余额加50。由于没有隔离控制,事务B购买水杯前先读取余额,发现只有50,无法购买茶杯,于是茶杯购买失败。此时事务A执行异常,将张三的余额回滚为事务执行前的状态,余额为100。当前场景中事务B读到了事务A还没有commit的数据50,发生了脏读,任何业务场景下都必须杜绝
  • 不可重复读举例:首先事务B查询余额,发现为100。事务A完成转账50,并且commit,事务B再次查询余额,发现变成了50,在某些业务场景下是可以允许的,不一定非要杜绝
  • 幻读举例:事务B查询年龄为20的人,发现有5个。事务A插入或删除了年龄为20的记录,并且commit,事务B再次查询年龄为20的人,发现已经不是5个人了,幻读也是在某些业务场景下是可以允许的,不一定非要杜绝

事务相关命令

一般我们业务上如果要考虑到事务处理,我们需要设置为手动提交方式,如果一个事务包含多个SQL,若是自动提交方式,一句SQL执行完就自动提交了,后面的SQL万一执行失败就无法正常rollback,无法保证事务的原子特性

BEGIN:开启一个事务 COMMIT:提交一个事务 ROLLBACK:回滚一个事务到初始的位置 SAVEPOINT point1:设置一个名字为point1的保存点 ROLLBACK TO point1:事务只回滚到保存点point1,而不是回滚到初始状态SET TRANSACTION_ISOLATION=‘REPEATABLE-READ’设置事务的隔离级别SELECT @@TRANSACTION_ISOLATION:查询事务的隔离级别