事物的隔离性
mysql的服务端是支持多个客户端同时与之连接的,每个客户端可能还并发了好几个连接,所以mysql是需要同时处理很多事情的,每一件独立的事情就叫做事务。我们知道事务有一个叫隔离性的特性,隔离性理论上是指在某个事物对某个数据进行访问时,其他的事务就应该排队知道访问数据的事务提交才能继续访问该数据。但是这样对性能的影响就太大了,但是我们又必须保持一定的隔离性,所以就需要折中一下。
事务并发可能的问题
首先,让我们来看看如果不能保证绝对孤立会遇到什么问题。
[En]
First, let’s take a look at what problems will be encountered if there is no guarantee of absolute isolation.
- 脏写
如果一个事务修改了由另一个未提交的事务修改的数据,这意味着发生了脏写入。
[En]
If one transaction modifies data modified by another uncommitted transaction, this means that a dirty write has occurred.
- 脏读
如果一个事务读取由另一个未提交的事务修改的数据,这意味着发生了脏读取。
[En]
If one transaction reads data modified by another uncommitted transaction, this means that a dirty read has occurred.
- 不可重复读
如果提交另一个事务后的最新值可以在同一事务中读取,则另一个事务对该数据所做的每一次更改都将导致该事务读取不同的最新值,这意味着发生了不可重复的读取。
[En]
If the latest value after the commit of another transaction can be read in the same transaction, each change made by another transaction to this data will cause the transaction to read a different latest value, which means that an unrepeatable read has occurred.
- 幻读
如果一个事务根据一些记录找到一些记录,然后另一个事务将满足这些条件的记录插入到表中,当该事务再次使用该条件查询时,它会找出上次没有找到的数据,即另一个事务刚刚插入的数据,这称为虚读。
[En]
If one transaction finds some records based on some, and then another transaction inserts records that meet these conditions into the table, when the transaction queries with that condition again, it will find out the data that was not found last time, that is, the data just inserted by another transaction, which is called illusory reading.
小贴士:
不可重复读和幻读确实有点相似,但不可重复读重点在于update和delete,而幻读的重点在于insert。对于前者,要避免只需锁住满足条件的已有记录即可,避免后者就需要锁住满足条件的记录(包括存在的和不存在的),不存在的记录如何才能锁住呢?所以锁的范围需要扩大到满足条件的相邻范围的记录(临键锁)
事务的隔离级别
这时就出现了一个标准用来定义上面说的折中的程度,在SQL标准中定义了4个隔离级别:
- READ UNCOMMITTED:读未提交
- READ COMMITTED:读已提交
- REPEATABLE READ:可重复读
- SERIALIZABLE:可串行化
SQL标准中规定,针对不同的隔离级别并发事务可以发生不同严重程度的问题
MVCC
MVCC解决了事务并发时读和写同时进行互不影响的问题,从而提升系统性能。mvcc并不能解决完全解决脏读和不可重复读的问题,如果innoDB只有mvcc没有锁,那么当前事务确实没办法读取到未提交的数据,但是可以修改。
对于已提交事务和可重复事务,都必须确保读取的记录是由已提交事务修改的记录,因此,如果要确保读取和写入不相互影响,核心问题是确定在读取记录时版本链中的哪个版本对当前事务可见。
[En]
For both committed and repeatable transactions, it is necessary to ensure that the records read are those modified by committed transactions, so if you want to ensure that reads and writes do not affect each other, the core problem is to determine which version in the version chain is visible to the current transaction when reading the record.
MVCC原理
对于innoDB存储引擎来说,每张表中都含有两个必要的隐藏列trx_id和roll_pointer。每次对某条记录进行改动时,都会把旧的版本写入到 undo日志中,然后在这个roll_pointer列中存储旧版本在undo日志的地址,可以通过它来找到该记录修改前的信息,同时将进行改动的事务id写入trx_id列。
在可重复读的隔离模式下每个事务都会生成一个 ReadView ,主要包含4个重要的内容:
m_ids :表示在生成 ReadView 时当前系统中活跃的读写事务的 事务id 列表。
min_trx_id :表示在生成 ReadView 时当前系统中活跃的读写事务中最小的 事务id ,也就是
m_ids 中的最 小值。
max_trx_id :表示生成 ReadView 时系统中应该分配给下一个事务的 id 值。
creator_trx_id :表示生成该 ReadView 的事务的 事务id 。
有了这个 ReadView ,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:
如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己 修改过的记录,所以该版本可以被当前事务访问。
如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 前已经提交,所以该版本可以被当前事务访问。
如果被访问版本的 trx_id 属性值大于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生 成 ReadView 后才开启,所以该版本不可以被当前事务访问。
如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。
如果数据的一个版本对当前事务不可见,则按照版本链查找数据的下一个版本,继续执行上述步骤以确定可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本不可见,则表示该记录对交易完全不可见,查询结果中不包含该记录。
[En]
If one version of the data is not visible to the current transaction, follow the version chain to find the next version of the data, continue to follow the above steps to determine visibility, and so on, until the last version in the version chain. If the last version is not visible, it means that the record is completely invisible to the transaction, and the query result does not include the record.
上面两段引用来自于《MySQL是怎样运行的:从根儿上理解MySQL》,我觉得已经很明白了。
我写文章试图纠正,但水平有限,欢迎老板斧头纠正。
[En]
I write articles to try to be correct, but the level is limited, welcome boss axe correct.
相关资源:
MySQL是怎样运行的:从根儿上理解MySQL
Original: https://www.cnblogs.com/hjsh/p/16632370.html
Author: 时有限学无涯
Title: 事物的隔离性和MVCC
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/504922/
转载文章受原作者版权保护。转载请注明原作者出处!