MYSQL独立性レベルおよびテストには、ダーティリード、ファンタジーリードが含まれます.

20108 ワード

概要
SQL規格では、4つの独立性レベルが定義されています.これには、トランザクションの内外でどのような変更が表示されるか、表示されないかを定義するための特定のルールが含まれています.低レベルの独立性レベルは、一般的に、より高い同時処理をサポートし、システムオーバーヘッドを低減します.1.Read Uncommitted(コミットされていないコンテンツを読み込む)独立性レベルでは、すべてのトランザクションに他のコミットされていないトランザクションの実行結果が表示されます.この独立性レベルは、他のレベルよりもパフォーマンスが優れていないため、実際のアプリケーションではあまり使用されません.コミットされていないデータを読み取ることをダーティリード(Dirty Read)ともいう.2.Read Committed(コミットされたコンテンツの読み取り)ほとんどのデータベース・システムのデフォルトの独立性レベルです(ただし、MySQLのデフォルトではありません).独立性の簡単な定義を満たしています.1つのトランザクションでは、トランザクションにコミットされた変更しか表示されません.この独立性レベルは、同じトランザクションの他のインスタンスがインスタンス処理の間に新しいcommitを返す可能性があるため、同じselectが異なる結果を返す可能性があるため、いわゆる重複不可能読み取り(Nonrepeatable Read)もサポートします.3.Repeatable Read(再読み込み可能)MySQLのデフォルトのトランザクション独立性レベルです.同じトランザクションの複数のインスタンスがデータを同時に読み込むときに同じデータ行が表示されることを保証します.しかし、理論的には、幻読(Phantom Read)というもう一つの厄介な問題を引き起こす.簡単に言えば、幻読みとは、ユーザがある範囲のデータ行を読み取ると、別のトランザクションがその範囲内に新しい行を挿入し、ユーザがその範囲のデータ行を読み取ると、新しい「幻」行が発見されることを意味する.InnoDBとFalconストレージエンジンは、この問題をマルチバージョン同時制御(MVCC,Multiversion Concurrency Control)メカニズムによって解決した.4.Serializable(シリアル化可能)これは最も高い独立性レベルであり、トランザクションのソートを強制することで、互いに衝突しないようにし、ファンタジーの問題を解決します.簡単に言えば、各読み出しデータ行に共有ロックを追加します.このレベルでは、大量のタイムアウト現象とロック競合を引き起こす可能性があります.この4つの独立性レベルは異なるロックタイプで実現され,同じデータを読み取ると問題が発生しやすい.たとえば、ダーティリード(Drity Read):あるトランザクションがデータを更新し、別のトランザクションが同じデータを読み込んだ場合、いくつかの理由で前のRollBackが操作した場合、後のトランザクションが読み込んだデータは正しくありません.再読み取り不可(Non-repeatable read):1つのトランザクションの2つのクエリでデータが一致しません.これは、2つのクエリ・プロシージャの間に1つのトランザクションが更新された元のデータが挿入されている可能性があります.幻読(Phantom Read):1つのトランザクションの2つのクエリでデータペン数が一致しません.たとえば、1つのトランザクションがいくつかのカラム(Row)データをクエリしたのに、別のトランザクションがこの時点で新しい数列のデータを挿入した場合、前のトランザクションは次のクエリで、いくつかのカラムのデータが以前になかったことがわかります.MySQLでは、次の4つの独立性レベルが実装されています.
独立性レベル
汚読
繰り返し不可
まぼろし読み
Read Uncommitted



Read Committed
×


Repeatable Read
×
×

Serializable
×
×
×
テスト
テスト前の準備
以下、MySQLのクライアントプログラムを利用して、それぞれいくつかの独立性レベルをテストします.テストデータベースはtest、テーブルはt 1である.表構造:mysql-uxxxx-pでクライアントウィンドウMYSQL_を開くA
mysql> desc t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(10) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.05 sec)

独立性レベルの変更方法:ユーザーはSET TRANSACTION文を使用して、単一セッションまたはすべての新規接続の独立性レベルを変更できます.デフォルトの動作(sessionおよびglobalを除く)は、次の(開始されていない)トランザクションの独立性レベルを設定することに注意してください.GLOBALキーを使用する場合、文は、その時点から作成されたすべての新しい接続(存在しない接続を除く)に対してデフォルトのトランザクション・レベルをグローバルに設定します.これをするにはSUPER権限が必要です.SESSIONキーを使用して、現在の接続で将来実行されるトランザクションのデフォルトのトランザクション・レベルを設定します.どのクライアントでも、セッション独立性レベル(トランザクションの中央でも)を自由に変更したり、次のトランザクションに独立性レベルを設定したりできます.
次の文を使用して、グローバルトランザクションとセッショントランザクションの独立性レベルを問い合わせることができます.SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} SELECT @@global.tx_isolation; SELECT @@session.tx_isolation;
##           
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+
1 row in set (0.00 sec)
mysql> select @@tx_isolation;
+-----------------------+
| @@tx_isolation        |
+-----------------------+
| READ-UNCOMMITTED      |
+-----------------------+
1 row in set (0.00 sec)

トランザクションの自動コミットの変更
mysql> show variables like '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> set autocommit = off;
Query OK, 0 rows affected (0.01 sec)

mysql> show variables like '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

独立性レベルと手動トランザクションを変更した後.MYSQL_という別のmysqlウィンドウを開きますB
テストの開始
Read Uncommittedのテスト(読み取りはコミットされていません)
MYSQL_A実行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1 values(1,'aa');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | aa   |
+------+------+
1 row in set (0.00 sec)

MYSQL_B実行:
mysql> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | aa   |
+------+------+
1 row in set (0.00 sec)

MYSQL_A実行:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t1 values(2,'bb');
Query OK, 1 row affected (0.01 sec)

MYSQL_B実行:
mysql> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | aa   |
|    2 | bb   |
+------+------+
2 rows in set (0.00 sec)

MYSQL_A実行:
mysql> rollback;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | aa   |
+------+------+
1 row in set (0.00 sec)

MYSQL_B実行:
mysql> select * from t1;
+------+------+
| id   | name |
+------+------+
|    1 | aa   |
+------+------+
1 row in set (0.00 sec)

以上の実験で結論が得られ、事務MYSQL_Aレコードが挿入されていますが、コミットされていません.このときMYSQL_Bは、コミットされていないレコードを問い合わせることができる.汚読現象を引き起こす.コミットされていない読み取りは、最も低い独立性レベルです.
注意【MYSQL_A】でも【MYSQL_B】でもトランザクションを開くたびに、CRUD操作が終了したらcommitします.そうでなければ、他のトランザクション操作に影響します.
Read Committedのテスト(コミット済み)
MYSQL_A実行:
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-COMMITTED        |
+-----------------------+
1 row in set (0.00 sec)

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+-----------------------+
| @@tx_isolation        |
+-----------------------+
| READ-COMMITTED        |
+-----------------------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test.t1;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | ccFromMB5 |
|    2 | bb        |
|    3 | cc        |
|    4 | dd        |
+------+-----------+
4 rows in set (0.00 sec)

MYSQL_B実行:
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-COMMITTED        |
+-----------------------+
1 row in set (0.00 sec)

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+-----------------------+
| @@tx_isolation        |
+-----------------------+
| READ-COMMITTED        |
+-----------------------+
1 row in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test.t1 values(5,'ee');
Query OK, 1 row affected (0.00 sec)

MYSQL_A実行:依然として4つの記録です.
mysql> select * from test.t1;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | ccFromMB5 |
|    2 | bb        |
|    3 | cc        |
|    4 | dd        |
+------+-----------+
4 rows in set (0.00 sec)

MYSQL_B実行:commit
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

MYSQL_A実行:
mysql> select * from test.t1;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | ccFromMB5 |
|    2 | bb        |
|    3 | cc        |
|    4 | dd        |
|    5 | ee        |
+------+-----------+
5 rows in set (0.00 sec)
#           ,      commit
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

以上の実験により、コミットされたリード独立性レベルはダーティリードの問題を解決したが、2つのクエリの間にトランザクションBが1つのデータを挿入したため、トランザクションAがB commit前後で2回のクエリのデータが一致しないという再読み取り不可能な問題が発生した.コミットされた読み取りは、コミットされたレコードの読み取りのみを許可しますが、繰り返し読み取りは必要ありません.
Repeatable Readのテスト(再読み取り可能)
MYSQL_AとMYSQL_B実行:
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set (0.00 sec)

MYSQL_A実行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test.t1;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | ccFromMB5 |
|    2 | bb        |
|    3 | cc        |
|    4 | dd        |
+------+-----------+
4 rows in set (0.00 sec)

MYSQL_B実行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test.t1 values(6,'fff');
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> update test.t1 set name = 'rere' where id =1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test.t1;
+------+------+
| id   | name |
+------+------+
|    1 | rere |
|    2 | bb   |
|    3 | cc   |
|    4 | dd   |
|    5 | ee   |
|    6 | fff  |
+------+------+
6 rows in set (0.00 sec)

MYSQL_A実行:
#   commit
mysql> select * from test.t1;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | ccFromMB5 |
|    2 | bb        |
|    3 | cc        |
|    4 | dd        |
|    5 | ee        |
+------+-----------+
5 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)
#  commit ,       
mysql> select * from test.t1;
+------+------+
| id   | name |
+------+------+
|    1 | rere |
|    2 | bb   |
|    3 | cc   |
|    4 | dd   |
|    5 | ee   |
|    6 | fff  |
+------+------+
6 rows in set (0.00 sec)

伝説のRepeatable Readの幻読みをテスト
幻読みを再現するために、t 1テーブルのidフィールドをプライマリキーMYSQL_に変更します.A実行:
mysql> alter table test.t1 add primary key(id);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc test.t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | 0       |       |
| name  | varchar(10) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

OK幻読MYSQLのテストを始めましたA実行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test.t1;
+----+------+
| id | name |
+----+------+
|  1 | rere |
|  2 | bb   |
|  3 | cc   |
|  4 | dd   |
|  5 | ee   |
|  6 | fff  |
+----+------+
6 rows in set (0.00 sec)

MYSQL_B実行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test.t1 values(7,'ggg');
Query OK, 1 row affected (0.01 sec)

mysql> commit;
Query OK, 0 rows affected (0.02 sec)

mysql> select * from test.t1;
+----+------+
| id | name |
+----+------+
|  1 | rere |
|  2 | bb   |
|  3 | cc   |
|  4 | dd   |
|  5 | ee   |
|  6 | fff  |
|  7 | ggg  |
+----+------+
7 rows in set (0.01 sec)

MYSQL_A実行:
mysql> select * from test.t1;
+----+------+
| id | name |
+----+------+
|  1 | rere |
|  2 | bb   |
|  3 | cc   |
|  4 | dd   |
|  5 | ee   |
|  6 | fff  |
+----+------+
6 rows in set (0.00 sec)

mysql> insert into test.t1 values (7,'vvv');
ERROR 1062 (23000): Duplicate entry '7' for key 'PRIMARY'
         ,    6 ,    B     id 7   ,    ,  A        (  :ERROR 1062 (23000): Duplicate entry '7' for key 'PRIMARY'),   A   ,       id 7       。   Repeatable Read(   )    
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from test.t1;
+----+------+
| id | name |
+----+------+
|  1 | rere |
|  2 | bb   |
|  3 | cc   |
|  4 | dd   |
|  5 | ee   |
|  6 | fff  |
|  7 | ggg  |
+----+------+
7 rows in set (0.00 sec)

Serializableのテスト(シリアル化)
MYSQL_A実行:
mysql> set global transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| SERIALIZABLE          |
+-----------------------+
1 row in set (0.00 sec)

MYSQL_Bは依然として繰り返し読み取り可能独立性レベルである.MYSQL_A実行:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test.t1;
+----+------+
| id | name |
+----+------+
|  1 | rere |
|  2 | bb   |
|  3 | cc   |
|  4 | dd   |
|  5 | ee   |
|  6 | fff  |
|  7 | ggg  |
|  8 | hhh  |
+----+------+
8 rows in set (0.00 sec)

MYSQL_B実行:
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ       |
+-----------------------+
1 row in set (0.00 sec)

mysql> insert into test.t1 values(9,'iii');

この場合、クライアントはAトランザクションがコミットされるまで実行結果を印刷しないまま待機します.MYSQL_A実行:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

MYSQL_B自動終了待ちはA側がcommitを実行して印刷したもので、実行時間を見ることができます.私は23秒待ってから手動でA端でcommitを実行しました.
#     insert       
Query OK, 1 row affected (23.41 sec)
#B commit。
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

MYSQL_A実行:
mysql> select * from test.t1;
+----+------+
| id | name |
+----+------+
|  1 | rere |
|  2 | bb   |
|  3 | cc   |
|  4 | dd   |
|  5 | ee   |
|  6 | fff  |
|  7 | ggg  |
|  8 | hhh  |
|  9 | iii  |
+----+------+
9 rows in set (6.09 sec)

ここで注意しなければならないのは、B端insert後もcommitがない場合です.では、A側も同様に待機状態に入り、B側がトランザクションをコミットするまで待機します.実行時間を見ることができます.私はまずA側でクエリーを実行し、待機に入ります.次にB側でcommitを実行する.その後、A端は、B端に挿入されたidが9であるデータの検出を待つ.この独立性レベルが最も高く、リソースが最も消費され、パフォーマンスが最も悪いレベルであることがわかります.serializableはフィールドを完全にロックし、1つのトランザクションが同じデータをクエリーする場合は、前のトランザクションが完了してロックが解除されるまで待たなければなりません.完全な独立性レベルであり、対応するデータテーブルがロックされるため、効率的な問題があります.
Serializableは、B端子が挿入されたときにタイムアウトを待つエラーを引き起こす可能性があります.例えば、SELECT @@tx_isolation;