データベースのPKに意味のある値を使うな
この記事はしおだいふく Advent Calendar 2020 2日目の記事です。
ユニークだからといって、データベースの主キーに業務上何らかの意味のある値を使うのはやめましょう。 Key-Valueストアなどのキー値にも意味のある値を使うのはやめましょう。
よくやりがちな失敗
さすがにモノの名前みたいななんらユニークである保証のない値を使うことはもうほとんどないとは思いますが、 業務上重複が発生しないとされている管理番号とか、メールアドレスとか電話番号とか、自分たちで定義したコード値や列挙型の文字列表現とかをうっかり主キーにしてしまう事例が散見されます。
業務上ユニークとされているものは高々UNIQUE制約をかける程度に留め、面倒でも無意味な識別符号を振りましょう。
なぜか
端的に言うと、業務上意味のある値は業務の都合に影響を受けるためです。
もともとユニークだと思っていたものがアプリケーション側の都合でユニークでなくなるくらいならカワイイもので、 いろいろな事情によりデータを公開していい範囲が変わったり、 セキュリティ上の都合で主キーとして使っていた値に暗号化が必要になったり、 といったようなイベントが数年に1回くらいの頻度で発生します。
これらの例に限らず、業務上意味のある値はなんかしらの理由で変更を余儀なくされることがありますので、 キーの値は識別符号以上の意味を持たない値を使うと将来不幸になる確率を下げることができます。
なにを使うのがいいか
要件が許すのであればUUIDをおすすめします。
とはいえ、UUIDを得るのは結構面倒です。 結構事情によりUUIDが使えない場合は、適当な長さのランダムな文字列がよいでしょう。 衝突が起こる可能性は否定できませんが、長い文字列であれば雑な疑似乱数で生成したとしてもその確率を十分に小さくすることができます。
アプリケーションのSLAにもよりますが、僕が設計するときは概ねID衝突の発生確率が十分に小さければいいでしょ、と考えています。 どうせ衝突が発生してエラーが起こるよりも先に油断した結果のヒューマンエラーとかインフラの不調で障害がおこるので、それに比べればコントロール可能な(≒発生が予期できている)事故は無視してもよいだろうと考えます。 不幸にも衝突を起こしてしまった場合にはエラーが出てだれかが処理をリトライすることにはなるでしょうが、 そのへんのハンドリングまで考えておくことを前提として、奇跡的に起こってしまったエラーは許容しようかなという気持ちです。
auto_incrementはどうなの?
衝突がどうしても嫌な場合は、数値をIDとしてauto_incrementなどのRDBMSが備えている機能をつかうこともできますが、個人的にはおすすめしません。 主な理由は次の通りです:
- データの増え方によっては整数型だと桁が足りなくなる可能性がある
- 昇順に並ぶのでソートにつかいたくなる
- 人間、特定の数値に意味を見出しがち1
仕事をしていると「偉い人が使うアカウントのために良い番号のIDを払い出してくれ!」みたいなバカバカしい依頼をたまに受けたりもします。 設計者が意味のない値だと思っていても勝手に意味を見つけてくる輩が後をたたないので、面倒なことをしたくなければユーザの目につく値とデータベースの都合でつかうID値は分けておくといいと思います。
もちろんUUIDやランダムな文字列でも同様の問題が発生する可能性はありますが、数値に比べると圧倒的に問題を回避しやすいので大丈夫でしょう。
まとめ
データベースのPKに意味のある値を使うな!!!頼む!!!
- 1桁の番号、ゾロ目、キリ番etc...↩