過去の人気記事:
1、《往期精选优秀博文都在这里了!》
2、为什么我不建议你用去 “ ! = null " 做判空?
3、这四种情况下,才是考虑分库分表的时候!
4、线上 4 台机器同一时间全部 OOM,到底发生了什么?
5、爽啊!Intellij IDEA 神器居然还藏着这些实用小技巧 !
往期热门文章:
1、《历史文章分类导读列表!精选优秀博文都在这里了!》
2、万亿级数据应该怎么迁移?
3、从应用到底层 36张图带你进入Redis世界
4、写代码有这16个好习惯,可以减少80%非业务的bug
5、顺丰快递:请签收MySQL灵魂十连
6、一个基于SpringBoot + MyBatis + Vue的代码生成器
7、Redis 分布式锁使用不当,超卖了100瓶飞天茅台!!!
8、如何设计订单系统?这篇写得太好了!
9、如果MySQL磁盘满了,会发生什么?还真被我遇到了!
10、阿里开源的27个项目,值得收藏!
主キーIDにAUTO_INCREMENT
属性を割り当てることに慣れていますが、AUTO_INCREMENT
非主キーフィールドに割り当てることもできます。唯一の制約は、このフィールドにインデックスを追加することです。インデックスを使用すると、この最大値をすばやく読み取ることができます。SELECT MAX(*
ai_col*)
値のようなステートメントを介したデータの列。
この記事で説明するトピックは、自己増加するデータ列を処理MySql
するInnoDB
エンジンの原理です。
MySql5.1より前の実装
このバージョンの前は、AUTO_INCREMENT
変更されたデータ列は確かに厳密に連続的で自己増加しています。MySql
実装では、挿入ステートメントごとに完全なテーブルディメンションロックを追加します。このロックにより、一度に1つの挿入ステートメントのみが実行され、データの各行が挿入され、自動インクリメントデータが生成されます。
mysql> CREATE TABLE t1 (
-> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> c2 CHAR(1)
-> ) ENGINE=InnoDB AUTO_INCREMENT=100;
上記のテーブルをデータベースに作成する場合は、挿入ステートメントを実行します。
mysql> INSERT INTO t1 (c1,c2) VALUES (NULL,'a'), (NULL,'b'), (NULL,'c'), (NULL,'d');
このMySql
実行のプロセスは次のとおりです。
プラスフルテーブル
AUTO-INC
ロック1.1主キーIDの生成:101
1.2行(101、 'a')をテーブルに挿入します
1.3主キーIDの生成:102
1.4行(102、 'b')をテーブルに挿入します
..。
リリース
AUTO-INC
ロック
MySql
5.1より前のこの実装ではAUTO_INCREMENT
、厳密な自己インクリメントを保証できますが、AUTO_INCREMENT
このステートメントが終了するまでロックはテーブルロック全体であるため、並行性の程度も最悪です。
MySql5.1バージョンによってもたらされる最適化
前の記事のinsert
ステートメントは比較的単純です。いわゆる単純insert
ステートメントは、事前に決定できる挿入されたデータ行の数を指します。対照的に、Bulk insert
たとえばINSERT ... SELECT
、そのような挿入ステートメントの挿入された行の数は、前進。
このバージョン以降では、単純なステートメントを挿入するために、テーブル全体のAUTO-INC
ロックは追加されなくなりました。自動インクリメント列データの生成時に軽量ミューテックスロックのみが追加されます。自動インクリメントデータが割り当てられた後、ロックが解除されるので、上記の例のようにMySql
、5.1以降の実行フローは次のようになります。
プラス軽量ミューテックス
1.1自己インクリメントデータの配布
ロックを解除する
行(101、 'a')をテーブルに挿入します
行(102、 'b')をテーブルに挿入します
..。
単純な挿入ステートメントの場合、同時実行の場合のクリティカルセクションが削減され、テーブル全体のロックが保持されなくなり、同時実行のパフォーマンスが向上することがわかります。もちろん、AUTO-INC
ロックしようとするプロセス中にテーブル全体のロックを保持している他のトランザクションに遭遇した場合でも、AUTO-INC
この挿入操作を実行する前に、テーブル全体のロックが解放されるのを待つ必要があります。
以下のためにBulk insert
INSERT文、グローバルAUTO-INC
ロックはまだ避けられない。そのような文の場合、それらの実行の流れは、このような例として、以下の表のように、5.1以前のバージョンと一致したまま
CREATE TABLE t1 (
c1 INT(11) NOT NULL AUTO_INCREMENT,
c2 VARCHAR(10) DEFAULT NULL,
PRIMARY KEY (c1)
) ENGINE=InnoDB;
次の2つのステートメントを実行します
Tx1: INSERT INTO t1 (c2) SELECT 1000 rows from another table ...
Tx2: INSERT INTO t1 (c2) VALUES ('xxx');
Tx1の実行時に挿入される特定の行数を知ることはInnoDB
不可能であるため、テーブル全体のロックが取得され、挿入ステートメントが実行されるたびに新しい値が自動インクリメント列に割り当てられます。テーブル全体がロックされているため、Tx1のこのステートメントによって挿入されたすべての行は継続的にインクリメントされ、Tx2インクリメント列の値はTx1インクリメント列の最小値よりも小さいか、Tx1インクリメント列の最大値よりも大きくなります。これら2つのステートメントの実行順序によって異なります
InnoDB
このような決定を行う重要な理由は、マスタースレーブレプリケーションですMySql
。8.0より前は、MySql
マスタースレーブレプリケーションはステートメントレプリケーションに基づいていました。今の例では、完全なテーブルロックなしでTx1を実行すると、Tx1の実行中にTx2も実行されている可能性があります。これにより、Tx1とTx2の自動インクリメント列のデータが毎回異なります。スレーブライブラリでのセンテンス再生ではコピーできません。
MySql8.0バージョン後の最適化
MySql
5.1バージョンでは、単純な挿入ステートメントを最適化してテーブル全体のロックを回避しますが、INSERT ... SELECT
このような複雑な挿入ステートメントの場合、テーブル全体のAUTO-INC
ロックを回避することはできません。主な理由は、実行されたステートメントのマスタースレーブレプリケーションを完全に行う必要があるためです。メインライブラリを再生してコピーすると、すべてのステートメントの実行結果を実行順序に関連付けることはできません。
ではMySql
8.0以降では、デフォルトのマスタースレーブのレプリケーション戦略は、データ行をもとになっています。このような状況において、INSERT ... SELECT
このような複雑なINSERT文は、自動インクリメント列データを生成するために、完全なテーブルロックを必要としません。すべてのステートメントインサートは唯一それが必要とされて生成され、列データを自動インクリメントするときに軽量ミューテックスロックを保持し、自動インクリメントデータが生成された後にロックを解放します。この実装では、すべての挿入ステートメントの自動インクリメント列は継続的な自動インクリメントを保証できませんが、並行性のパフォーマンスは確かに最高です。
総括する
挿入ステートメントが配置されているトランザクションがロールバックされる場合、生成された自動インクリメント列データはロールバックされないことに注意してください。この場合、自動インクリメント列データは非連続的に増加します。
上記は各MySql
バージョンのデフォルトの実装ですMySql
。5.1では新しいパラメータが導入されています。 innodb_autoinc_lock_mode
このフィールドの値を変更することで、InnoDB
自動インクリメント列を生成する戦略を変更できます。値は次のように要約されます。
auto update
- SELECT MAX(*
increment列データを明示的に指定することはお勧めしません。5.7以前のバージョンでは、ステートメントを介してai_colより大きい自動インクリメント列*)
値を明示的に指定すると、後続のinsert
ステートメントが「重複エントリ」エラーをスローする可能性があるためです。バージョン8.0以降、変更が加えられました。明示的なupdate
ステートメントをSELECT MAX(*
使用してai_colより大きい自動インクリメント列*)
値を明示的に指定すると、値は永続化され、後続の自動インクリメント列値がこの値から生成されます。 。
次の表がある場合
mysql> CREATE TABLE t1 (
-> c1 INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> c2 CHAR(1)
-> ) ENGINE = INNODB AUTO_INCREMENT=100;
次のステートメントを実行した後、テーブルの内容がどうなるか想像してみてください。
mysql> INSERT INTO t1 (c1,c2) VALUES (1,'a'), (NULL,'b'), (5,'c'), (NULL,'d');
MySql 5.1より前、またはinnodb_autoinc_lock_mode
0に設定
mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
+-----+------+
| c1 | c2 |
+-----+------+
| 1 | a |
| 101 | b |
| 5 | c |
| 102 | d |
+-----+------+
このモードでは、データの行が挿入されるたびに、自己インクリメント値が生成されてc1
この行に割り当てられるためc1
、次の自己インクリメント値は103になります。
MySql 8.0より前、またはinnodb_autoinc_lock_mode
1に設定
mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
+-----+------+
| c1 | c2 |
+-----+------+
| 1 | a |
| 101 | b |
| 5 | c |
| 102 | d |
+-----+------+
現在のテーブルのデータは前のシナリオと一致していますが、このシナリオでは、挿入ステートメントの実行の開始時に自己インクリメントデータが生成されるため、次の自己インクリメントは105です。
MySql 8.0以降、またはinnodb_autoinc_lock_mode
2に設定
mysql> SELECT c1, c2 FROM t1 ORDER BY c2;
+-----+------+
| c1 | c2 |
+-----+------+
| 1 | a |
| x | b |
| 5 | c |
| y | d |
+-----+------+
このシナリオでは、他の挿入ステートメントの実行がある可能性がありますが、次の値が不明であるためx
、y
値が不確実であるためです。