We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
数据库中的数据在被更新的时候不总是一次到位的,有些时候我们需要分多步进行操作,比如在转账流程中我们需要先减少支付方账户上一定数目的金额,然后在收款方账户上增加对应数目的金额。
两步操作应该保证要么同时成功要么同时失败,否则会导致总体金额增加或者减少,这就会导致银行亏损。
为了避免这个问题,数据库中的数据操作需要保证 ACID 特性,即:
在 MySQL 中,满足以上特性的功能叫做 事务(Transaction) 。
为了实现上面的四个特性,MySQL 实现了锁机制和 undo log 和 redo log。其中锁机制保证了事务之间的隔离性,即使存在并发也不会导致两个事务之间的数据更新互相影响;undo log 和 redo log 保证了事务的一致性、原子性和隔离性。
事务之间实现了互相隔离的特性,但是这个隔离也是有级别的,我们可以设置数据库的默认事务隔离级别,MySQL 有以下这几种隔离级别,从上到下隔离级别逐渐增高:
MySQL 的默认隔离级别是 RR(Repeatable Read)。
前一部分有说到几种隔离级别中分别存在的问题,现在加以解释:
根据 SQL 标准,这些问题和事务隔离级别之间的关系如下所示:
一定程度上来说,MySQL 通过 MVCC 解决了 RR 隔离级别中的幻读问题。
通过提高隔离级别,可以解决每个隔离级别自身的问题。
比如为了解决脏读问题,可以将隔离级别提高到 RC;为了解决不可重复读问题,将隔离级别提高到 RR,为了解决幻读问题,将隔离级别提高到 S。
但是我们一般不会直接使用 S 级别的隔离,因为性能确实比较差,MySQL 默认使用 RR 隔离级别。这种情况下需要解决幻读问题,就可以采用 MVCC 或者间隙锁和临键锁。
下面实践体验一下三种问题。
创建一个 table:
CREATE TABLE `trx_lock_tab` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `a` int NOT NULL, `b` int NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uidx_a` (`a`), KEY `idx_b` (`b`) );
表中存在的数据如下:
在事务中的查询语句分为两种,分别是当前读和快照读。
当前读的意思是在 select 语句执行的时候先给要查询的记录加上悲观锁,这样可以确保查询出来的数据是最新的,不会被在事务执行期间发生变化。当前读也叫做加锁读或者阻塞读。MySQL 的 RR 隔离级别下,当前读通过间隙锁或者临键锁解决幻读问题。
快照读的意思是在 select 语句执行的时候生成数据快照(实际上是通过 MVCC 的 ReadView 记录了事务的活跃情况),在 RR 隔离级别下第一个 select 语句会生成 ReadView 快照,之后不再生成;在 RC 隔离级别中每一个 select 语句都会生成 ReadView。所以 RC 隔离级别下每次 select 都可能读到的数据不一样(读取了其他事务的已提交修改,不可重复读),RR 隔离级别下的快照读在事务执行过程中读取到的数据始终是一样的,所以说 MySQL RR 隔离级别下的快照读通过 MVCC 解决了幻读问题。
MVCC(MultiVersion Concurrency Control) :多版本并发控制。通过数据版本链来解决并发场景下的快照读-写(或者写-快照读)时不可重复读或者幻读的问题,避免了加锁带来的开销,提升了 DB 性能。
对于快照读场景(select 语句不加锁),RC 和 RR 隔离级别下需要确保读取到的数据不包含未被事务提交的修改记录(RC)以及确保读取到的数据在事务执行过程中始终一致。MVCC 能够确保这两种隔离级别下的快照读可以满足要求。
MVCC 的前提条件:
版本链示意图:
基于这两个前提,MySQL 的设计者提出了 ReadView 的概念。
在事务执行的特定阶段(后续补充),会生成对应的 ReadView,ReadView 的内容包括:
所以当食物中的快照读语句开始执行时,需要基于当前生成的 ReadView 和对应数据行的 trx_id 进行对比,可能的对比场景如下:
当对应数据行的 trx_id 不满足要求而导致不能返回查询数据时,应该依据 roll_pointer 指针往旧版本数据中进行查找,知道找到符合条件的版本的数据进行返回或者返回为空。
那么 MVCC 是如何区分 RC 和 RR 两种隔离级别的呢?
答案是 ReadView 在两种隔离级别中生成的时机不一样,在 RC 隔离级别中,只需要确保未被提交的数据不能读取到就好了,所以每次 select 快找查询都会生成 ReadView。所以如果事务中存在多次读取,第二次读取可能会返回和第一次读取不一样的数据,因为两次读取的间隔时间段内可能有其他事务修改并提交了事务。
在 RR 隔离级别中,要确保事务内的每一次 select 快找查询都依据统一版本数据进行查找,所以 ReadView 只有在事务中的第一条 select 语句执行时才会被创建,之后的每次 select 语句执行都会基于已经创建的 ReadView 进行版本判断。所以 RR 级别事务中的所有 select 语句读取到的数据都来自同一个版本,这有点儿像 Git Tag。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
数据库中的数据在被更新的时候不总是一次到位的,有些时候我们需要分多步进行操作,比如在转账流程中我们需要先减少支付方账户上一定数目的金额,然后在收款方账户上增加对应数目的金额。
两步操作应该保证要么同时成功要么同时失败,否则会导致总体金额增加或者减少,这就会导致银行亏损。
事务特性
为了避免这个问题,数据库中的数据操作需要保证 ACID 特性,即:
在 MySQL 中,满足以上特性的功能叫做 事务(Transaction) 。
为了实现上面的四个特性,MySQL 实现了锁机制和 undo log 和 redo log。其中锁机制保证了事务之间的隔离性,即使存在并发也不会导致两个事务之间的数据更新互相影响;undo log 和 redo log 保证了事务的一致性、原子性和隔离性。
四种隔离级别
事务之间实现了互相隔离的特性,但是这个隔离也是有级别的,我们可以设置数据库的默认事务隔离级别,MySQL 有以下这几种隔离级别,从上到下隔离级别逐渐增高:
MySQL 的默认隔离级别是 RR(Repeatable Read)。
三种问题
前一部分有说到几种隔离级别中分别存在的问题,现在加以解释:
根据 SQL 标准,这些问题和事务隔离级别之间的关系如下所示:
一定程度上来说,MySQL 通过 MVCC 解决了 RR 隔离级别中的幻读问题。
通过提高隔离级别,可以解决每个隔离级别自身的问题。
比如为了解决脏读问题,可以将隔离级别提高到 RC;为了解决不可重复读问题,将隔离级别提高到 RR,为了解决幻读问题,将隔离级别提高到 S。
但是我们一般不会直接使用 S 级别的隔离,因为性能确实比较差,MySQL 默认使用 RR 隔离级别。这种情况下需要解决幻读问题,就可以采用 MVCC 或者间隙锁和临键锁。
下面实践体验一下三种问题。
创建一个 table:
表中存在的数据如下:
当前读和快照读
在事务中的查询语句分为两种,分别是当前读和快照读。
当前读的意思是在 select 语句执行的时候先给要查询的记录加上悲观锁,这样可以确保查询出来的数据是最新的,不会被在事务执行期间发生变化。当前读也叫做加锁读或者阻塞读。MySQL 的 RR 隔离级别下,当前读通过间隙锁或者临键锁解决幻读问题。
快照读的意思是在 select 语句执行的时候生成数据快照(实际上是通过 MVCC 的 ReadView 记录了事务的活跃情况),在 RR 隔离级别下第一个 select 语句会生成 ReadView 快照,之后不再生成;在 RC 隔离级别中每一个 select 语句都会生成 ReadView。所以 RC 隔离级别下每次 select 都可能读到的数据不一样(读取了其他事务的已提交修改,不可重复读),RR 隔离级别下的快照读在事务执行过程中读取到的数据始终是一样的,所以说 MySQL RR 隔离级别下的快照读通过 MVCC 解决了幻读问题。
关于 MVCC
MVCC(MultiVersion Concurrency Control) :多版本并发控制。通过数据版本链来解决并发场景下的快照读-写(或者写-快照读)时不可重复读或者幻读的问题,避免了加锁带来的开销,提升了 DB 性能。
MVCC 的基本功能
对于快照读场景(select 语句不加锁),RC 和 RR 隔离级别下需要确保读取到的数据不包含未被事务提交的修改记录(RC)以及确保读取到的数据在事务执行过程中始终一致。MVCC 能够确保这两种隔离级别下的快照读可以满足要求。
MVCC 的基本原理
MVCC 的前提条件:
版本链示意图:
基于这两个前提,MySQL 的设计者提出了 ReadView 的概念。
在事务执行的特定阶段(后续补充),会生成对应的 ReadView,ReadView 的内容包括:
所以当食物中的快照读语句开始执行时,需要基于当前生成的 ReadView 和对应数据行的 trx_id 进行对比,可能的对比场景如下:
当对应数据行的 trx_id 不满足要求而导致不能返回查询数据时,应该依据 roll_pointer 指针往旧版本数据中进行查找,知道找到符合条件的版本的数据进行返回或者返回为空。
那么 MVCC 是如何区分 RC 和 RR 两种隔离级别的呢?
答案是 ReadView 在两种隔离级别中生成的时机不一样,在 RC 隔离级别中,只需要确保未被提交的数据不能读取到就好了,所以每次 select 快找查询都会生成 ReadView。所以如果事务中存在多次读取,第二次读取可能会返回和第一次读取不一样的数据,因为两次读取的间隔时间段内可能有其他事务修改并提交了事务。
在 RR 隔离级别中,要确保事务内的每一次 select 快找查询都依据统一版本数据进行查找,所以 ReadView 只有在事务中的第一条 select 语句执行时才会被创建,之后的每次 select 语句执行都会基于已经创建的 ReadView 进行版本判断。所以 RR 级别事务中的所有 select 语句读取到的数据都来自同一个版本,这有点儿像 Git Tag。
The text was updated successfully, but these errors were encountered: