数据库的事务不一致性及MySQL数据库InnoDB引擎隔离级别
数据库事务不一致性
脏读
检索不可靠数据的操作,该数据是由另一个事务更新但尚未提交的数据。只有在隔离级别为读未提交的情况下才有可能出现。
原文:"An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed. It is only possible with the isolation level known as read uncommitted."
不可重复读
查询检索数据,同一事务中的后续查询检索应为相同数据的情况,但是查询返回不同的结果(与此同时,另一笔提交的事务已(对数据进行了)更改)。
原文:"The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (changed by another transaction committing in the meantime)."
官方对于不可重复读的影响是如此形容的
这种操作违背了数据库设计的ACID原则。在交易中,数据应保持一致,并具有可预测和稳定的关系。
原文:"This kind of operation goes against the ACID principle of database design. Within a transaction, data should be consistent, with predictable and stable relationships."
私以为可以理解成:不可重复读的存在使事务中的查询数据变得不可预测。即对于当前事务,在未对本事务历史查询结果集中所涉及的数据进行修改的前提下,相同的查询语句所获取的结果集中的数据依旧是不可预测的。而这种不可预测性在“不可重复读”中仅表现在数据的读取方面,即若隔离级别为“可重复读”,则仅仅能够保证在同一事务中两次查询所获取的结果集(在本事务中没有对其进行修改的情况下)是一致的,但并不关心结果集中涉及的数据实际上是否被修改。
幻读
幻读(phantom read),意为“幻象读取”,而MySQL官方对于“幻象”(phantom)的解释为
“在查询结果集中出现,而在(同一事务中)较早的查询结果集中没有出现的行。”
原文:"A row that appears in the result set of a query, but not in the result set of an earlier query. "
由于InnoDB的RR级别对于不可重复读问题所采取的[多版本并发控制](MVCC)机制,幻象行在InnoDB的RR级别不会在查询结果集中显示出来,但幻象行却是实际存在的。
例如在事务A中经过一次一致性非锁定读取确认表中符合某查询条件的记录不存在,随后事务B向表中插入了若干条符合查询条件的数据并提交,那么此时若事务A根据首次查询结果而向表中插入与事务B插入的记录有着相同主键的记录,此次操作将由于主键冲突而被拒绝。
幻读现象的发生反映出查询结果集相较于快照发生了扩充。然而可重复读仅仅保证了结果集内容的一致性,即通过对[一致性非锁定读取](Consistent Nonlocking Reads)查询到的结果集所在行进行加锁(待进一步核实)的方式防止事务提交前对结果集中行内数据的修改以及对结果集中行的删除。但对于添加符合查询条件记录的操作无法进行限制,从而造成了上文中出现的“结果集扩充”现象,即幻读。
实际上传统的RR级别并没有办法避免幻读,因为常规的对行加锁并不能锁住即将插入的不存在的行。MySQL数据库InnoDB引擎在RR级别下使用了下一键锁(next-key lock),即在行级别锁(row-level locking)的基础上添加了间隙锁(gap locking),在事务中进行插入操作或执行有插入意向(select for update)的语句时将其间隙加锁,能够一定程度上避免幻读的发生,但使用一致性非锁定读取(即常规查询)时并不会对记录加任何锁,故而在实际开发的事务中应该避免使用[单纯的查询语句的结果]作为下方insert或update语句的执行判断依据。
MySQL数据库事务隔离级别与效果
分类
事务隔离级别
是否脏读
是否不可重复读
是否幻读
介绍
读未提交(read-uncommitted(RU))
✔
✔
✔
·能读到未提交数据
读已提交(read-committed(RC))
✘
✔
✔
·写数据会对行加锁
·不能读到未提交数据
可重复读(repeatable-read(RR))
✘
✘
✔
·可通过next-key锁避免幻读
·MySQLInnoDB默认级别
·查询的都是事务开始时的数据
串行读(serializable)
✘
✘
✘
·所有操作串行执行
·读写锁整张表
时序图
脏数据
#### 不可重复读
#### 幻读