不可能 |
| #5. 小さな試み
1. グローバルまたはセッションのトランザクション分離レベルを確認します
SELECT @@global.tx_isolation, @@tx_isolation;
2. グローバルまたはセッションのトランザクション分離レベルを変更します
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
6. MySQL のデフォルトは反復読み取り分離レベルです。論理的に言えば、ファントム読み取り問題は解決できません?
以下では、まずデータベースに関係するロックについて紹介します。
7. ロックの基本的な説明
1. ロックの概要
データベースのロックとは、ユーザー (プロセス セッション) が特定のデータ リソースを占有するのを制御および防止するために使用されるソフトウェア メカニズムを指します。これは、ユーザーのデータ操作に影響を与えるか、データの非整合性や非一貫性の問題を引き起こすことを意味します。
2. ロック レベル
ロック レベルに応じて、ロックは共有ロックと排他ロックに分類できます。
同じデータに対して、複数の読み取り操作を相互に影響を与えることなく同時に実行できます。共有ロックは UPDATE 中にのみロックされます。UPDATE 操作が送信される前は、他のトランザクションは最新のレコードを取得することしかできませんが、UPDATE 操作を実行することはできません。
現在の書き込み操作が完了する前に、他の書き込みロックと読み取りロックをブロックします。
3. ロックの粒度
ロックの粒度に従って、ロックはテーブル レベルのロック、行レベルのロック、ページ レベルのロックに分類できます。
オーバーヘッドが大きい、ロックが遅い、デッドロックが発生する、ロック強度が最も小さい、ロック競合の可能性が最も低い、同時実行性が高いです。
オーバーヘッドが小さく、ロックが高速で、デッドロックが発生せず、ロック力が強く、競合の可能性が高く、同時実行性が低いです。
オーバーヘッドとロック時間はテーブル ロックと行ロックの間であり、デッドロックが発生します。ロック強度はテーブル レベル ロックと行レベル ロックの間です。その間の同時実行性は平均的です。
8. 悲観的ロックと楽観的ロック
8.1 悲観的ロック
1. 基本的な考え方: 常に最悪のシナリオを想定し、データを取得するたびにそれを考える その他変更するため、データを取得するたびにロックされます。このようにして、他の人がデータを取得しようとしても、ロックを取得するまでブロックされます (共有リソースは一度に 1 つのスレッドによってのみ使用されます) 、他のスレッドはブロックされます。使用後はブロックされます。リソースは他のスレッドに転送されます)。このようなロック メカニズムの多くは、行ロック、テーブル ロック、読み取りロック、書き込みロックなど、従来のリレーショナル データベースで使用されており、これらはすべて操作前にロックされるため、実際に競合が発生するかどうかに関係なく、ロックが使用されます。機構。
2. 悲観的ロック機能:
- 読み取りレコードをロックして、他のトランザクションがこれらのレコードを読み取り、更新できないようにします。このトランザクションが終了するまで、他のトランザクションはブロックされます。
- 悲観的ロックは、データベースのトランザクション分離機能の使用と占有リソースの排他的使用に基づいて、読み取りデータの一貫性を確保し、変更の損失を回避します。
- 悲観的ロックでは、悲観的ロックの要件を完全に満たす反復読み取りトランザクションを使用できます。
8.2 楽観的ロック
1. 基本的な考え方: 常に最良の状況を想定します。データを取得しに行くたびに、他の人がそのデータを変更しないと考えます。ロックはしませんが、更新時には、この期間中に他の人がこのデータを更新したかどうかが判断され、これは バージョン番号メカニズム と CAS アルゴリズム を使用して実装できます。オプティミスティック ロックは、マルチ読み取りアプリケーション タイプに適しており、スループットを向上させることができます。
2. 説明: オプティミスティック ロックはアイデアです。オプティミスティック ロックは何もロックしません。つまり、データベースのトランザクション メカニズムに依存しません。 . 、オプティミスティック ロックは完全にアプリケーション システム レベルで行われます。したがって、これはロック機構ではありません。オプティミスティックロックを使用する場合、データベースはバージョンフィールドを追加する必要があり、そうでない場合はすべてのフィールドを比較することしかできませんが、浮動小数点型は比較できないため、実際にはバージョンフィールドなしでは実行できません
8.3 バージョン番号のメカニズム
通常、データ バージョン番号 version フィールドは、データが変更された回数を示すためにデータ テーブルに追加されます。データが変更されると、バージョン値が表示されます。 1ずつ増加します。スレッド A がデータ値を更新する場合、データの読み取り中にバージョン値も読み取ります。更新を送信するときは、読み取ったバージョン値が現在のデータベースのバージョン値と等しい場合にのみ更新し、それ以外の場合は再試行します。 . アップデートが成功するまでアップデート操作を行います。
8.4 CAS アルゴリズム
1. 中心となるアイデア: Compare and Swap、つまり、比較してから交換します。
2. プロセス: スレッド A がメモリ内の name という名前の変数の値を変更しようとしていると仮定すると、スレッド A は前に読み取った name 変数の値とこの時点の name の値を比較します。それらが同じであるということは、変数値が変更されていないため、更新および変更できますが、それ以外の場合は更新が失敗することを意味します。
9. MySQL の反復読み取りトランザクションに戻る分離レベル
前述したように、MySQL はデフォルトで反復可能読み取りトランザクション分離レベルを実装していますが、ファントム リードの問題は解決できません。ただし、反復可能読み取りを使用する MySQL データベースのトランザクション分離条件では、ファントム リードが発生します。 MySQL は MVCC (Multi-version Concurrency) Control を使用します) が制御されました。
9.1名词简析:
1.MVCC:是multiversion concurrency control的简称,也就是多版本并发控制,是个很基本的概念。MVCC的作用是让事务在并行发生时,在一定隔离级别前提下,可以保证在某个事务中能实现一致性读,也就是该事务启动时根据某个条件读取到的数据,直到事务结束时,再次执行相同条件,还是读到同一份数据,不会发生变化(不会看到被其他并行事务修改的数据)。
2.read view:InnoDB MVCC使用的内部快照的意思。在不同的隔离级别下,事务启动时(有些情况下,可能是SQL语句开始时)看到的数据快照版本可能也不同。在上面介绍的几个隔离级别下会用到 read view。
3.快照读: 就是所谓的根据read view去获取信息和数据,不会加任何的锁。
4.当前读:前读会获取得到所有已经提交数据,按照逻辑上来讲的话,在一个事务中第一次当前读和第二次当前读的中间有新的事务进行DML操作,这个时候俩次当前读的结果应该是不一致的,但是实际的情况却是在当前读的这个事务还没提交之前,所有针对当前读的数据修改和插入都会被阻塞,主要是因为next-key lock解决了当前读可能会发生幻读的情况。
next-key lock当使用主键索引进行当前读的时候,会降级为record lock(行锁)
9.2 Read view详析
InnoDB支持MVCC多版本控制,其中READ COMMITTED和REPEATABLE READ隔离级别是利用consistent read view(一致读视图)方式支持的。所谓的consistent read view就是在某一时刻给事务系统trx_sys打snapshot(快照),把当时的trx_sys状态(包括活跃读写事务数组)记下来,之后的所有读操作根据其事务ID(即trx_id)与snapshot中trx_sys的状态做比较,以此判断read view对事务的可见性。
REPEATABLE READ隔离级别(除了GAP锁之外)和READ COMMITTED隔离级别的差别是创建snapshot时机不同。REPEATABLE READ隔离级别是在事务开始时刻,确切的说是第一个读操作创建read view的时候,READ COMMITTED隔离级别是在语句开始时刻创建read view的。这就意味着REPEATABLE READ隔离级别下面一个事务的SELECT操作只会获取一个read view,但是READ COMMITTED隔离级别下一个事务是可以获取多个read view的。
创建/关闭read view需要持有trx_sys->mutex,会降低系统性能,5.7版本对此进行优化,在事务提交时session会cache只读事务的read view。
9.3 read view 判断当前版本数据项是否可见
在InnoDB中,创建一个新事务的时候,InnoDB会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。当用户在这个事务中要读取该行记录的时候,InnoDB会将该行当前的版本号与该read view进行比较。
具体的算法如下:
设该行的当前事务id为trx_id,read view中最早的事务id为trx_id_min, 最迟的事务id为trx_id_max。
如果trx_id如果trx_id>trx_id_max的话,那么表明该行记录所在的事务在本次新事务创建之后才开启,所以该行记录的当前值不可见。
如果trx_id_min
从该行记录的DB_ROLL_PTR指针所指向的回滚段中取出最新的undo-log的版本号的数据,将该可见行的值返回。
需要注意的是,新建事务(当前事务)与正在内存中commit 的事务不在活跃事务链表中。
在具体多版本控制中我们先来看下源码:
函数:read_view_sees_trx_id。
read_view中保存了当前全局的事务的范围:
【low_limit_id, up_limit_id】
1.当行记录的事务ID小于当前系统的最小活动id,就是可见的。
if (trx_id up_limit_id) {
return(TRUE);
}
2.当行记录的事务ID大于当前系统的最大活动id(也就是尚未分配的下一个事务的id),就是不可见的。
if (trx_id >= view->low_limit_id) {
return(FALSE);
}
3.当行记录的事务ID在活动范围之中时,判断是否在活动链表中,如果在就不可见,如果不在就是可见的。
for (i = 0; i <p><strong>Read view 图解</strong>:<br></p><p><img src="https://img.php.cn/upload/image/428/162/665/1553653110536794.jpg" title="1553653110536794.jpg" alt="MySQL データベースのトランザクション分離と MVCC の詳細な紹介 (画像とテキスト)"></p><p style="max-width:90%">结语:笔者水平有限,文中如有不妥,请大家多多指教,MySQL数据库事务机制还有很多需要深入研究的,我们仍需不断钻研。</p><p>本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的<a href="http://www.php.cn/course/list/51.html" target="_blank">MySQL视频教程</a>栏目!</p><p></p>