mysql のデフォルトのトランザクション分離レベルは反復読み取り可能です
##4. 例を使用して各分離レベルを説明します
1 . Read uncommitted: (1) クライアント A を開き、現在のトランザクション モードを read uncommitted (非コミット読み取り) に設定し、テーブル アカウントの初期値をクエリします: # (2) クライアント A のトランザクションがコミットされる前に、別のクライアント B を開いてテーブル アカウントを更新します。
(3) この時点では、クライアントB のトランザクションはまだ送信されていないため、クライアント A は B の更新されたデータをクエリできます:
(4) クライアント B が何らかの理由でトランザクションがロールバックされると、その場合、クライアント A によってクエリされたデータは、実際にはダーティ データです:
(5) クライアント A で、更新ステートメントを実行します update account setbalance = Balance - id =1の50です。リレイの残高は350ではなく、実際は400です。おかしくありませんか。データが矛盾しています。そう思っているなら、あなたは甘すぎます。アプリケーションでは、400-50=350を使用します。 , そして、他のセッションがロールバックされたことはわかりません。この問題を解決するには、読み取りコミット分離レベル
2. コミットされた読み取り
(1) クライアント A を開き、現在のトランザクション モードをコミット済み読み取り (アンコミット読み取り) に設定し、テーブル アカウントの初期値をクエリします。
(2) クライアント A のトランザクションがコミットされる前に、別のクライアント B を開いてテーブル アカウントを更新します:
クライアント B のトランザクションはまだ送信されていません。クライアント A B の更新データをクエリできません。これにより、ダーティ リード問題が解決されます。
(4) クライアント B のトランザクション Submit
(5) クライアント A が前のステップと同じクエリを実行すると、結果が前のステップと矛盾します。つまり、反復不可能な読み取りの問題が発生します。 3 .Repeatable read (1) クライアント A を開き、現在のトランザクション モードをrepeatable readに設定し、テーブル アカウントをクエリします
(2) Beforeクライアント A のトランザクションが送信され、別のクライアント B を開き、テーブル アカウントを更新して送信します。
# # エンド A は、ステップ (1) のクエリを実行します。
# Aticbility:非反復性の読み取り問題が発生します。その後、更新バランス=バランス-50ここでid = 1で、残高は400-50 = 350になりません。 lileiのバランス値は(2)の350を使って計算されているので、データの整合性は崩れていない300になっています。これはちょっと不思議です。mysqlの機能なのかもしれません。dmlをする際に、データがテーブルの実データに従って繰り返し読み取ることができます
mysql> select * from account;
+------+--------+---------+
| id | name | balance |
+------+--------+---------+
| 1 | lilei | 400 |
| 2 | hanmei | 16000 |
| 3 | lucy | 2400 |
+------+--------+---------+
3 rows in set (0.00 sec)
mysql> update account set balance = balance - 50 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from account;
+------+--------+---------+
| id | name | balance |
+------+--------+---------+
| 1 | lilei | 300 |
| 2 | hanmei | 16000 |
| 3 | lucy | 2400 |
+------+--------+---------+
3 rows in set (0.00 sec)
(5) クライアントAの場合 トランザクションをサブミットし、テーブルアカウントの初期値をクエリします
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+------+--------+---------+
| id | name | balance |
+------+--------+---------+
| 1 | lilei | 300 |
| 2 | hanmei | 16000 |
| 3 | lucy | 2400 |
+------+--------+---------+
3 rows in set (0.00 sec)
(= 7 クライアントAの場合残高の合計を計算します。値は 300 16000 2400 = 18700 です。クライアント B の値は含まれません。クライアント A が送信した後、残高の合計が計算され、19300 になります。これは、クライアントの値がB が計算されます。B の 600 が含まれます
,站在客户的角度,客户是看不到客户端B的,它会觉得是天下掉馅饼了,多了600块,这就是幻读,站在开发者的角度,数据的 一致性并没有破坏。但是在应用程序中,我们得代码可能会把18700提交给用户了,如果你一定要避免这情况小概率状况的发生,那么就要采取下面要介绍的事务隔离级别“串行化”
mysql> select sum(balance) from account;
+--------------+
| sum(balance) |
+--------------+
| 18700 |
+--------------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select sum(balance) from account;
+--------------+
| sum(balance) |
+--------------+
| 19300 |
+--------------+
1 row in set (0.00 sec)
4.串行化
(1)打开一个客户端A,并设置当前事务模式为serializable,查询表account的初始值:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;
+------+--------+---------+
| id | name | balance |
+------+--------+---------+
| 1 | lilei | 10000 |
| 2 | hanmei | 10000 |
| 3 | lucy | 10000 |
| 4 | lily | 10000 |
+------+--------+---------+
4 rows in set (0.00 sec)
(2)打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account values(5,'tom',0);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
补充:
1、SQL规范所规定的标准,不同的数据库具体的实现可能会有些差异
2、mysql中默认事务隔离级别是可重复读时并不会锁住读取到的行
3、事务隔离级别为读提交时,写数据只会锁住相应的行
4、事务隔离级别为可重复读时,如果有索引(包括主键索引)的时候,以索引列为条件更新数据,会存在间隙锁间隙锁、行锁、下一键锁的问题,从而锁住一些行;如果没有索引,更新数据时会锁住整张表。
5、事务隔离级别为串行化时,读写数据都会锁住整张表
6、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,鱼和熊掌不可兼得啊。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
相关推荐:《mysql教程》