今日お見せしたいのは、Mysqlトランザクション分離の分離レベルです。
トランザクションの4つの主要な特徴を簡単に紹介します
InnoDBトランザクションの原則
- トランザクション(Transaction)は、データベースとファイルシステムを区別する重要な特性の1つです。トランザクションは、データベースを1つの整合性状態から別の整合性状態に変換します。
- データベースが送信されると、すべての変更が保存されたか、すべての変更が保存されていないかを確認できます。
トランザクション(ACID)の特性
- 原子性:全体のすべての操作が正常に送信されるか、すべて失敗してロールバックされます(部分的な実行は行われません)。
- 一貫性:並行して実行されるいくつかのトランザクションの場合、実行結果は、特定の順序でのシリアル実行の結果と一致している必要があります。
- 分離(分離):トランザクションの実行は他のトランザクションによって干渉されず、トランザクションの実行の中間結果は他のトランザクションに対して透過的でなければなりません。
- 耐久性:トランザクションがコミットされると、データベース内のデータへの変更は永続的です。データベースシステムで障害が発生しても、トランザクションをコミットする操作は失われません。
トランザクション分離レベル
-
コミットされていない読み取り:ダーティリード(READ UNCOMMITTED)コミットされていない読み取り
-
読み取りの送信:繰り返し不可の読み取り(読み取りコミット)
-
反復可能読み取り:ファントム読み取り(REPEATABLE READ):これはMySQLのデフォルトのトランザクション分離レベルです
-
シリアル読み取り可能(SERIALIZABLE)シリアル化およびシリアル読み取り
この分離レベルでは、以前に発生したダーティリード、繰り返し不可の読み取り、およびファントムリードの問題を解決できますが、多くのタイムアウトやロック競合も発生します。通常、
√発生する、×発生しない
分離レベル | ダーティリード | 繰り返し不可 | ファントムリーディング |
---|---|---|---|
コミットされていない読み取り(コミットされていない読み取り) | √ | √ | √ |
コミットされた読み取り | × | √ | √ |
反復可能な読み取り | × | × | √ |
シリアライズ可能(シリアライズ可能) | × | × | × |
表格借鉴的Java识堂(https://blog.csdn.net/weixin_44685869/article/details/104105291)
この記事で使用されるいくつかのMySQLコマンド
# 查看 MySQL 版本
select version()
# 查看 MySQL 隔离级别
SELECT @@tx_isolation
# MySQL在会话层面设置隔离级别
set session transaction isolation level 隔离级别名字
# 开启事务
start transaction
# 提交事务
commit
# 回滚事务
rollback
1234567891011121314151617
ライブラリ、テーブル、ビューを作成します。
# 创建 demo01 数据库
create database demo01
use demo01
# 创建测试表
create table test01(
id int(3) not null primary key auto_increment,
name varchar(64) default null,
price int(7) default 0 # 这里不许有逗号
)ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
1234567891011
初期データを挿入する
insert into test01(name,price) values('张三',100),('李四',0);
1
一切ok 开始干活
ダーティリード
表のデータは次のとおりです。分離レベルを非コミット読み取りに設定します
時間 | クライアントA(タブA) | クライアントB(タブB) |
---|---|---|
T1 | コミットされていないセッショントランザクション分離レベルの読み取りを設定します;トランザクションを開始します; test01を更新しますset value = price + 100 where id = 1; select * from test01 where id = 1;コミットされていない読み取りとして設定し、Zhang Sanアカウント+100出力は200 | |
T2 | セッショントランザクションの分離レベルをコミットせずに読み取り、トランザクションを開始、select * from test01、id = 1を選択、クエリバランス出力は200 | |
T3 | ロールバック | |
T4 | コミット | |
T5 | select * from test01 where id = 1;クエリバランス出力は100 |
ダーティリードとは、トランザクションがデータにアクセスしていて、データが変更され、この変更がまだデータベースにコミットされていない場合を指します。このとき、別のトランザクションもデータにアクセスし、データを使用します。
危険を証明する別の深刻な例を挙げてください
。表のデータは次のとおりです
時間 | クライアントA(タブA) | クライアントB(タブB) |
---|---|---|
T1 | コミットされていないセッショントランザクション分離レベルの読み取りを設定します。トランザクションを開始します。test01セットの価格を更新します=価格-100ここでid = 1; test01セットの更新価格=価格+ 100、ここでid = 2; | |
T2 | コミットされていないセッショントランザクション分離レベルの読み取りを設定します。トランザクションを開始します。id = 2であるtest01から価格を選択します。test01 set price = price-100を更新します。ここでid = 2です。更新语句被阻塞 | |
T3 | ロールバック | |
T4 | コミット |
実行が完了すると、データベース内のデータは次のようになります
時間 | 説明 |
---|---|
T1 | 1から2まで100 |
T2 | 2. 2の残高は100元を購入するのに十分であり、更新ステートメントはブロックされます |
T3 | 1はロールバックし、1の残高は100になり、2の残高は0になります |
T4 | 2控除成功、バランス0-100 = -100 |
今のところ大丈夫です、銀行は理由もなく100元を失いました。
繰り返し不可
テーブル内のデータは次のとおりです。分離レベルをコミット読み取りに設定します
時間 | クライアントA(タブA) | クライアントB(タブB) |
---|---|---|
T1 | コミットされたセッショントランザクション分離レベルの設定、トランザクションの開始、select * from test01、id = 2の選択、クエリバランス出力は0 | |
T2 | セッショントランザクションの分離レベルを読み取りコミットに設定します。トランザクションを開始します。test01セットの更新価格=価格+ 100、ここでid = 2; select * from test01 where id = 2; コミット; 查询余额输出100 | |
T3 | select * from test01 where id = 2; commit; check balance output 100 |
繰り返し不可の読み取りとは、トランザクション1でデータの一部が読み取られ、トランザクション1が終了していない場合、トランザクション2もデータにアクセスし、データを変更して送信したことを意味します。その直後、トランザクション1がこのデータを再度読み取ります。トランザクション2の変更により、トランザクション1で2回読み取られたデータは異なる場合があるため、非反復可能読み取りと呼ばれます。
当然你可以在T2时间段客户端B修改完id=2的账户余额但没有commit的时候,在客户端A查询id=2的账户余额,发现账户余额为0,可以证明提交读这个隔离级别不会发生脏读。
可重复读级别
看一下可重复读是个什么过程?
表中的数据如下,设置隔离级别为可重复读
时间 | 客户端A(Tab A) | 客户端B(Tab B) |
---|---|---|
T1 | set session transaction isolation level repeatable read; start transaction; select * from test01 where id = 2; 查询余额输出为0 | |
T2 | set session transaction isolation level repeatable read; start transaction; update test01 where set price = price + 100 where id = 2; select * from test01 where where id = 2; commit; 查询余额输出100 | |
T3 | select * from test01 where where id = 2; commit; 查询余额输出0 |
当我们将当前会话的隔离级别设置为可重复读的时候,当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。
但是在可重复读的隔离级别上,会产生幻读的问题。
幻读
设置隔离级别为可重复读
所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。
用大白话解释一下,就是事务1查询id<10的记录时,返回了2条记录,接着事务2插入了一条id为3的记录,并提交。接着事务1查询id<10的记录时,返回了3条记录,说好的可重复读呢?结果却多了一条数据。
演示如何解决的幻读,表中的数据如下
MySQL通过 MVCC 解决了这种情况下的幻读
MVCC 在我理解 就是类似于乐观锁
时间 | 客户端A(Tab A) | 客户端B(Tab B) |
---|---|---|
T1 | set session transaction isolation level repeatable read; start transaction; select count(*) from test01 where id <= 10; 输出2 | |
T2 | set session transaction isolation level repeatable read; start transaction; insert into test01 (id, name, price) values (3, “王五”, 0); select count(*) from test01 where id <= 10; commit; 输出3 | |
T3 | select count(*) from test01 where id <= 10; commit; 输出2 |
这种情况下的幻读被解决了,我再举一个例子
表中的数据如下
时间 | 客户端A(Tab A) | 客户端B(Tab B) |
---|---|---|
T1 | set session transaction isolation level repeatable read; start transaction; select count(*) from account where id = 3; 输出为0 | |
T2 | set session transaction isolation level repeatable read; start transaction; insert into account (id, name, balance) values (3, “王五”, 0); commit; | |
T3 | アカウント(ID、名前、残高)の値に挿入(3、 "王五"、0);主キーの複製、挿入の失敗 | |
T4 | id = 3のアカウントからcount(*)を選択します;出力は0です | |
T5 | ロールバック; |
レコードが存在するか、存在しないか、およびこのレコードを挿入する準備ができているかどうかを選択しますが、挿入を実行すると、レコードがすでに存在していて挿入できないことがわかります。これは問題です。
多くの人は、繰り返し不可の読みと幻の読みを混同する傾向があります。ただし、繰り返し不可の読み取りは更新と削除に重点が置かれ、ファントム読み取りは挿入に重点が置かれます。
一般に、幻像読み取りはトランザクションAがデータを操作することを意味します。行ロックが使用されるため、トランザクションBは引き続き挿入を使用してデータを挿入できます。これによって引き起こされるさまざまな奇妙な問題は幻像読み取りです。多くの兆候があるため、それらはリストしません。 。
分離レベルがシリアライズ可能に設定されている場合、トランザクションは強制的にシリアルに実行され、前述のファントムリードの問題が回避されます。
参照元のアドレス:https://blog.csdn.net/zzti_erlie/article/details/88080822
データベースはロックを使用して、より優れた並行性をサポートし、データの整合性と一貫性を提供します。
InnoDBは、行ロックをサポートするストレージエンジンです。ロックのタイプは次のとおりです。
- 共有ロック(S)
- 排他ロック(X)
- 意図共有(IS)
- 排他的意図(IX)
より良い並行性を提供するために、InnoDBは非ロック読み取りを提供します。アクセス行のロックが解放されるのを待つ必要はなく、行のスナップショットを読み取ります。このメソッドは、InnoDBの機能であるMVCCを介して実装されます。
InnoDBには3つの行ロックアルゴリズムがあります。
- レコードロック:単一行レコードのロック。
- ギャップロック:ギャップロック、範囲をロックしますが、レコード自体は含まれません。GAPロックの目的は、同じトランザクションの現在の2つの読み取りがファントム読み取りから防ぐことです。
- Next-Key Lock:1 + 2、範囲をロックし、レコード自体をロックします。行クエリの場合、このメソッドが使用されます。主な目的は、ファントム読み取りの問題を解決することです。