先来回顾一下事务并发可能存在的三大问题:
脏读(Dirty Read)–不能接受
一个事务读取了另一个事务未提交的数据。例如当事务A和事务B并发执行时,当事务A更新后,事务B查询读取到A尚未提交的数据,此时事务A回滚,则事务B读到的数据就是无效的脏数据。(事务B读取了事务A尚未提交的数据)
不可重复读(NonRepeatable Read)—具体业务具体分析
一个事务的操作导致另一个事务前后两次读取到不同的数据。例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A更新操作更改事务B查询到的数据,此时事务B再次去读该数据,发现前后两次读的数据不一样。(事务B读取了事务A已提交的数据)
虚读(Phantom Read)幻读—具体业务具体分析
一个事务的操作导致另一个事务前后两次查询的结果数据量不同。例如当事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除了一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。(事务B读取了事务A新增加的数据或者读不到事务A删除的数据)
隔离级别
为了解决以上问题,MySQL为事务提供了四种隔离级别,由低到高分别为Read Uncommited、Read Commited、Repeatable Read、Serializable。
- TRANSACTION_READ_UNCOMMITTED。未提交读。说明在提交前一个事务可以看到另一个事务的变化。这样读脏数据,不可重复读和虚读都是被允许的。
- TRANSACTION_READ_COMMITTED。已提交读。说明读取未提交的数据是不允许的。这个级别仍然允许不可重复读和虚读产生。脏读不会产生
- TRANSACTION_REPEATABLE_READ。可重复读。说明事务保证能够再次读取相同的数据而不会失败,但虚读仍然会出现。脏读和不可重复读不会产生
- TRANSACTION_SERIALIZABLE。串行化。是最高的事务级别,它防止读脏数据,不可重复读和虚读。就是串行读取数据,隔离级别最高,但是并发性最低,比较消耗数据库性能,一般不用。
总结:
- 四种隔离级别对以上三种并发存在的问题的解决能力:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | × | × | × |
已提交读 | √ | × | × |
可重复读 | √ | √ | × |
串行化 | √ | √ | √ |
- 越往上(->未提交读),并发能力越高,但是数据的一致性以及安全性越差;越往下(->串行化),并发能力越高,数据的一致性以及安全性越高。
- 事务隔离级别越高,为避免冲突所花费的性能也就越多。
- 在“可重复读”级别,实际上可以解决部分的虚读问题,但是不能防止update更新产生的虚读问题,要禁止虚读产生,还是需要设置串行化隔离级别。
查看隔离级别
select @@transaction_isolation;
设置隔离级别
set transaction_isolation='READ-UNCOMMITTED';
举个栗子~
READ-UNCOMMITTED 未提交读
READ-COMMITTED 已提交读
REPEATABLE_READ 可重复读—MySQL的默认隔离级别
可重复读在一定意义上,是可以防止虚读的!他可以防止insert和delete,但是不能防止update。
SERIALIZABLE 串行化
相当于读写锁,允许一起读,不允许一起写。mysql不会运行一直阻塞,超过时间限制就会报错。