このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
|
mariadb:10.4:transaction [2020/11/19 15:35] y2sunlight |
mariadb:10.4:transaction [2020/11/20 09:55] (現在) y2sunlight |
||
|---|---|---|---|
| 行 13: | 行 13: | ||
| * MariaDB10.4 トランザクション | * MariaDB10.4 トランザクション | ||
| - | 以下「MySQL」は「MariaDB」に読み替えて下さい | + | 以下「MySQL」は「MariaDB」に読み替えて下さい。 |
| ---- | ---- | ||
| - | MySQLではトランザクションセーフなテーブル(InnoDBなど)と非トランザクションセーフなテーブル(MyISAMなど)を作る事ができます。手動トランザクションが使えるのはトランザクションセーフなテーブルだけです。 MyISAMなどの非トランザクションセーフなテーブルの場合は、データベースの変更は直ちにコミットされます。本章ではストレージエンジンとしてInnoDBを使ったテーブルを対象とします。 | + | ===== 概要 ===== |
| - | ==== MySQLのトランザクション ==== | + | MySQLではトランザクションセーフなテーブル(InnoDBなど)と非トランザクションセーフなテーブル(MyISAMなど)を作る事ができます。手動トランザクションが使えるのはトランザクションセーフなテーブルだけです。 MyISAMなどの非トランザクションセーフなテーブルの場合は、データベースの変更は直ちにコミットされます。 |
| + | |||
| + | 本章ではストレージエンジンとしてInnoDBを使ったテーブルを対象として、トランザクションについて説明します。InnoDBでは、以下のトランザクション分離レベルをサポートしています: | ||
| + | |||
| + | * READ UNCOMMITTED --- 非コミット読み取り | ||
| + | * READ COMMITTED --- コミット済み読み取り | ||
| + | * REPEATABLE READ --- 再読み込み可能読み取り | ||
| + | * SERIALIZABLE --- 直列化 | ||
| + | |||
| + | ここでは、これらの分離レベルの違いをサンプルプログラムを使用して実感しながら説明したいと思います。 | ||
| + | |||
| + | \\ | ||
| + | |||
| + | ===== MySQLのトランザクション | ||
| MySQLはデフォルトで自動トランザクション機能(自動コミットモード)が有効です。本編ではMySQLiを使っているので、プログラムの途中で自動コミットモードを切り替える場合は、次のようにします。 | MySQLはデフォルトで自動トランザクション機能(自動コミットモード)が有効です。本編ではMySQLiを使っているので、プログラムの途中で自動コミットモードを切り替える場合は、次のようにします。 | ||
| 行 65: | 行 78: | ||
| InnoDBではSQL92で定義されている4つのトランザクション分離レベルを全て提供しています。デフォルトの分離レベルREPEATABLE READです。デフォルトの分離レベルはオプションファイル(my.ini)で変更することができます。 | InnoDBではSQL92で定義されている4つのトランザクション分離レベルを全て提供しています。デフォルトの分離レベルREPEATABLE READです。デフォルトの分離レベルはオプションファイル(my.ini)で変更することができます。 | ||
| - | < | + | {{fa> |
| + | < | ||
| [mysqld] | [mysqld] | ||
| transaction-isolation = REPEATABLE-READ | transaction-isolation = REPEATABLE-READ | ||
| 行 81: | 行 95: | ||
| > [[https:// | > [[https:// | ||
| - | ==== SQL文による手動トランザクション ==== | + | \\ |
| + | |||
| + | ===== SQL文による手動トランザクション ===== | ||
| ブラウザからサンプルプログラム(sqlfile.php)を実行します。 | ブラウザからサンプルプログラム(sqlfile.php)を実行します。 | ||
| 行 126: | 行 143: | ||
| この例で、START TRANSACTION文を1行目に移動して実行すると、奇妙な現象が起こります。ロールバックが効かなくなるのです。これは、DROP TABLE文により暗黙のコミットが発生してトランザクションが終了している為です。それ以降は自動コミットモードになるのでROLLBACK文は無効になります。この現象はDROP TABLEやCREATE TABLEなどのDDL文の場合に発生します。トランザクションを設計する場合は注意する必要があります。 | この例で、START TRANSACTION文を1行目に移動して実行すると、奇妙な現象が起こります。ロールバックが効かなくなるのです。これは、DROP TABLE文により暗黙のコミットが発生してトランザクションが終了している為です。それ以降は自動コミットモードになるのでROLLBACK文は無効になります。この現象はDROP TABLEやCREATE TABLEなどのDDL文の場合に発生します。トランザクションを設計する場合は注意する必要があります。 | ||
| - | ==== MySQLi による手動トランザクション ==== | + | \\ |
| + | |||
| + | ===== MySQLi による手動トランザクション | ||
| ブラウザからサンプルプログラム(sqlfile.php)を実行します。 | ブラウザからサンプルプログラム(sqlfile.php)を実行します。 | ||
| 行 163: | 行 182: | ||
| 上のスクリプトファイルにはEVAL文が含まれています。EVALに続く式(例えば:$mysqli-> | 上のスクリプトファイルにはEVAL文が含まれています。EVALに続く式(例えば:$mysqli-> | ||
| - | 実行結果 | + | === 実行結果 |
| [{{: | [{{: | ||
| 行 173: | 行 192: | ||
| MySQLiを利用してデータベースをアクセスする場合、移植性による理由から、begin_transaction/ | MySQLiを利用してデータベースをアクセスする場合、移植性による理由から、begin_transaction/ | ||
| - | ==== トランザクションの分離レベル ==== | + | \\ |
| + | |||
| + | ===== トランザクションの分離レベル | ||
| MySQLは4つのトランザクション分離レベルを提供しています。SQL92では、これらの分離レベルは下表のように説明されています。しかし、**MySQLではREPEATABLE READレベルでファントムリードが発生しません**。 | MySQLは4つのトランザクション分離レベルを提供しています。SQL92では、これらの分離レベルは下表のように説明されています。しかし、**MySQLではREPEATABLE READレベルでファントムリードが発生しません**。 | ||
| 行 238: | 行 259: | ||
| > 共有ロック:他のトランザクションからはデータの参照はできますが、更新はできません | > 共有ロック:他のトランザクションからはデータの参照はできますが、更新はできません | ||
| + | \\ | ||
| - | === トランザクション分離レベル:READ UNCOMMITTED === | + | ==== READ UNCOMMITTED |
| transaction2a.sql のトランザクション分離レベルをREAD UNCOMMITTEDに設定します。そして、syainテーブルの更新系トランザクションを含む以下のSQLスクリプトファイル(transaction2b.sql)を準備します。 | transaction2a.sql のトランザクション分離レベルをREAD UNCOMMITTEDに設定します。そして、syainテーブルの更新系トランザクションを含む以下のSQLスクリプトファイル(transaction2b.sql)を準備します。 | ||
| 行 267: | 行 289: | ||
| - ''< | - ''< | ||
| - | 実行結果 | + | === 実行結果 |
| [{{: | [{{: | ||
| 行 275: | 行 297: | ||
| 同様の実験を、transaction2a.sql のトランザクション分離レベルを**READ COMMITTED**に設定して行ないます。 | 同様の実験を、transaction2a.sql のトランザクション分離レベルを**READ COMMITTED**に設定して行ないます。 | ||
| - | 実行結果 | + | === 実行結果 |
| [{{: | [{{: | ||
| 行 281: | 行 303: | ||
| 2つ目の単純SELECT文ではダーティリードが発生しません。また、SELECT文が更新系トランザクションの終了を待たない点にも注目して下さい。これは、MySQLの読み取り一貫性がロックを取得しないで実現されているからです。この分離レベルではSELECTされた時点の最新のコミット済みスナップショットが保持されます。 | 2つ目の単純SELECT文ではダーティリードが発生しません。また、SELECT文が更新系トランザクションの終了を待たない点にも注目して下さい。これは、MySQLの読み取り一貫性がロックを取得しないで実現されているからです。この分離レベルではSELECTされた時点の最新のコミット済みスナップショットが保持されます。 | ||
| - | === トランザクション分離レベル:READ COMMITTED === | + | \\ |
| + | |||
| + | ==== READ COMMITTED | ||
| transaction2a.sql のトランザクション分離レベルを**READ COMMITTED**に設定します。そして、syainテーブルの更新系トランザクションを含む以下のSQLスクリプトファイル(transaction2c.sql)を準備します。 | transaction2a.sql のトランザクション分離レベルを**READ COMMITTED**に設定します。そして、syainテーブルの更新系トランザクションを含む以下のSQLスクリプトファイル(transaction2c.sql)を準備します。 | ||
| 行 303: | 行 327: | ||
| - ''< | - ''< | ||
| - | 実行結果 | + | === 実行結果 |
| [{{: | [{{: | ||
| 行 314: | 行 338: | ||
| この例では、3つ目の(共有ロックを取得する)SELECT文の検索結果が、単純SELECT文の場合と同じです。両者の違いは次の実験で明らかになります。 | この例では、3つ目の(共有ロックを取得する)SELECT文の検索結果が、単純SELECT文の場合と同じです。両者の違いは次の実験で明らかになります。 | ||
| - | === トランザクション分離レベル:REPEATABLE READ === | + | \\ |
| + | |||
| + | ==== REPEATABLE READ ==== | ||
| transaction2a.sql のトランザクション分離レベルを**REPEATABLE READ**に設定します。syainテーブルの更新系トランザクションを含むSQLスクリプトファイル(transaction2c.sql)は前の実験と同じものを使用します。 | transaction2a.sql のトランザクション分離レベルを**REPEATABLE READ**に設定します。syainテーブルの更新系トランザクションを含むSQLスクリプトファイル(transaction2c.sql)は前の実験と同じものを使用します。 | ||
| 行 334: | 行 360: | ||
| - ''< | - ''< | ||
| - | 実行結果 | + | === 実行結果 |
| [{{: | [{{: | ||
| 行 344: | 行 370: | ||
| 上の実行結果で、3つ目の(共有ロックを取得する)SELECT文の検索結果を見てください。この検索結果は前の例題(READ COMMITTED)の場合と同じです。ロックを取得するSELECT文を実行した場合は、最初に読み取ったスナップショットではなく、最新のデータを読み取っているようです。 | 上の実行結果で、3つ目の(共有ロックを取得する)SELECT文の検索結果を見てください。この検索結果は前の例題(READ COMMITTED)の場合と同じです。ロックを取得するSELECT文を実行した場合は、最初に読み取ったスナップショットではなく、最新のデータを読み取っているようです。 | ||
| - | === トランザクション分離レベル:SERIALIZABLE === | + | \\ |
| + | |||
| + | ==== SERIALIZABLE | ||
| transaction2a.sql のトランザクション分離レベルを**SERIALIZABLE**に設定します。syainテーブルの更新系トランザクションを含むSQLスクリプトファイル(transaction2c.sql)は前の実験と同じものを使用します。 | transaction2a.sql のトランザクション分離レベルを**SERIALIZABLE**に設定します。syainテーブルの更新系トランザクションを含むSQLスクリプトファイル(transaction2c.sql)は前の実験と同じものを使用します。 | ||
| 行 361: | 行 389: | ||
| ブラウザから ''< | ブラウザから ''< | ||
| - | 実行結果 | + | === 実行結果 |
| [{{: | [{{: | ||
| 行 369: | 行 397: | ||
| このレベル(SERIALIZABLE)の単純SELECT文は暗黙の共有ロックモード(LOCK IN SHARE MODE)で実行されます。SERIALIZABLEレベルのトランザクションは分離レベルが最も高く、他のトランザクションの影響を受けないように設計されています。但し、この実行例(排他ロックを取得する操作が待機状態になる事)からも分かるように、トランザクションの同時実行性は最も低くなります。 | このレベル(SERIALIZABLE)の単純SELECT文は暗黙の共有ロックモード(LOCK IN SHARE MODE)で実行されます。SERIALIZABLEレベルのトランザクションは分離レベルが最も高く、他のトランザクションの影響を受けないように設計されています。但し、この実行例(排他ロックを取得する操作が待機状態になる事)からも分かるように、トランザクションの同時実行性は最も低くなります。 | ||
| - | + | \\ | |