Tencentインタビュー:MySQLトランザクションとMVCCの間の分離レベルを達成する方法は?

WeChat検索[ SanTaizi Ao Bing ]は、感情と乾物で、このさまざまなプログラマーに注目しています。

この記事GitHubhttps ://github.com/JavaFamilyに含まれており、主要企業へのインタビュー用の完全なテストサイト、資料、および一連の記事が含まれています

序文

実は、この事業やMVCCなど、データベースの章の基本的な知識のポイントを書いたのですが、国民の日の情報を読んでみると、前回の執筆はほぼ意味があり、例もほぼ意味があることがわかりました。私の最新の理解によると、前の要約を追加することは書き直すことと同等です。あなたにも新しい利益があることを願っています。

データベーストランザクションの概要

トランザクションの4つの特性(ACID)

  1. Atomicity(atomicity):トランザクションの最小の作業単位で、すべて成功するか、すべて失敗します。

  2. 一貫性:トランザクションの開始と終了後、データベースの整合性は破壊されません。

  3. 分離(分離):異なるトランザクションは相互に影響を与えません.4つの分離レベルは、RU(コミットされていない読み取り)、RC(コミットされた読み取り)、RR(繰り返し可能な読み取り)、およびSERIALIZABLE(シリアル化)です。

  4. 耐久性:トランザクションがコミットされた後、データの変更は永続的であり、システムに障害が発生しても失われません。

トランザクション分離レベル

UnCommitted / RUを読む

ダーティリードも呼ばれ、あるトランザクションが別のトランザクションのコミットされていないデータを読み取ることができます。コミットされていないトランザクションはロールバックされるため、この分離レベルは最も安全性の低いレベルです。

コミット済み/ RCを読む

非反復読み取りも呼ばれます。あるトランザクションが別のトランザクションによってコミットされた変更されたデータを読み取るため、現在のトランザクションの異なる時間に同じデータを読み取ることによって得られる結果には一貫性がありません。

たとえば、次の例では、トランザクション中にSessionAが2回クエリするデータが異なることがわかります。その理由は、現在の分離レベルがRCであり、SessionAのトランザクションがSessionBによって送信された最新のデータを読み取ることができるためです。

発生時間 SessionA SessionB
1 ベギン;
2 select * from user where id = 1;(张三)
3 update user set name = 'Li Si' where id = 1;(トランザクションはデフォルトで暗黙的にコミットされます)
4 select * from user where id = 1;(李四)
5 update user set name = 'Wang Er' where id = 1;(デフォルトでは暗黙的にトランザクションをコミットします)
6 select * from user where id = 1;(王二)

繰り返し可能な読み取り(繰り返し可能な読み取り/ RR)

ファントム読み取りも呼ばれるトランザクションは、他のトランザクションによって送信されたデータを読み取ることができますが、RR分離レベルでは、現在のデータは1回しか読み取ることができません。現在のトランザクションでは、読み取り回数に関係なく、データはそれでも、他のトランザクションが最初の読み取り後にデータを変更して送信するため、最初に読み取られた値は変更されません。したがって、読み取られたデータが必ずしも最新のデータであるとは限らないため、ファントム読み取りとも呼ばれます。

例:SessionAでデータが初めて読み取られると、後続のトランザクションによって送信されたデータが変更され、SessionAによって読み取られるデータ値に影響がなくなります。これは繰り返し可能な読み取りです。

発生時間 SessionA SessionB
1 ベギン;
2 select * from user where id = 1;(张三)
3 update user set name = 'Li Si' where id = 1;(トランザクションはデフォルトで暗黙的にコミットされます)
4 select * from user where id = 1;(张三)
5 update user set name = 'Wang Er' where id = 1;(デフォルトでは暗黙的にトランザクションをコミットします)
6 select * from user where id = 1;(张三)

シリアル化可能(シリアル化可能)

すべてのデータベースの読み取りまたは書き込み操作はシリアルに実行され、現在の分離レベルでは1つの要求のみが同時にサポートされ、すべての操作をキューで実行する必要があります。したがって、この分離レベルのすべてのデータは最も安定していますが、パフォーマンスも最悪です。データベースのロック実装は、この分離レベルのより小さな詳細バージョンです。

発生時間 SessionA SessionB
1 ベギン;
2 ベギン;
3 ユーザーセット名を更新= '李四'ここでid = 1;
4 select * from user where id = 1;(等待、待機)
5 コミット;
6 select * from user where id = 1;(李四)

トランザクションとMVCCの原則

同じデータを同時に操作する異なるトランザクションによって引き起こされる問題

例:

発生時間 SessionA SessionB
1 ベギン;
2 ベギン;
3 チェック残高= 1000元
4 チェック残高= 1000元
5 預金額は100元、修正残高は1100元
6 この時点で100元を現金で引き出し、残高を900元に変更します
8 トランザクションをコミットします(残高= 1100)
9 トランザクションをコミットします(残高= 900)
発生時間 SessionA SessionB
1 ベギン;
2 ベギン;
3 チェック残高= 1000元
4 チェック残高= 1000元
5 預金額は100元、修正残高は1100元
6 この時点で100元を現金で引き出し、残高を900元に変更します
8 トランザクションをコミットします(残高= 1100)
9 取引をキャンセルします(残高は1,000元に戻ります)

上記の2つの状況は、1つのデータに対して複数のトランザクションを同時に操作した場合に発生する可能性のある問題です。特定のトランザクションの操作が上書きされ、データが失われる可能性があります。

LBCCはデータ損失を解決します

LBCC、ロックベースの同時実行制御、ロックベースの同時実行制御。

ロックメカニズムを使用すると、現在のトランザクションでデータを変更する必要がある場合、現在のトランザクションがロックされます.1つのトランザクションのみが同時に現在のデータを変更でき、他のトランザクションはロックが解除されるのを待ってから操作できます。

MVCCはデータ損失を解決します

MVCC、マルチバージョン同時実行制御、マルチバージョン同時実行制御。

バージョンを使用して、同時実行の場合のデータの問題を制御します。トランザクションBがアカウントの変更を開始し、トランザクションがコミットされていない場合、トランザクションAがアカウントの残高を読み取る必要がある場合、トランザクションBの変更操作前のアカウントの残高のコピーデータがこの時点で読み取られます。 、ただし、トランザクションAがアカウント残高データを変更する必要がある場合は、トランザクションBがトランザクションをコミットするのを待つ必要があります。

MVCC使得数据库读不会对数据加锁,普通的SELECT请求不会加锁,提高了数据库的并发处理能力。 借助MVCC,数据库可以实现READ COMMITTED,REPEATABLE READ等隔离级别,用户可以查看当前数据的前一个或者前几个历史版本,保证了ACID中的I特性(隔离性)。

InnoDB的MVCC实现逻辑

InnoDB存储引擎保存的MVCC的数据

InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。一个保存了行的事务ID(DB_TRX_ID),一个保存了行的回滚指针(DB_ROLL_PT)。每开始一个新的事务,都会自动递增产 生一个新的事务id。事务开始时刻的会把事务id放到当前事务影响的行事务id中,当查询时需要用当前事务id和每行记录的事务id进行比较。

下面看一下在REPEATABLE READ隔离级别下,MVCC具体是如何操作的。

SELECT

InnoDB 会根据以下两个条件检查每行记录:

  1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的事务编号小于或等于当前事务的事务编号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。

  2. 删除的行要事务ID判断,读取到事务开始之前状态的版本,只有符合上述两个条件的记录,才能返回作为查询结果。

INSERT

InnoDB为新插入的每一行保存当前事务编号作为行版本号。

DELETE

InnoDB为删除的每一行保存当前事务编号作为行删除标识。

UPDATE

InnoDB为插入一行新记录,保存当前事务编号作为行版本号,同时保存当前事务编号到原来的行作为行删除标识。

保存这两个额外事务编号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。

MVCC只在REPEATABLE READ和READ COMMITIED两个隔离级别下工作。其他两个隔离级别都和 MVCC不兼容 ,因为READ UNCOMMITIED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁。

MVCC 在mysql 中的实现依赖的是 undo log 与 read view 。

undo log

根据行为的不同,undo log分为两种: insert undo logupdate undo log

  • insert undo log:

insert 操作中产生的undo log,因为insert操作记录只对当前事务本身课件,对于其他事务此记录不可见,所以 insert undo log 可以在事务提交后直接删除而不需要进行purge操作。

purge的主要任务是将数据库中已经 mark del 的数据删除,另外也会批量回收undo pages

数据库 Insert时的数据初始状态:

  • update undo log:

    update 或 delete 操作中产生的 undo log。 因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此update undo log 不能在事务提交时就进行删除,而是将事务提交时放到入 history list 上,等待 purge 线程进行最后的删除操作。

    数据第一次被修改时:

当另一个事务第二次修改当前数据:

为了保证事务并发操作时,在写各自的undo log时不产生冲突,InnoDB采用回滚段的方式来维护undo log的并发写入和持久化。回滚段实际上是一种 Undo 文件组织方式。

ReadView

对于 RU(READ UNCOMMITTED) 隔离级别下,所有事务直接读取数据库的最新值即可,和 SERIALIZABLE 隔离级别,所有请求都会加锁,同步执行。所以这对这两种情况下是不需要使用到 Read View 的版本控制。

对于 RC(READ COMMITTED)RR(REPEATABLE READ) 隔离级别的实现就是通过上面的版本控制来完成。两种隔离界别下的核心处理逻辑就是判断所有版本中哪个版本是当前事务可见的处理。针对这个问题InnoDB在设计上增加了ReadView的设计,ReadView中主要包含当前系统中还有哪些活跃的读写事务,把它们的事务id放到一个列表中,我们把这个列表命名为为m_ids

对于查询时的版本链数据是否看见的判断逻辑:

  • 如果被访问版本的 trx_id 属性值小于 m_ids 列表中最小的事务id,表明生成该版本的事务在生成 ReadView 前已经提交,所以该版本可以被当前事务访问。

  • 如果被访问版本的 trx_id 属性值大于 m_ids 列表中最大的事务id,表明生成该版本的事务在生成 ReadView 后才生成,所以该版本不可以被当前事务访问。

  • 如果被访问版本的 trx_id 属性值在 m_ids 列表中最大的事务id和最小事务id之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。

举个例子:

READ COMMITTED 隔离级别下的ReadView

每次读取数据前都生成一个ReadView (m_ids列表)

时间 Transaction 777 Transaction 888 Trasaction 999
T1 begin;
T2 begin; begin;
T3 UPDATE user SET name = ‘CR7’ WHERE id = 1;
T4
T5 UPDATE user SET name = ‘Messi’ WHERE id = 1; SELECT * FROM user where id = 1;
T6 commit;
T7 UPDATE user SET name = ‘Neymar’ WHERE id = 1;
T8 SELECT * FROM user where id = 1;
T9 UPDATE user SET name = ‘Dybala’ WHERE id = 1;
T10 commit;
T11 SELECT * FROM user where id = 1;

这里分析下上面的情况下的ReadView

时间点 T5 情况下的 SELECT 语句:

当前时间点的版本链:

此时 SELECT 语句执行,当前数据的版本链如上,因为当前的事务777,和事务888 都未提交,所以此时的活跃事务的ReadView的列表情况 m_ids:[777, 888] ,因此查询语句会根据当前版本链中小于 m_ids 中的最大的版本数据,即查询到的是 Mbappe。

时间点 T8 情况下的 SELECT 语句:

当前时间的版本链情况:

此时 SELECT 语句执行,当前数据的版本链如上,因为当前的事务777已经提交,和事务888 未提交,所以此时的活跃事务的ReadView的列表情况 m_ids:[888] ,因此查询语句会根据当前版本链中小于 m_ids 中的最大的版本数据,即查询到的是 Messi。

时间点 T11 情况下的 SELECT 语句:

当前时间点的版本链信息:

此时 SELECT 语句执行,当前数据的版本链如上,因为当前的事务777和事务888 都已经提交,所以此时的活跃事务的ReadView的列表为空 ,因此查询语句会直接查询当前数据库最新数据,即查询到的是 Dybala。

总结: 使用READ COMMITTED隔离级别的事务在每次查询开始时都会生成一个独立的 ReadView。

REPEATABLE READ 隔离级别下的ReadView

在事务开始后第一次读取数据时生成一个ReadView(m_ids列表)

时间 Transaction 777 Transaction 888 Trasaction 999
T1 begin;
T2 begin; begin;
T3 UPDATE user SET name = ‘CR7’ WHERE id = 1;
T4
T5 UPDATE user SET name = ‘Messi’ WHERE id = 1; SELECT * FROM user where id = 1;
T6 commit;
T7 UPDATE user SET name = ‘Neymar’ WHERE id = 1;
T8 SELECT * FROM user where id = 1;
T9 UPDATE user SET name = ‘Dybala’ WHERE id = 1;
T10 commit;
T11 SELECT * FROM user where id = 1;

时间点 T5 情况下的 SELECT 语句:

当前版本链:

再当前执行select语句时生成一个ReadView,此时 m_ids 内容是:[777,888],所以但前根据ReadView可见版本查询到的数据为 Mbappe。

时间点 T8 情况下的 SELECT 语句:

当前的版本链:

此时在当前的 Transaction 999 的事务里。由于T5的时间点已经生成了ReadView,所以再当前的事务中只会生成一次ReadView,所以此时依然沿用T5时的m_ids:[777,999],所以此时查询数据依然是 Mbappe。

时间点 T11 情况下的 SELECT 语句:

当前的版本链:

此时情况跟T8完全一样。由于T5的时间点已经生成了ReadView,所以再当前的事务中只会生成一次ReadView,所以此时依然沿用T5时的m_ids:[777,999],所以此时查询数据依然是 Mbappe。

MVCC总结:

所谓的MVCC(Multi-Version Concurrency Control ,多版本并发控制)指的就是在使用 READ COMMITTDREPEATABLE READ 这两种隔离级别的事务在执行普通的 SEELCT 操作时访问记录的版本链的过程,这样子可以使不同事务的 读-写写-读 操作并发执行,从而提升系统性能。

在 MySQL 中, READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成 ReadView 的时机不同。在 READ COMMITTED 中每次查询都会生成一个实时的 ReadView,做到保证每次提交后的数据是处于当前的可见状态。而 REPEATABLE READ 中,在当前事务第一次查询时生成当前的 ReadView,并且当前的 ReadView 会一直沿用到当前事务提交,以此来保证可重复读(REPEATABLE READ)。

好啦以上就是本期的全部内容了,如果你觉得写得不错,请给我一个免费的在看,你知道的越多,你不知道的越多,我是敖丙,我们下期见,拜拜!

絮叨

敖丙把自己的面试文章整理成了一本电子书,共 1630页!

干货满满,字字精髓。目录如下,还有我复习时总结的面试题以及简历模板,现在免费送给大家。

链接:https://pan.baidu.com/s/1ZQEKJBgtYle3v-1LimcSwg 密码:wjk6

我是敖丙,你知道的越多,你不知道的越多,感谢各位人才的:点赞收藏评论,我们下期见!


文章持续更新,可以微信搜一搜「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

おすすめ

転載: blog.csdn.net/qq_35190492/article/details/109044141