環境: mysql5.7.25、cmd コマンドで実証。
データベース トランザクションは開発プロセスで頻繁に使用されるため、この章は非常に重要です。
この記事の内容
-
トランザクションとは何ですか?何に使用されますか?
-
トランザクションのいくつかの特徴
-
一般的なトランザクション操作命令の詳細な説明
-
トランザクション分離レベルの詳しい説明
-
ダーティリード、ノンリピータブルリード、リピータブルリード、ファントムリードの詳細説明
-
さまざまな分離レベルによって生成される現象を実証する
-
分離レベルの選択について
トランザクションとは何ですか?
データベース内のトランザクションとは、データベース上で一連の操作を実行することを指します。これらの操作は最終的にはすべて成功するか失敗するかのどちらかであり、部分的に成功することはありません。
例えば
たとえば、ユーザー A がユーザー B に 100 を転送する場合、プロセスは次のようになります。
1.从A账户扣100
2.给B账户加100
トランザクションでサポートされている場合、最終的には次の 2 つの結果のみになります。
-
操作は成功しました。アカウント A は 100 減少し、アカウント B は 100 増加しました。
-
操作は失敗しました。アカウント A も B も変更されていません。
トランザクション サポートがない場合、エラーが発生する可能性があります: アカウント A が 100 減らされ、システムがハングアップします。その結果、アカウント B は 100 を追加せず、アカウント A は突然 100 を失います。
トランザクションのいくつかの特性 (ACID)
原子性
トランザクションのプロセス全体はアトミックな操作に似ています。最終的にはすべて成功するかすべて失敗します。このアトミック性は最終結果からわかります。最終結果から見ると、このプロセスは分割できません。
一貫性
トランザクションの開始前、実行中、および実行後のこれらの時点で、複数の人がトランザクション操作のデータを観察すると、表示されるデータは一貫しています。たとえば、トランザクション操作中、接続 A は 100 を参照し、その後、接続 A は 100 を参照します。このときBさんも見てみたら100だったので、ABさんが見たデータが違うとは言えず、ある時点で見たデータは一致していました。
分離
トランザクションの実行は、他のトランザクションによって干渉されることはできません。つまり、トランザクション内で使用される操作とデータは他の同時トランザクションから分離されており、同時に実行されるトランザクションは相互に干渉できません。
耐久性
トランザクションがコミットされると、データベース内のデータに対する変更は永続的になるはずです。トランザクションがコミットされると、データはハードディスクに保存され、変更は永続的になります。
MySQL でのトランザクション操作
MySQL のトランザクションはデフォルトでは暗黙的なトランザクションであり、挿入、更新、削除の操作が実行されると、データベースは自動的にトランザクションを開始し、トランザクションをコミットまたはロールバックします。
暗黙的なトランザクションを有効にするかどうかはautocommit
変数によって制御されます。
したがって、トランザクションは暗黙的なトランザクションと明示的なトランザクションに分けられます。
暗黙的なトランザクション
トランザクションは、挿入、更新、削除ステートメントなど、自動的に開かれ、送信され、ロールバックされます。トランザクションの開始、送信、またはロールバックは、MySQL によって内部的に自動的に制御されます。
autocommit
変数の自動送信がオンになっているかどうかを確認します
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
autocommit
ON は自動送信がオンになっていることを意味します。
明示的なトランザクション
トランザクションは手動で開始、コミット、またはロールバックする必要があり、これは開発者によって制御されます。
トランザクションを手動で制御する 2 つの方法:
方法 1:
文法:
//设置不自动提交事务
set autocommit=0;
//执行事务操作
commit|rollback;
例 1: 次のようにトランザクション操作を送信します。
mysql> create table test1 (a int);
Query OK, 0 rows affected (0.01 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values(1);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
例 2: 次のようなロールバック トランザクション操作:
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values(2);
Query OK, 1 row affected (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
上記のデータがロールバックされていることがわかります。
元に戻してみましょうautocommit
:
mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)
方法 2:
文法:
start transaction;//开启事务
//执行事务操作
commit|rollback;
例 1: 次のようにトランザクション操作を送信します。
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into test1 values (3);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
上記で 2 つのデータが正常に挿入されました。
例 2: 次のようなロールバック トランザクション操作:
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> delete from test1;
Query OK, 3 rows affected (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
上記のトランザクションで削除したデータは
test1
、3 行が削除され、最終的にトランザクションがロールバックされたことを示しています。
セーブポイントのキーワード
トランザクション中に多数の操作を実行しました。おそらく、一部のデータをロールバックしたいだけかもしれません。どのようにすればよいですか?
多数の操作をいくつかの部分に分割し、ロールバックする特定の部分を指定できます。savepoin
これは以下を使用して実現できます。
まずtest1
テーブルのデータをクリアします。
mysql> delete from test1;
Query OK, 3 rows affected (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
デモンストレーションsavepoint
効果を注意深く観察してください:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)
mysql> savepoint part1;//设置一个保存点
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (2);
Query OK, 1 row affected (0.00 sec)
mysql> rollback to part1;//将savepint = part1的语句到当前语句之间所有的操作回滚
Query OK, 0 rows affected (0.00 sec)
mysql> commit;//提交事务
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
上記からわかるように、2 回の挿入操作が実行され、最終的に 1 つのデータのみが挿入されました。
savepoint
セーブポイント間で操作をロールバックするrollback to sp1
には、一緒に使用する必要があります。sp1
rollback to
読み取り専用トランザクション
これは、クエリなどの一部の読み取り専用操作がトランザクションで実行されることを意味しますが、挿入、更新、および削除操作は実行されません。データベース内の読み取り専用トランザクションのパフォーマンスが最適化される場合があります。
使用方法は次のとおりです。
start transaction read only;
例:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction read only;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 1 |
+------+
2 rows in set (0.00 sec)
mysql> delete from test1;
ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 1 |
+------+
2 rows in set (0.00 sec)
読み取り専用トランザクションで削除を実行すると、エラーが報告されます。
取引におけるいくつかの問題
これらの問題は主に、複数のトランザクションにわたるデータの可視性に基づいています。
ダーティリード
実行中、1 つのトランザクションは、他のトランザクションがまだコミットしていないデータを読み取ります。
これは比較的理解しやすいです。
コミットされた読み取り
文字通り、トランザクション操作中に、他のトランザクションによって送信されたデータを読み取ることができることを理解できます。
トランザクション内の各読み取り操作は、データベース内の他のトランザクションによって送信された最新のデータを読み取ります (現在の読み取りに相当)
反復可能な読み取り
トランザクション操作で読み取り操作を何回実行しても、読み取り結果は同じになります。
ファントムリーディング
ダーティ リード、ノンリピータブル リード、リピータブル リード、ファントム リード、最も理解しにくいのはファントム リードです
mysql を例に挙げます。
ファントム読み取りは反復読み取りモードでのみ発生し、他の分離レベルでは発生しません。
ファントム リーディング現象の例:
たとえば、反復読み取りモードでは、主キーとして携帯電話番号を持つユーザー テーブルがある場合、次の操作を実行するものが 2 つあります。
トランザクション A の操作は次のとおりです。
1. トランザクションを開きます
。 2. 存在しない番号 X のレコードをクエリします。
3. 番号のデータを挿入します。
まだ存在しないことがわかります (トランザクションであるため)。反復可能な読み取り、読み取りレコード X はまだ存在しません)
トランザクション B 操作: の記録
上記の操作は A にとって幻覚のようなものです。クエリ X (A の 2 番目と 4 番目のステップ) が存在しないことは明らかですが、正常に挿入できません。
ファントム読み取りは次のように理解できます。トランザクション内の後続の操作 (数値 X の挿入) には、上記の読み取り操作 (数値のレコードのクエリ) によるサポートが必要です。まるで幻想です。
それでも理解できない場合は、後で詳細なデモンストレーションを行うために、以下を読み続けてください。
トランザクション分離レベル
複数のトランザクションが同時に進行している場合、現在のトランザクションのデータの正確性を確認するにはどうすればよいですか? たとえば、2 つのこと A と B が同時に進行しているとき、A は B が送信したデータを見ることができますか?それとも B が送信していないデータですか? これには、トランザクションの分離レベルに応じて、異なる分離レベルによってもたらされる効果が異なることを確認する必要があります。
トランザクション分離レベルは主に、複数のトランザクション間のデータの可視性とデータの正確性に関する上記の問題を解決します。
分離レベルには 4 種類あります。
-
コミットされていない読み取り: READ-UNCOMMITTED
-
送信された読み取り: READ-COMMITTED
-
反復可能な読み取り: REPEATABLE-READ
-
シリアル: シリアル化可能
上記 4 つの分離レベルはますます強化されており、データベースの同時実行性はますます低くなります。
分離レベルを確認する
mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name | Value |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)
分離レベルの設定
次の 2 つの手順で、ファイルを変更し、mysql を再起動します。
次のように、mysql の my.init ファイルを変更し、分離レベルを READ-UNCOMMITTED に設定します。
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=READ-UNCOMMITTED
次のように、管理者として cmd ウィンドウを開き、mysql を再起動します。
C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
さまざまな分離レベルで発生する問題
分離レベル | ダーティリード | 反復不可能な読み取り | ファントムリーディング |
---|---|---|---|
読み取り未コミット | 持っている | 持っている | なし |
読み取りコミット済み | なし | 持っている | なし |
反復読み取り | なし | なし | 持っている |
シリアル化可能 | なし | なし | なし |
テーブルとインターネットの間には、主にファントム リーディングの領域でいくつかの違いがあり、ファントム リーディングは反復読み取りレベルでのみ表示され、他のレベルには存在しません。
ウィンドウ A と B という 2 つのウィンドウを開いて、両方のウィンドウで mysql にログインすることで、さまざまな分離レベルでの可視性の問題を実証してみましょう。
READ-UNCOMMITTED: コミットされていない読み取り
分離レベルを次のように設定しますREAD-UNCOMMITTED
。
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=READ-UNCOMMITTED
mysqlを再起動します。
C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
分離レベルを確認します。
mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name | Value |
+-----------------------+----------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)
まず、test1 テーブルのデータをクリアします。
delete from test1;
select * from test1;
2 つのウィンドウで次の操作を時系列に実行します。
時間 | 窓A | ウィンドウB |
---|---|---|
T1 | トランザクションを開始します。 | |
T2 | test1 から * を選択します。 | |
T3 | トランザクションを開始します。 | |
T4 | test1 の値に挿入 (1); | |
T5 | test1 から * を選択します。 | |
T6 | test1 から * を選択します。 | |
T7 | 専念; | |
Q8 | 専念; |
ウィンドウは次のとおりです。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
Bウィンドウは次のとおりです。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
見てください:
T2-A: データなし、T6-A: データあり。B は時間 T6 に送信されていません。この時点で、A は B によって挿入されたデータを確認しており、ダーティ リードが発生したことを示しています。
T2-A: データなし、T6-A: データあり クエリ結果が異なり、繰り返し読み取りができないことを示します。
結論: 読み取りがコミットされていない場合、他のトランザクションのコミットされていないデータを読み取ることができますが、複数の読み取りの結果は異なり、ダーティ リードや反復不可能な読み取りが発生します。
READ-COMMITTED: 読み取りがコミットされました
分離レベルを次のように設定します。READ-COMMITTED
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=READ-COMMITTED
mysqlを再起動します。
C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
分離レベルを確認します。
mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name | Value |
+-----------------------+----------------+
| transaction_isolation | READ-COMMITTED |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)
まず、test1 テーブルのデータをクリアします。
delete from test1;
select * from test1;
2 つのウィンドウで次の操作を時系列に実行します。
時間 | 窓A | ウィンドウB |
---|---|---|
T1 | トランザクションを開始します。 | |
T2 | test1 から * を選択します。 | |
T3 | トランザクションを開始します。 | |
T4 | test1 の値に挿入 (1); | |
T5 | test1 から * を選択します。 | |
T6 | test1 から * を選択します。 | |
T7 | 専念; | |
Q8 | test1 から * を選択します。 | |
T9 | 専念; |
ウィンドウは次のとおりです。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
Bウィンドウは次のとおりです。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
見てください:
T5-B: データあり、T6-A ウィンドウ: データなし、A は B のデータを見ることができず、ダーティ リードがないことを示します。
T6-A ウィンドウ: データなし、T8-A: B が挿入したデータを確認、B はこの時点でそれを送信しました。A は B が送信したデータを確認しました。つまり、送信されたデータは読み取れることを意味します。
T2-A、T6-A:データなし、T8-A:データあり 複数回の読み取り結果が異なり、繰り返し読み取りができないことを示します。
結論:Read Committedの場合、他のトランザクションで送信されていないデータは読み込めないが、他のトランザクションで送信されたデータは読み取れる、複数回読み込んだ結果は異なる、ダーティリードは発生しない、ただし読み取りはコミットされました。繰り返し読み取ることはできません。
REPEATABLE-READ: 反復可能な読み取り
分離レベルを次のように設定します。REPEATABLE-READ
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=REPEATABLE-READ
mysqlを再起動します。
C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
分離レベルを確認します。
mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name | Value |
+-----------------------+----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)
まず、test1 テーブルのデータをクリアします。
delete from test1;
select * from test1;
2 つのウィンドウで次の操作を時系列に実行します。
時間 | 窓A | ウィンドウB |
---|---|---|
T1 | トランザクションを開始します。 | |
T2 | test1 から * を選択します。 | |
T3 | トランザクションを開始します。 | |
T4 | test1 の値に挿入 (1); | |
T5 | test1 から * を選択します。 | |
T6 | test1 から * を選択します。 | |
T7 | 専念; | |
Q8 | test1 から * を選択します。 | |
T9 | 専念; | |
T10 | test1 から * を選択します。 |
ウィンドウは次のとおりです。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> select * from test1;
Empty set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 1 |
+------+
2 rows in set (0.00 sec)
Bウィンドウは次のとおりです。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+------+
| a |
+------+
| 1 |
| 1 |
+------+
2 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
見てください:
T2-A および T6-A ウィンドウ: データなし、T5-B: データ A は B のデータを見ることができず、ダーティ リードがないことを示します。
T8-A: データがありません。この時点で B が送信しました。A は、B によって送信されたデータを参照できません。A での 3 回の読み取りの結果は同じであり、データがないことを示し、読み取りを繰り返すことができることを示します。
結論: 反復読み取りの場合、ダーティ読み取りは発生せず、他のトランザクションによって送信されたデータは読み取られず、複数の読み取りの結果は一貫しているため、読み取りを繰り返すことができます。
ファントムリーディングのデモンストレーション
ファントム読み取りはREPEATABLE-READ
(反復読み取り) レベルでのみ発生するため、最初に分離レベルを反復読み取りに変更する必要があります。
分離レベルを次のように設定します。REPEATABLE-READ
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=REPEATABLE-READ
mysqlを再起動します。
C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
分離レベルを確認します。
mysql> show variables like 'transaction_isolation';
+-----------------------+----------------+
| Variable_name | Value |
+-----------------------+----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+----------------+
1 row in set, 1 warning (0.00 sec)
データを準備します:
mysql> create table t_user(id int primary key,name varchar(16) unique key);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t_user values (1,'路人甲Java'),(2,'路人甲Java');
ERROR 1062 (23000): Duplicate entry '路人甲Java' for key 'name'
mysql> select * from t_user;
Empty set (0.00 sec)
上記では、t_user テーブルを作成し、name に一意の制約を追加しました。これは、name を繰り返すことができないことを意味します。そうしないと、エラーが報告されます。
2 つのウィンドウで次の操作を時系列に実行します。
時間 | 窓A | ウィンドウB |
---|---|---|
T1 | トランザクションを開始します。 | |
T2 | トランザクションを開始します。 | |
T3 | -- 路人甲Java t_user 値に挿入 (1,'passer A Java'); |
|
T4 | select * from t_user; | |
T5 | -- 查看路人甲Java 是否存在select * from t_user where name='路人甲Java'; |
|
T6 | commit; | |
T7 | -- 插入路人甲Java insert into t_user values (2,'路人甲Java'); |
|
T8 | -- 查看路人甲Java 是否存在select * from t_user where name='路人甲Java'; |
|
T9 | commit; |
A窗口如下:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user where name='路人甲Java';
Empty set (0.00 sec)
mysql> insert into t_user values (2,'路人甲Java');
ERROR 1062 (23000): Duplicate entry '路人甲Java' for key 'name'
mysql> select * from t_user where name='路人甲Java';
Empty set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t_user values (1,'路人甲Java');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_user;
+----+---------------+
| id | name |
+----+---------------+
| 1 | 路人甲Java |
+----+---------------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
看一下:
A想插入数据路人甲Java
,插入之前先查询了一下(T5时刻)该用户是否存在,发现不存在,然后在T7时刻执行插入,报错了,报数据已经存在了,因为T6时刻B
已经插入了路人甲Java
。
然后A有点郁闷,刚才查的时候不存在的,然后A不相信自己的眼睛,又去查一次(T8时刻),发现路人甲Java
还是不存在的。
此时A心里想:数据明明不存在啊,为什么无法插入呢?这不是懵逼了么,A觉得如同发生了幻觉一样。
SERIALIZABLE:串行
SERIALIZABLE会让并发的事务串行执行。
看效果:
将隔离级别置为SERIALIZABLE
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行
transaction-isolation=SERIALIZABLE
重启mysql:
C:\Windows\system32>net stop mysql
mysql 服务正在停止..
mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .
mysql 服务已经启动成功。
查看隔离级别:
mysql> show variables like 'transaction_isolation';
+-----------------------+--------------+
| Variable_name | Value |
+-----------------------+--------------+
| transaction_isolation | SERIALIZABLE |
+-----------------------+--------------+
1 row in set, 1 warning (0.00 sec)
先清空test1表数据:
delete from test1;
select * from test1;
按时间顺序在2个窗口中执行下面操作:
时间 | 窗口A | 窗口B |
---|---|---|
T1 | start transaction; | |
T2 | select * from test1; | |
T3 | start transaction; | |
T4 | insert into test1 values (1); | |
T5 | select * from test1; | |
T6 | commit; | |
T7 | commit; |
按时间顺序运行上面的命令,会发现T4-B这样会被阻塞,直到T6-A执行完毕。
可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。
关于隔离级别的选择
-
需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
-
隔离级别越高,并发性也低,比如最高级别
SERIALIZABLE
会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。 -
具体选择哪种需要结合具体的业务来选择。
-
读已提交(READ-COMMITTED)通常用的比较多。
总结
-
理解事务的4个特性:原子性、一致性、隔离性、持久性
-
掌握事务操作常见命令的介绍
-
set autocommit
可以设置是否开启自动提交事务 -
start transaction:开启事务
-
start transaction read only:开启只读事物
-
commit:提交事务
-
rollback:回滚事务
-
savepoint:设置保存点
-
rollback to 保存点:可以回滚到某个保存点
-
掌握4种隔离级别及了解其特点
-
了解脏读、不可重复读、幻读