0. 设置MYSQL的隔离界别
# 查询数据库当前事务隔离级别
mysql> select @@global.transaction_isolation,@@transaction_isolation;
+--------------------------------+-------------------------+
| @@global.transaction_isolation | @@transaction_isolation |
+--------------------------------+-------------------------+
| REPEATABLE-READ | REPEATABLE-READ |
+--------------------------------+-------------------------+
1 row in set (0.00 sec)
# 设置数据库事务隔离级别为 读未提交
set global transaction isolation level READ UNCOMMITTED;
# 这一句话只针对当前客户端生效,因此两个客户端都需要
set session transaction isolation level READ UNCOMMITTED;
# 关闭事务的自动提交
set autocommit = 0;
1. 脏读
- 脏读概念:读取了其他事务未提交的数据,可能会造成严重的数据错误(理论上来说如果两个事务都正常提交就没问题)
事务A | 事务B |
---|---|
开启事务 | 开启事务 |
查询TOM用户的余额 = 100 | 查询JOHN余额 = 1100 |
JOHN给TOM转500 | |
查询TOM用户的余额 = 600 | |
TOM用户花300买了个耳机 | |
JOHN使用的支付转账设备异常,对支付状态校验失败,于是事务回滚 |
- 结果:数据异常,余额只有100 的TOM买了个300的耳机。
- 脏读其实在MYSQL中一般是不会出现的,即使我们手动将MYSQL的隔离级别调整至最低的读未提交,MyISAM的表不支持事务,InnoDB在事务对某行进行修改的时候会自动加上行锁,相当于TOM买耳机的这个操作会被阻塞等待,因此不用对脏读太过担心。
2. 不可重复读
- 概念:由于第一次读取和第二次读取同一行记录的时间段中有另一个事务 修改 了数据,导致前后两次读取的结果不一致
事务A | 事务B |
---|---|
开启事务 | |
查询TOM的余额 = 300 | |
TOM买了300的耳机 | |
开启事务 | |
领导给TOM发了500工资 | |
提交事务 | |
再次查询TOM的余额 = 500,欸我怎么原300买300还剩500 |
- 结果:在事务A看来,事务期间对于TOM这一行的数据重复读取的时候获得了不一样的结果,数据异常。
- 不可重复读可以通过设置数据库的隔离级别来进行规避,当数据库的事务隔离级别为
Repeatable Read
或Serializable
的时候,不可重复读情况消失,具体情况如下图所示:
3. 幻读
- 概念:事务执行中,有其他事务增删了某些字段,导致本事务没有得到消息,就感觉像出现幻觉一样。
事务A | 事务B |
---|---|
开启事务 | |
查询用户JACK的余额 = 500 | |
开启事务 | |
删除用户JACK | |
提交事务 | |
给JACK用户存200 | |
查询JACK用户余额,欸怎么查不到JACK用户了?? |
-
结果:在事务A进行的过程中,B事务对同一张表的其他数据进行了修改没通知A,导致A做了一些无用的幻觉操作。
-
MYSQL避免幻读的方式:(摘自【MYSQL如何解决幻读】)
(1) 隔离级别可重复读下,快照读 MVCC + 当前读 Next-Lock Key(只在可重复读隔离级别下生效)
(2)隔离级别:SERIALIZABLE,在这个隔离级别下,事务在读操作时,先加表级别的共享锁,直到事务结束才释放;事务在写操作时,先加表级别的排它锁,直到事务结束才释放。也就是说,串行化锁定了整张表,幻读不存在的