读取的问题
- 脏读:读取到了未提交的数据
- 不可重复读:一个事务,两次读取到的数据不同。注意,这里不同是指两次读取到的记录主键一样,但是其中的内容不一样
- 幻读:一个事务,两次读取到的数据记录不同。注意,这里的要点是,读取到了之前没读取到的记录,或者之前读取的记录,本次读取没有获取到。
这些问题都是在并发访问下出现的,如何更好理解这几个问题呢?
在一个事务A进行数据库操作时,另外的事务B可能进行一些操作:
- B修改、新增了数据,但未提交,此时A去查询数据库
- 此时如果没有任何隔离操作,那么A有可能读到未提交的数据,出现脏读
- A查询数据库 --> B修改数据并且提交 --> A再次查询数据库
- 假如数据库做了限制,仅提交后的数据才会被读到。但是A仍有可能在B被提交前后两次读取中,获取到了被B修改后的不同的数据,出现不可重复读
- A查询数据库 --> B新增数据并且提交 --> A再次查询数据库
- 假定数据库在2的基础上进一步做限制,对第一次查询的数据记录加了锁或创建快照。但A前后两次查询语句条件,仍有可能包含B新增的记录,于是出现了幻读
事务的隔离性
对应上面的问题,于是对应的有几种隔离。
READ UNCOMMITTED:未作任何隔离,此时之前的1、2、3都可能发生
READ COMMITTED(RC):顾名思义,只读提交后的数据,那么可以解决问题(1)脏读
REPEATABLE READ(RR):解决(2)不可重复读的问题的隔离机制
SERIALIZABLE: 解决幻读的隔离级别
与MySQL的关系
MySQL默认采用RR级别,但是MySQL的RR级别,也可解决幻读问题。
这种控制多事务并发隔离的做法,有个名字叫MVCC(多版本并发管理)。
MySQL的实现方式:通过在每条记录后面增加两个事务版本号,版本号是每个事务开始时唯一生成的。两个版本号一个存储创建版本,一个存储删除版本。
查询时,事务只查询
- 规则1:创建版本在本事务之前 - 从而去除被其他事务新加入的记录
- 规则2:且删除版本号大于本事务(或不存在)的记录 - 从而保证被其他事务删除的记录仍然能读到
那么修改呢?修改时,MySQL会生成一条新记录,并更新原记录删除版本号。这样
- 对于本事务来说,因为有规则2,所以查询不到原记录
- 对于其他事务(在次事务开始前的事务),因为有规则1,新插入的记录版本会高于其他事务版本,其他事务也就读不到了。