MySQL トランザクションの詳細

MySQL トランザクションの詳細

導入

トランザクションは、SQL ステートメントのグループで構成される論理的な処理単位です。

取引の4つの特徴

原子性:
	すべて成功するか、すべて失敗するかのどちらかです。Undo ログは一貫性を実現します。
	たとえば、転送前と転送後の 2 つの金額の合計は変更されません。分離: データベースは、トランザクションが外部の同時操作の影響を受けない「独立した」環境で実行されるように、特定の分離メカニズムを提供します。ロック、MVCC マルチバージョン同時実行制御。耐久性: トランザクションの送信はディスクの再実行ログに保持されます。

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

データベースには、コミットされていない読み取り、コミットされた読み取り、反復可能な読み取り、シリアル化の 4 つのトランザクション分離レベルがあります。分離レベルが異なると、ダーティ リード、ファントム リード、反復不可能な読み取り、およびその他の関連する問題が発生する可能性があります。したがって、分離レベルを選択するときは、アプリケーションのシナリオに基づいて決定し、異なる分離レベルを使用する必要があります。

分離レベルダーティリード繰り返し不可能な読み取りファントムリード
読み取り未コミット
読み取りコミット×
繰り返し読み取り× ×
シリアル化可能× × ×

トランザクション分離レベルによって発生する問題

ダーティ リード (ダーティ リード) トランザクションが別のトランザクションのコミットされていないデータにアクセスします。
	トランザクションがデータにアクセスしてデータを変更し、この変更がまだデータベースにコミットされていない場合、別のトランザクションもデータにアクセスしてデータを使用します。
非反復読み取り (トランザクションは同じクエリを 2 回実行しますが、異なるデータを取得します):
	トランザクションは、あるデータを読み取り、次に前に読み取ったデータを読み取ります。そのデータが、前に読み取ったデータと一致していないことが検出されます。ファントム リード (トランザクションが同じクエリを 2 回実行し、異なるデータを取得する) を更新および削除します。
	トランザクションは、同じクエリ条件に従って以前にクエリされたデータを再読み取りますが、他のトランザクションがそのクエリ条件を満たす新しいデータを挿入したことを検出します。

確認する

トランザクション分離レベルを表示するには、「tx_isolation」などの変数を表示します。

トランザクションが自動的にコミットされるかどうかを確認します。「autocommit」などの変数を表示します。

自動コミットトランザクションを無効にする = 0|OFF

自動コミットを 0 に設定します。

ダーティリード:

トランザクション分離レベルA、Bを設定する
	セッショントランザクション分離レベルをコミットされていない読み取りに設定します。
セッションA
トランザクションを開始します。トランザクションを開始します。
データを挿入します INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi');
セッションB
別の接続は select * from t_user; をクエリします。
	+----+----------+
	| ID | 名前 |
	+----+----------+
	| 1 | 張三 |
	| 2 | リシ |
	| 3 | 王武 |
	| 4 | 老王 |
	| 5 | ドゥキ |
	+----+----------+
	このとき、接続Bは接続AのコミットされていないトランザクションのレコードIDを照会します。これは5です。
	
ここでは、セッションが別のトランザクションからコミットされていないデータを読み取ったことを確認します。

繰り返し不可能な読み取り:

トランザクション分離レベルを変更します。セッション トランザクション分離レベルを読み取りコミットに設定します。
A はトランザクションを開始します。
更新 B がクエリ ステートメント MySQL [db_test]> select * from t_user; を実行することを確認します。
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+
	A は更新ステートメント update t_user set name = 'duqi' where id = 5; を実行します。
	B はクエリ ステートメントの開始トランザクションを実行します。
		MySQL [db_test]> t_user から * を選択します。
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+
	A はトランザクションコミットをコミットします。
	B はクエリ ステートメントを実行します (同じトランザクションの 2 つのクエリの結果は矛盾しています)
		MySQL [db_test]> t_user から * を選択します。
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+
削除の確認を続けます。A がトランザクションを開始します。B がトランザクションを開始します。トランザクションを開始します。
	A は、id = 5 の t_user からレコードを削除します。
	トランザクション B クエリは正常で、削除されたレコードは MySQL [db_test]> select * from t_user; に残っています。
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+
	コミット。
	B はクエリを続行し、同じトランザクションでの複数のクエリの結果が矛盾していることを発見します。MySQL [db_test]> select * from t_user;
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		+----+----------+

A と B の挿入を確認します。トランザクションを開始します。トランザクションを開始します。
	A はレコードを挿入します INSERT INTO `db_test`.`t_user`(`id`, `name`) VALUES (5, 'DuQi');
	BはMySQLにクエリを実行します [db_test]> select * from t_user;
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		+----+----------+	
	A はトランザクションコミットをコミットします。
	B クエリは、A によって送信されたトランザクションをクエリすることもできます。MySQL [db_test]> select * from t_user;
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+

ファントムリーディング:

トランザクション分離レベルを変更します。セッション トランザクション分離レベルの繰り返し読み取りを設定します。
A と B がトランザクションを開始します。
Aはデータを挿入する	
	`db_test`.`t_user`(`id`, `name`) に VALUES (5, 'DuQi') を挿入します。
B MySQLクエリ [db_test]> select * from t_user;
	+----+----------+
	| ID | 名前 |
	+----+----------+
	| 1 | 張三 |
	| 2 | リシ |
	| 3 | 王武 |
	| 4 | 老王 |
	+----+----------+
A はトランザクションコミットをコミットします。
B トランザクションクエリ MySQL [db_test]> select * from t_user;
	+----+----------+
	| ID | 名前 |
	+----+----------+
	| 1 | 張三 |
	| 2 | リシ |
	| 3 | 王武 |
	| 4 | 老王 |
	| 5 | ドゥキ |
	+----+----------+
異なるトランザクション間で、挿入を照会できることがわかります。更新と削除の検証を続けましょう。A と B がトランザクションを開始します。A は更新します。update t_user set name = 'duqi' where id = 5;
	B MySQLクエリ [db_test]> select * from t_user;
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+	
	Aはトランザクションをコミットする
	BはMySQLのクエリを続行します [db_test]> select * from t_user;
		+----+----------+
		| ID | 名前 |
		+----+----------+
		| 1 | 張三 |
		| 2 | リシ |
		| 3 | 王武 |
		| 4 | 老王 |
		| 5 | ドゥキ |
		+----+----------+
	削除の確認を続けましょう。A と B はトランザクションを開始します。トランザクション A は削除操作 delete from t_user where id = 5; を実行します。
		トランザクション B はクエリ MySQL [db_test]> select * from t_user; を実行します。
			+----+----------+
			| ID | 名前 |
			+----+----------+
			| 1 | 張三 |
			| 2 | リシ |
			| 3 | 王武 |
			| 4 | 老王 |
			| 5 | ドゥキ |
			+----+----------+
		A はトランザクションをコミットし、B は MySQL のクエリを続行します [db_test]> select * from t_user;
			+----+----------+
			| ID | 名前 |
			+----+----------+
			| 1 | 張三 |
			| 2 | リシ |
			| 3 | 王武 |
			| 4 | 老王 |
			| 5 | ドゥキ |
			+----+----------+
REPEATABLE-READ トランザクション分離レベルでは削除と更新の問題は解決されますが、挿入の問題は依然として存在します。

MVCC

マルチバージョンの同時実行制御。 MVCC は同時実行制御方式であり、一般的にデータベース管理システムで使用され、プログラミング言語でデータベースやトランザクション メモリへの同時アクセスを実装します。

MySQL INNODB での mvcc の実装は、主にデータベースの同時パフォーマンスを向上させ、読み取り/書き込み競合をより適切に処理して、読み取り/書き込み競合が発生した場合でも、非ロックおよび非ブロッキングの同時読み取りを実現できるようにすることを目的としています。

mvcc を理解する前に、まず現在の読み取りとスナップショット読み取りという 2 つの概念を理解する必要があります。

現在の読書

最新版のデータを読む

共有モードでのロックの選択 (共有ロック)、更新の選択、更新、挿入、削除 (排他ロック) などの操作はすべて現在の読み取りです。
なぜ「Current Reading」という名前なのですか?
	つまり、レコードの最新バージョンを読み取ります。読み取り時には、他の同時トランザクションが現在のレコードを変更できないようにする必要があり、読み取られたレコードはロックされます。

スナップショット読み取り

履歴バージョンデータを読む

たとえば、ロック解除された選択操作はスナップショット読み取りであり、ロックなしの非ブロッキング読み取りです。
スナップショット読み取りの前提は、分離レベルがシリアル レベルではないことです。シリアル レベルでのスナップショット読み取りは、現在の読み取りに退化します。
スナップショット読み取りの理由は、同時実行パフォーマンスを向上させるためです。スナップショット読み取りの実装は、マルチバージョン同時実行制御、つまり MVCC に基づいています。MVCC は行ロックのバリエーションと考えることができますが、多くの場合、ロック操作を回避し、オーバーヘッドを削減します。

現在の読み取り、スナップショット読み取り、および MVCC 関係

MVCC マルチバージョン同時実行制御とは、読み取り操作と書き込み操作で競合が発生しないように、データの複数のバージョンを維持することを指します。スナップショット読み取りは、MVCC を実装するための MySQL の非ブロッキング読み取り機能です。
Mysql の mvcc モジュールの具体的な実装は、3 つの暗黙的なフィールド、undo ログ、および readview コンポーネントによって実装されます。

ここでもう 1 つ追加する点は、3 つの暗黙的なフィールドの 1 つが列の一意の識別子であるということです。学生の中には、ほとんど役に立たないにもかかわらず、テーブルを設計するときに主キー (列は主キーに依存) を追加しなければならない人もいます。実際、構成テーブルの場合、追加や削除がほとんど行われないテーブルには主キーを追加する必要はありません。データを挿入するときに、MySQL はテーブルに主キーがあるかどうかを判断します。主キーがある場合は、主キーを一意の識別子として使用します。主キーがない場合は、7 バイトの主キーが自動的に生成されます。したがって、テーブルの合理性は、さまざまな使用シナリオに応じて設計する必要があります。

mvccが解決する問題

同時シナリオ

1. 読み取り-読み取り: 問題はなく、同時実行制御も必要ありません。2. 読み取り-書き込み: スレッドの安全性の問題があり、トランザクション分離レベルの問題が発生する可能性があり、ダーティ読み取り、反復不可能な読み取り、ファントム読み取りが発生する可能性があります。3. 書き込み-書き込み: スレッドの安全性の問題があり、更新の損失の問題が発生する可能性があります。

問題は解決した

1. データベースの同時読み取りと書き込みを行う際に、読み取り操作中に書き込み操作をブロックすることなく実行でき、書き込み操作で読み取り操作をブロックする必要がないため、データベースの同時読み取りと書き込みのパフォーマンスが向上します。 2. ダーティ リード、ファントム リード、非反復読み取りなどのトランザクション分離の問題を解決しますが、更新損失の問題は解決できません。

MVCC 実装原則

mvcc の実装原理は、主にレコード内の 3 つの隠しフィールド、undolog、および読み取りビューに依存します。

隠しフィールド

カスタム フィールドに加えて、行レコードには、DB_TRX_ID、BD_ROLL_PTR、DB_ROW_ID など、データベースによって暗黙的に定義されたフィールドもあります。

DB_TRX_ID 最後に変更されたトランザクション ID:
	6バイト、このレコードを作成した、またはこのレコードを最後に変更したトランザクションIDを記録します。
DB_ROLL_PTR ロールバック ポインタ:
	7 バイト、このレコードの前のバージョンを指し、undolog と連携するために使用され、以前の古いバージョンを指す DB_ROW_ID 隠し主キー:
	6 バイト。データベース テーブルに主キーがない場合、InnoDB は自動的に 6 バイトの row_id を生成します。

元に戻すログ

UNDO ログはロールバック ログとも呼ばれ、挿入、削除、更新操作が実行されたときに生成される便利なロールバック ログを意味します。

挿入操作を実行する場合、生成される UNDO ログはトランザクションのロールバック時にのみ必要であり、トランザクションがコミットされた直後に破棄できます。更新および削除操作を実行する場合、生成される UNDO ログはトランザクションのロールバック時だけでなく、スナップショットの読み取り時にも必要となるため、気軽に削除することはできません。スナップショットの読み取りまたはトランザクションのロールバックにログが関係しない場合にのみ、対応するログはパージ スレッドによって均一にクリアされます (データが更新または削除されると、古いレコードのみが設定されます。レコードの delete_id が true で、DB_TRX_ID がパージ スレッドの読み取りビューに表示されている場合、このレコードは確実にクリアできます)

原理

挿入操作を実行すると、対応する削除文が生成されます。削除操作を実行すると、元のデータの挿入文がバックアップされます。更新を実行すると、元のデータの更新文が記録されます。この操作は、レコードのロールバックに便利です。

読む 表示

READ ビューは、トランザクションがスナップショット読み取り操作を実行したときに生成される読み取りビューです。トランザクションがスナップショットを実行すると、データ システムの現在のスナップショットが生成され、システム内で現在アクティブなトランザクションの ID が記録および維持されます。トランザクション ID 値が増分されます。

DB_ROW_ID翻訳元DB_ROLL_PTR c_name年齢
1 1張さん1 18
2 2 1張さん2 19
読み取りビューの最大の機能は、トランザクションがスナップショットを実行している場合、レコードのために作成され、現在のトランザクションが読み取られる可能性があるか、最新のデータが読み取られている可能性があります変更するデータの最新のレコードでDB_TRX_IDを取り出し、DB_TRX_IDが読み取りビューの属性と比較され、DB_TRX_IDがDB_ROLL_LOLL_LOLL LOLL POINTRYを介して削除された場合、DB_TRX_IDが視認性の属性と比較された場合、DB_TROLLBACK POINERSを介して摂取されます。条件を満たすB_TRX_IDは、このDB_TRX_IDが配置されている古いレコードです。

可視性ルール

表示ルールを理解する前に、まず読み取りビューの 3 つのグローバル プロパティを理解する必要があります。

trx_list:
	読み取りビューが生成された時点でシステム内でアクティブなトランザクションIDを維持するために使用される値のリスト
アップリミットID:
	トランザクションIDの最小IDをtrx_listリストに記録する
下限ID:
	読み取りビューが生成されたときに、システムはまだ次のトランザクションIDを割り当てていません。

比較ルール

1. まず、DB_TRX_ID < up_limit_id かどうかを判断します。up_limit_id 未満の場合、現在のトランザクションは DB_TRX_ID が配置されているレコードを見ることができます。以上である場合は、次の判断に進みます。 2. DB_TRX_ID >= low_limit_id かどうかを判断します。以上である場合は、DB_TRX_ID が配置されているレコードは Read View が生成された後にのみ表示されるため、現在のトランザクションには確実に表示されません。未満である場合は、次の判断に進みます。 3. DB_TRX_ID がアクティブなトランザクション内にあるかどうかを判断します。そうである場合は、Read View の生成時に、このトランザクションはまだアクティブであり、コミットされていないことを意味します。変更されたデータは、現在のトランザクションからは見ることができません。そうでない場合は、Read View が生成される前にこのトランザクションがコミットされているため、変更された結果を見ることができることを意味します。

MySQL トランザクションの詳細な紹介に関するこの記事はこれで終わりです。より関連性の高い MySQL トランザクション コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • MySQL データベースのインデックスとトランザクション
  • MySQL トランザクションと分離レベルの基本原理の簡単な分析
  • MySQL トランザクション分析
  • MySQLデータベースのトランザクションとインデックスの詳細な説明
  • MySQL トランザクション分離はどのように実現されますか?
  • MySQLトランザクションの詳細な分析

<<:  vue+elementUI で埋め込みテーブルを実装する方法の例

>>:  EDMをHTMLで記述する際の注意点まとめ(メール送信時の一般的な注意点)

推薦する

ウェブサイトのユーザビリティとコンバージョン率を向上させる 25 のツール

ウェブサイトの場合、ユーザビリティとは、ユーザーが必要な情報を効果的に見つけたり、タスクを完了したり...

docker run 起動パラメータ コマンドを表示する方法 (推奨)

runlike を使用してコンテナの docker run 起動パラメータを表示します。 pipを...

ウェブページ作成に役立つコード

<br />ホームページの右側にあるスクロールバーを削除するにはどうすればよいですか? ...

Ubuntuデュアルシステムが起動時に停止する問題の解決方法の詳細な説明

起動時に Ubuntu デュアル システムが停止する問題の解決方法 (Ubuntu 16.04 およ...

JavaScript でオブジェクトをエレガントに扱う 6 つの方法

目次序文1. オブジェクト.freeze() 2. オブジェクト.seal() 3. オブジェクト....

クラウド サーバーを使用して CentOS システムに .NET 6.0 をインストールする

.NET SDK ダウンロード リンクhttps://dotnet.microsoft.com/do...

MySQLチュートリアルDMLデータ操作言語の例の詳細な説明

目次1. データ操作言語 (DML) 2. データを追加する(挿入) 3. 既存のテーブルをコピーし...

CentOS 6 ZLMediaKit のコンパイルとインストール分析

Centos6にZLMediaKitをインストールするZLMediaKit の作者は Ubuntu ...

2時間のDocker入門チュートリアル

目次1.0 はじめに2.0 Dockerのインストール3.0基本的なDockerコマンド4.0 Do...

IdeaはリモートDockerをデプロイし、ファイルを構成する

1. LinuxサーバーのDocker構成ファイルを変更する vim /usr/lib/system...

一般的な Dockerfile コマンドの使用方法の紹介

目次01 CM 02 エントリーポイント03 ワークディレクトリ04 環境05 ユーザー06巻07 ...

JS配列メソッドの詳細な説明

目次1. 元の配列が変更されます1. プッシュ(): 2.ポップ(): 3. シフト(): 4.un...

Tomcatがセッションを管理する方法の例

ConcurrentHashMapを学習しましたが、どのように適用すればよいかわかりませんか? To...

エンコードが utf-8 に設定されている場合に Web ページが文字化けする問題の解決策

最近、PHP で Web ページを書いているときに、エンコードを UTF-8 に設定しました。しかし...

Windows 10でDockerコンテナのポートにアクセスできない問題に対する完璧な解決策

Windows 10 で Docker コンテナのポートにアクセスできない問題を解決する (ポート ...