C Sharpens you up

http://qiita.com/yuba に移しつつあります

DBの自動連番がロールバックしても戻らない理由

DBの主キーカラムに自動連番(MySQLならAUTO_INCREMENTSQL ServerならIDENTITYOraclePostgreSQLならシーケンス)を設定していると、基本的には連続した数字が付番されていくのですが、行削除もしていないのに抜け番が発生する場合があります。

などの場合です。

特にトランザクションロールバックは、DB全体の変更をなかったことにするはずなのに連番だけは進んだまま戻らないということで、知らないと違和感があるものです。

連番が戻らない理由

PostgreSQLのドキュメントがこう説明しています。

同一のシーケンスから数値を取得する同時実行トランザクション同士のブロックを防止するため、nextval演算は決してロールバックされません。

http://www.postgresql.jp/document/9.3/html/functions-sequence.html

nextval演算とはシーケンスの数字を一個進めつつ取得する操作のことです。シーケンスが戻らないことの目的はトランザクション同士のブロックを防止するためだと明言しています。

つまりこういうことです。

もしシーケンスがロールバックされうるとしたら、シーケンスを使ってINSERTしているトランザクションが実行中だった場合、それが終了するまではシーケンスが次に何の数字を吐き出すか確定できません。すると、他のトランザクションが単に連番を生成するためだけにブロックされてしまうのです。
RDBの世界で主キーに期待されていることはユニークなことだけであり、連続であることは期待されていません(どうせDELETEで抜け番が発生しますから)。期待されてもいない連続性のためにブロックが発生するのはあり得ないというのがRDB製品共通の解釈だというわけです。

どうしても連続性が必要とされているビジネス要件があるなら、ブロッキングな連番生成をストアドプロシージャで自力記述しましょうねということになります。その場合は行削除で発生する欠番についても合意しておく必要がありますけどね。欠番は詰めることなどという要件はアンチパターン21番「疑似キー潔癖症」です。そういう「ID」は主キーではなく表示名やメールアドレスなどと同列のユニークなだけの値として格納するべきです。

ユニークな値と論理削除の共存については次の記事も参考にどうぞ。