この記事では、MySQL のトランザクションを理解し、MVCC の原理を紹介します。お役に立てれば幸いです。
データベース トランザクションは、一連のデータ操作を指します。トランザクション内の操作はすべて成功するか、すべて失敗します。何も行われません。実際、何も行われないわけではありません。可能性があります。一部は完了していますが、1 つのステップが失敗した場合はすべての操作をロールバックする必要があり、これは少しノンストップの操作となります。
MySQL では、トランザクション サポートはエンジン層で実装されます。 MySQL は複数のエンジンをサポートするシステムですが、すべてのエンジンがトランザクションをサポートしているわけではありません。 たとえば、MySQL のネイティブ MyISAM エンジンはトランザクションをサポートしていません。これが、MyISAM が InnoDB に置き換えられた重要な理由の 1 つです。
1.1 4 つの主要な特徴
1.2 分離レベル
SQL トランザクションの 4 つの主要な特性のうち、アトミック性、一貫性、耐久性はすべて比較的簡単に実現できます。理解する。しかし、トランザクションの分離レベルは確かに難しいので、今日は主に MySQL トランザクションの分離について説明します。 SQL の標準トランザクション分離は、低レベルから高レベルまで次のとおりです:read uncommitted (コミットされていない読み取り)、read commited (コミットされた読み取り)、Repeatable Read (反復可能な読み取り)、および Serializable (シリアル化可能)。レベルが高くなるほど、効率は低くなります。
1.3 解決された同時実行性の問題
SQL トランザクション分離レベルは、同時実行性の問題を最大限に解決するように設計されています。シリアル化された分離レベルのみが 3 つの問題すべてを解決し、他の 3 つの分離レベルにはそれぞれ欠陥があります。
ダーティ リード | Non-repeatable read | ファントム リード | |
---|---|---|---|
可能 | 可能 | 可能 | |
不可能 | 可能 | 可能 | |
不可能 | 不可能 | 可能 | 連載 |
#不可能 | # 追記: 非反復読み取りとファントム読み取りは混同されやすいですが、非反復読み取りは変更に重点が置かれ、ファントム読み取りは追加または削除に重点が置かれます。非反復読み取りの問題を解決するには、条件を満たす行をロックするだけで済みますが、ファントム読み取りの問題を解決するには、テーブルをロックする必要があります 1.4例をあげてください これは少しわかりにくいかもしれないので、例を挙げてみましょう。以前のテーブル構造とテーブル データはそのままです CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `age` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 66 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
今、2 つの食品を同時に開始したいとします。トランザクション A は生徒に次の質問をします。 id = 2 年齢、トランザクション B は id = 2 の生徒の年齢を更新します。プロセスは次のとおりです。 4 つの分離レベルでの X1、X2、および X3 の値は何ですか?
では、なぜそのような結果が起こるのでしょうか?トランザクション分離レベルはどのように実装されますか? トランザクション分離レベルはどのように実装されますか?私は Geek Time での Ding Qi 先生のクラスで答えを見つけました: 実際、データベース内にビューが作成され、アクセス時にはビューの論理結果が優先されます。 「Repeatable Read」分離レベルでは、このビューはトランザクションの開始時に作成され、トランザクション全体で使用されます。 「読み取りコミット」分離レベルでは、このビューは各 SQL ステートメントの先頭に作成されます。ここで注意する必要があるのは、「read uncommitted」分離レベルでは、ビューの概念なしでレコードの最新の値が直接返されるのに対し、「serialization」分離レベルでは、並列を避けるためにロックが直接使用されることです。アクセス###。 1.5 トランザクション分離レベルの設定 データベースごとにデフォルトのトランザクション分離レベルも大きく異なります。Oracle データベースのデフォルトの分離レベルは次のとおりです。Read commit ですが、MySQL は repeatable read です。したがって、システムでデータベースを Oracle から MySQL に移行する必要がある場合は、予期せぬ問題を回避するために、レベルを移行前のレベル (読み取りコミット) と一致するように設定してください。 #1.5.1 トランザクション分離レベルの確認# 分離レベルを変更するステートメントの形式は次のとおりです: set [スコープ] トランザクション分離レベル [トランザクション分離レベル]スコープはオプションです: SESSION (セッション)、GLOBAL ( global); 分離レベル 上記の 4 つであり、大文字と小文字は区別されません。 例: グローバル分離レベルを read-commit に設定しますset global transaction isolation level read committed;1.6 トランザクションの起動 MySQL トランザクションの起動は次のようにいくつかの方法があります: 理解了隔离级别,那事务的隔离是怎么实现的呢?要想理解事务隔离,先得了解 MVCC 多版本的并发控制这个概念。而 MVCC 又依赖于 undo log 和 read view 实现。 2.1 什么是 MVCC? 百度上的解释是这样的: MVCC 使得数据库读不会对数据加锁,普通的 SELECT 请求不会加锁,提高了数据库的并发处理能力;数据库写才会加锁。 借助 MVCC,数据库可以实现 READ COMMITTED,REPEATABLE READ 等隔离级别,用户可以查看当前数据的前一个或者前几个历史版本,保证了 ACID 中的 I 特性(隔离性)。 2.1.1 InnDB 中的 MVCC InnDB 中每个事务都有一个唯一的事务 ID,记为 transaction_id。它在事务开始时向 InnDB 申请,按照时间先后严格递增。 而每行数据其实都有多个版本,这就依赖 undo log 来实现了。每次事务更新数据就会生成一个新的数据版本,并把 transaction_id 记为 row trx_id。同时旧的数据版本会保留在 undo log 中,而且新的版本会记录旧版本的回滚指针,通过它直接拿到上一个版本。 所以,InnDB 中的 MVCC 其实是通过在每行记录后面保存两个隐藏的列来实现的。一列是事务 ID:trx_id;另一列是回滚指针:roll_pt。 2.2 undo log 回滚日志保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。 根据操作的不同,undo log 分为两种: insert undo log 和 update undo log。 2.2.1 insert undo log insert 操作产生的 undo log,因为 insert 操作记录没有历史版本只对当前事务本身可见,对于其他事务此记录不可见,所以 insert undo log 可以在事务提交后直接删除而不需要进行 purge 操作。 所以,插入数据时。它的初始状态是这样的:
2.2.2 update undo log UPDATE 和 DELETE 操作产生的 Undo log 都属于同一类型:update_undo。(update 可以视为 insert 新数据到原位置,delete 旧数据,undo log 暂时保留旧数据)。 事务提交时放到 history list 上,没有事务要用到这些回滚日志,即系统中没有比这个回滚日志更早的版本时,purge 线程将进行最后的删除操作。 1 つのトランザクションが現在のデータを変更します:
別のトランザクションがデータを変更します:
データベース内に同じレコードの複数のバージョンが存在します。これが、前述のマルチバージョン同時実行制御 MVCC です。 さらに、アンドゥ ログを利用してロールバックすることで、以前のバージョンの状態に戻すことができます。たとえば、V1 に戻すには、2 つのロールバックを順番に実行するだけで済みます。 2.3 読み取りビュー #読み取りビューは、RC の実装をサポートするために MVCC を実装するときに InnDB によって使用される一貫した読み取りビューです。 (Read Committed) および RR (Repeatable Read) 分離レベル 。 read ビューは実際には存在せず、単なる概念であり、undo ログはその具体化です。主にバージョンとアンドゥログを通じて計算されます。この機能は、トランザクションが参照できるデータ を決定することです。 各トランザクションまたはステートメントには、独自の整合性ビューがあります。通常のクエリ ステートメントは一貫した読み取りです。一貫した読み取りは、行 trx_id と一貫したビュー に基づいてデータ バージョンの可視性を決定します。 2.3.1 データ バージョンの可視性ルール アクティブな (まだ送信されていない) トランザクション を保存します。 システム内で送信されたトランザクション ID の最大値が配列の最低水位として記録され、作成されたトランザクション ID 1 は高水位として記録されます。 このビュー配列と最高水位は、現在のトランザクションの整合性ビュー (読み取りビュー) を形成します。 ルールは次のとおりです:
RC (読み取りコミット) および RR (反復読み取り) 分離レベルでは、T4 および T5 時点でのクエリ経過時間の値はそれぞれいくらですか? T4 の更新された値は何ですか? 少し考えてみてください。誰もが独自の答えを持っていると思います。答えは記事の最後にありますので、自分なりの疑問を持ちながら読み進めていただければと思います。 2.3.2 RR (Repeatable Read) での結果 RR レベルでは、クエリはトランザクションが実行される前にのみ認識されます。送信された完了したデータについては、トランザクションが開始されるとビューが構築されます。したがって、一貫性のあるスナップショット コマンドでトランザクションの開始を使用すると、ビューがすぐに作成されます。 现在假设: 在这种隔离级别下,他们创建视图的时刻如下:
根据上图得,事务 A 的视图数组是[2,3];事务 B 的视图数组是 [2,3,4];事务 C 的视图数组是[2,3,4,5]。分析一波: T4 时刻,B 读数据都是从当前版本读起,过程是这样的: T5 时刻,A 读数据都是从当前版本读起,过程是这样的: 这样执行下来,虽然期间这一行数据被修改过,但是事务 A 不论在什么时候查询,看到这行数据的结果都是一致的,所以我们称之为一致性读。 其实视图是否可见主要看创建视图和提交的时机,总结下规律: 事务 B 的 update 语句,如果按照上图的一致性读,好像结果不大对? 如下图周明,B 的视图数组是先生成的,之后事务 C 才提交。那就应该看不见 C 修改的 age = 23 呀?最后 B 怎么得出 24 了?
没错,如果 B 在更新之前执行查询语句,那返回的结果肯定是 age = 22。问题是更新就不能在历史版本更新了呀,否则 C 的更新不就丢失了? 所以,更新有个规则:更新数据都是先读后写(读是更新语句执行,不是我们手动执行),读的就是当前版本的值,叫当前读;而我们普通的查询语句就叫快照读。 因此,在更新时,当前读读到的是 age = 23,更新之后就成 24 啦。 除了更新语句,查询语句如果加锁也是当前读。如果把事务 A 的查询语句 select age from t where id = 2 改一下,加上锁(lock in mode 或者 for update),也都可以得到当前版本 4 返回的 age = 24 下面就是加了锁的 select 语句: 假设事务 C 不马上提交,但是 age = 23 版本已生成。事务 B 的更新将会怎么走呢?
事务 C 还没提交,写锁还没释放,但是事务 B 的更新必须要当前读且必须加锁。所以事务 B 就阻塞了,必须等到事务 C 提交,释放锁才能继续当前的读。
2.3.3 RC(读提交)下的结果 在读提交隔离级别下,查询只承认在语句启动前就已经提交完成的数据;每一个语句执行之前都会重新算出一个新的视图。 注意:在上图的表格中用于启动事务的是 start transaction with consistent snapshot 命令,它会创建一个持续整个事务的视图。所以,在 RC 级别下,这命令其实不起作用。等效于普通的 start transaction(在执行 sql 语句之前才算是启动了事务)。所以,事务 B 的更新其实是在事务 C 之后的,它还没真正启动事务,而 C 已提交。 现在假设: この分離レベルでは、ビューが作成される時間は次のとおりです。 上図によると、トランザクション A のビュー配列は [2,3,4] ですが、高水位が 6 以上 (トランザクション ID 1 が作成されている)、ビュー配列トランザクション B のビュー配列は [2,4]、トランザクション C のビュー配列は [2,5] です。分析の波: T4 時間に、B は現在のバージョンからデータを読み取ります。プロセスは次のとおりです: 時刻 T5 で、A は現在のデータからデータを読み取ります。 [関連する推奨事項: |
以上がMySQLのトランザクションとMVCCの原理を詳しく解説した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。