Mysql在并发时,多个事务之间会存在一些问题,脏读、不可重复读、幻读。
脏读
脏读是指事务A读取到了事务B没有提交的数据,但是事务B回滚了,那么此时事务A读取的数据是数据库不存在的,这个时候的数据则称之为脏数据,也就是脏读。
在上面的例子的流程中,
- 事务B开启事务
- 事务B执行了一条语句 update user set name='测试' where id=2
- 事务A读物id=2
- 事务B回滚
事务B最终获取到的id=2 user数据不是数据库中存在的数据。
不可重复读
不可重复读的意思是事务A在一个事务内 第一次读取的数据和第二次读取的数据不一致
在上面的例子流程中:
- 事务A开启事务
- 事务A第一次读取id=2
- 事务B对id=2进行的update操作并提交
- 事务A第二次读取id=2
这样事务A两次查找id=2的结果不一样。
幻读
幻读是指数据库在可以重复读的情况下发生的问题。
幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。
更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。
上图中大致意思是A事务在插入前先判断是否存在id=2的数据
- 事务A开启事务
- 事务A第一次查询id=2
- 事务B新增id=2的数据并提交
- 事务A第二次读取id=2(事务满足可重复读,此时数据库不存在id=2的数据)
- 事务A也新增id=2的数据
- 事务A提交事务报错
在它两次查询数据库中没有id=2的数据的时候进行新增发现报错,感觉跟幻觉一样。
事务的隔离级别
MySQL 里有四个隔离级别:Read uncommttied(可以读取未提交数据)、Read committed(可以读取已提交数据)、Repeatable read(可重复读)、Serializable(可串行化)。
其中最后一个的隔离级别最高,第一个隔离级别最低,隔离级别越高系统付出的代价越大,并发的问题就越小。
在 InnoDB 中,默认为 Repeatable read级别,可重复读,InnoDB 中使用一种被称为 next-key locking 的策略来避免幻读(phantom)现象的产生。
使用 select @@tx_isolation; 可以查看 MySQL 默认的事务隔离级别。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED(可以读取未提交数据) | √ | √ | √ |
READ-COMMITTED(可以读取已提交数据) | × | √ | √ |
REPEATABLE-READ(可以重复读) | × | × | √ |
SERIALIZABLE(可串行化) | × | × | × |