🆔

主キー (user_idとか) にはユニークIDを使う

2023/10/01に公開

レガシーな Web アプリケーションではテーブル定義の ID に連番の数値を使っているケースが多い。フレームワークで一番流行った Ruby on Rails のデフォルトも AUTO_INCREMENT だった。
でも連番方式はスケールしないから、最近はみんなユニークIDをアプリケーション側で生成して INSERT して使ってると思う。

❌ AUTO_INCREMENTの問題点

AUTO_INCREMENT はスケーリングしない。トランザクションを使用した INSERT では内部的に連番を保つためのロックが発生するし、シャーディングが必要な大規模なシステムでは取り扱いに注意しないと ID の衝突や、ホットスポットの発生でパフォーマンス劣化を引き起こす。

また、AUTO_INCREMENT は連番であるため予測可能性があるという点でセキュリティ上の問題が発生しやすい。例えば user_id を指定するクエリパラメータがある場合、「数字増やせば別のユーザー情報みれるのかな?」とエンジニアなら誰でも試したくなると思う。

GET /api/users?user_id=12345

🆔 ユニークIDといえばUUID

ユニークIDの代表として UUID v4がある。Go では google/uuid というライブラリが有名。生成される ID は36文字と長く、冗長に感じる。

70d50e5d-ba50-4b9c-ad21-48846a07dd79
f72d9b3f-6f63-4454-bbe7-cd55dd51b8f9
0b50ee1e-196b-4690-9da0-d72d75812f57

テストデータとして擬似的に手入力で作るときにめんどいし、データベース見る時は横幅とるし、URL のクエリパラメータにあったりするとデバッグするとき余計むずかしく感じたりする。

🔢 ソート可能なユニークID

ユニークIDには生成アルゴリズムによっていろんな性質があり、必須ではないにしろソート可能な時系列順のユニークIDだと便利。

例えば SELECT * FROM users ORDER BY id DESC の指定だけで最新のユーザー登録順に取得できる。created_at でソートする必要がないから、その用途では新たなインデックスを貼る必要もない。それに ID を見比べればどちらのユーザーが新しいかわかるし「あ、こっちはかなり古いユーザーぽいな」みたいのも判断できる。

🎉 結論:rs/xidが便利

利便性の観点とパフォーマンスの観点の両方で rs/xid がとてもいい。生成されるユニークIDは20文字。短いしソートも可能。

9bsv0s11fg60037tqn50
9bsv0s11fg60037tqn5g
9bsv0s11fg60037tqn60

Discussion