InnoDBでのINSERT IGNOREとAUTO_INCREMENT

InnoDBでINSERT IGNOREを繰り返してたら、レコード数は増えてないのに、
AUTO_INCREMENTだけがガンガン増えててびっくり。おかげでidの値が飛んでる。

なんなんだろうと思ったらこれ、MySQL 5.1.22以降の仕様なんですね。

以下、マニュアルから引用。強調はおいら。

簡単に言えば、このロックモードの重要な効果は、スケーラビリティーの大幅な向上です。このモードは、ステートメントベースのレプリケーションで使用しても安全です。さらに、「従来」 ロックモードの場合と同じく、任意のステートメントによって割り当てられた自動インクリメント番号が「連続」した値になります。このモードでは 「従来」 モードと比較して、ある重要な例外を除けば、自動インクリメントを使用する任意のステートメントでの意味上の「違い」はありません。

その例外とは、ユーザーが複数行 「単純挿入」 の全部ではなく一部の行で AUTO_INCREMENT カラムの明示的な値を提供する 「混在モード挿入」 の場合です。そのような挿入では、InnoDB は挿入行数よりも多くの自動インクリメント値を割り当てます。ただし、自動的に割り当てられる値はすべて連続的に生成されるため、直前に実行されたステートメントによって生成された自動インクリメント値よりも値が大きくなります。「余分」 な番号は失われます。

INSERT … ON DUPLICATE KEY UPDATE を使用する場合にも似たような状況になります。このステートメントも 「混在モード挿入」 として分類されていますが、これは、自動インクリメント値が必ずしもすべての行で生成されないからです。
 

なるほど。



この仕様の主眼は、AUTO_INCREMENTの値を増やすか増やさないかと言うことではなくて、
AUTO_INCREMENT時のロックを高速化することのようなので、これはこれで使い道があるんだろうと。
ちゃんと解ってれば有用なんでしょうね。

というかそうか、AUTO_INCREMENTでINSERTが遅くなるんだったら、
何でもかんでもAUTO_INCREMENTフィールド作るのは良くないかな。
今まではどのテーブルもほぼ必ず一意の整数を持つ「id」フィールドを作ってきたんだけど。
(リレーションするのに都合が良いので)

話を戻して、現在あるテーブルでAUTO_INCREMENTを無駄に増やさないようにしたい場合には、
以下の変更を行う必要があります。

InnoDB は実際に挿入が試みられる前に自動インクリメント値を割り当てるため、挿入する値が既存の値と重複しているかどうかを知ることができず、したがって自身が生成する自動インクリメント値が新しい行で使用されるかどうかを知ることができません。したがって、ステートメントベースのレプリケーションを使用する場合には、INSERT … ON DUPLICATE KEY UPDATE を使用しないようにするか、あるいは innodb_autoinc_lock_mode = 0 (「従来」 ロックモード) を使用する必要があります。
 


「従来」 ロックモードを利用する方法は次の内のどちらか。

  • 「–innodb_autoinc_lock_mode=0」オプションを付けて起動
  • my.cnfのmysqldディレクティブに「innodb_autoinc_lock_mode=0」と記述

参考:
MySQL :: MySQL 5.1 リファレンスマニュアル (オンラインヘルプ) :: 9.3 InnoDB スタートアップオプションとシステム変数


これでMySQLを再起動すればOK。




ただまぁ既に割り振られたIDのことを考えると、見た目美しくない以外に特に問題はないし、
振り直そうと思うと色々とだるいのでこれはこれで諦めることにする…んだったら、
結果的にはこの設定自体やらなくてもいいんじゃね?という話も。

まぁそうなんだけどね。高速の方が良いしね。戻すか。