web-dev-qa-db-ja.com

MySQLの自動インクリメントが失敗した挿入で増加するのはなぜですか?

同僚が、MySQLの非常に奇妙な動作に気付いたところです。

Auto_incrementフィールドと一意に設定された別のフィールド(ユーザー名フィールドなど)を含むテーブルがあるとします。テーブルにすでに存在するユーザー名の行を挿入しようとすると、期待どおりに挿入が失敗します。それでも、auto_incrementの値は増加していますが、何回か失敗した後で有効な新しいエントリを挿入するとわかります。

たとえば、最後のエントリが次のようになっている場合...

ID: 10
Username: myname

...次の挿入時に同じユーザー名の値を持つ5つの新しいエントリを試し、次のように新しい行を作成します。

ID: 16
Username: mynewname

これ自体は大きな問題ではありませんが、MySQLリファレンスマニュアルに記載されているように、失敗した挿入リクエストでテーブルをフラッディングすることでテーブルを強制終了することは、非常に愚かな攻撃のように見えます。

「値が指定された整数型に格納できる最大整数より大きくなる場合、自動インクリメントメカニズムの動作は定義されません。」

これは予想される動作ですか?

45
Sorcy

InnoDBはトランザクションエンジンです。

これは、次のシナリオでは、

  1. Session Aはレコードを挿入します1
  2. Session Bはレコードを挿入します2
  3. Session Aはロールバックします

、ギャップの可能性があるか、session Bsession Aをコミットまたはロールバックするまでロックされます。

InnoDBデザイナー(他のほとんどのトランザクションエンジンデザイナーと同様)は、ギャップを許可することを選択しました。

ドキュメント から:

自動インクリメントカウンターにアクセスする場合、InnoDBは、トランザクションの最後ではなく、現在のSQLステートメントの最後まで保持する特別なテーブルレベルのAUTO-INCロックを使用します。 。 AUTO_INCREMENT列を含むテーブルへの挿入の同時実行性を向上させるために、特別なロック解放戦略が導入されました

InnoDBは、サーバーが実行されている限り、メモリ内の自動インクリメントカウンターを使用します。前述のように、サーバーが停止して再起動すると、InnoDBは最初のINSERTの各テーブルのカウンターをテーブルに再初期化します。

id列の折り返しが怖い場合は、BIGINT(8バイト長)にしてください。

37
Quassnoi

正確な内部構造がわからなければ、そうだと思います。自動インクリメントは、挿入の失敗に対してスキップされた値を許可する必要があります(SHOULD)。銀行取引など、トランザクション全体と複数のレコードがオールオアナッシングとして処理されているとします。挿入を試み、IDを取得してから、そのトランザクションIDを使用して後続のすべての詳細にスタンプを付け、詳細レコードを挿入する場合は、修飾された一意性を確保する必要があります。複数の人がデータベースを悪用している場合、それらの人も、トランザクションがコミットされたときに自分と競合しないように、自分のトランザクションIDを取得する必要があります。最初のトランザクションで何かが失敗しても、害はなく、ダウンストリームのぶら下がり要素はありません。

5
DRapp

古い投稿ですが、これは人々を助けるかもしれません、あなたは innodb_autoinc_lock_mode0または2に設定する必要があるかもしれません。

数値を取るシステム変数は、コマンドラインでは--var_name=valueとして、オプションファイルではvar_name=valueとして指定できます。

コマンドラインパラメータの形式:

--innodb-autoinc-lock-mode=0 

[〜#〜]または[〜#〜]mysql.iniを開き、次の行を追加します。

innodb_autoinc_lock_mode=0
3
DavidA