Innodb 中的事务隔离与常见问题

在日常开发过程中,事务是经常被使用的,然而大多数开发者并不了解事务隔离级别是什么,也不知道不同的隔离级别下使用事务时可能会发生的一些问题。

SQL标准定义的四个隔离级别为:

  • READ UNCOMMITTED (读未提交)
  • READ COMMITTED (读已提交)
  • REPEATABLE READ (可重复读)
  • SERIALIZABLE (串行)

 

user表

id name money
1 小李 1000

 

READ UNCOMMITTED

事务A修改了一条数据,但未提交,事务B可以读到被事务A修改的数据,存在脏读问题,同时也存在不可重复读、幻读等问题(不一一写实际例子)。

事务A 事务B
start TRANSACTION start TRANSACTION
update user set name = '小明' where id = 1 -
- select name from user where id = 1

 

READ COMMITTED

解决了脏读的情况,但是当事务A提交之后,事务B在一次事务内读取同一条数据会有不同的结果,存在不可重复读的问题。

事务A 事务B
start TRANSACTION start TRANSACTION
update user set name = '小明' where id = 1 -
- select name from user where id = 1
COMMIT -
- select name from user where id = 1

 

REPEATABLE READ

Mysql默认的事务隔离级别,解决了脏读、不可重复读问题,同时用
next_key_lock 解决了幻读的问题,但是有出现更新覆盖的可能。

事务A 事务B
start TRANSACTION start TRANSACTION
select money into @money from user where id = 1 select money into @money from user where id = 1
update user set money = @money-100 where id = 1 -
COMMIT -
- update user set money = @money-50 where id = 1
- COMMIT

对应实际的业务场景,就是用户分别消费了100元和50元,2个事务都成功了,但是实际上最终账户只减少了50元。

解决这种情况,需要在读取数据的时候加上排他锁(X锁),使2个事务变成串行操作。如下:

事务A 事务B
start TRANSACTION start TRANSACTION
select money into @money from user where id = 1 for update select money into @money from user where id = 1 for update
update user set money = @money-100 where id = 1 -
COMMIT -
- update user set money = @money-50 where id = 1
- COMMIT

 

SERIALIZABLE

串行模式下,可以解决事务相互依赖导致的死锁问题,以及REPEATABLE READ下可能出现的更新被覆盖问题,但是由于不能同时执行2个或以上的事务,会使性能下降,所以大部分情况下不会选择此模式。

你可能感兴趣的:(Innodb 中的事务隔离与常见问题)