2016年10月23日日曜日

自然キー vs 人工キー 勝手に決着

RDB界隈で時折バトル議論のネタになる「主キーには自然キーと人工キーのどちらを使うべきか?」というテーマについて。

「人工キーのほうがデータ量が少ないし高速だからこれでいいじゃん」
「いやいや、原則は自然キーで、人工キーはどうしようもないときにだけ使うべきだ」
「理論的には自然キーを使うべきなんだろうけど、効率とか考えたら現実的には人工キー使っちゃうよね」

現実を優先する人、原理原則を大事にする人、原則を尊重しつつも現実との折り合いをつける人…立場は様々ですが、これについて思うことが少々あるのでいつになく真面目に語ってみます。

まず用語を整理しようか。

自然キー、ナチュラルキー、人工キー、サロゲートキー、代理キー、複合キー…いろいろ出てきて混乱しますし、実際に混乱(混同)している人も少なくないのであらためて整理してみましょう。
  • 自然キー・ナチュラルキー
    • 要求仕様に含まれている情報のみで構成された一意な情報
    • 人間でいうと…氏名は同姓同名があるしDNA情報は一卵性双生児は同じだから…指紋情報あたり?数値化が難しそうだけど。
  • 人工キー・アーティフィシャルキー
    • 要求仕様とは関係なく、テーブル設計者が人工的につけた一意な情報 
    • 連番だったりUUIDだったり
  • 代替キー・サロゲートキー
    • 人工キーと同じような扱いを受けている
    • 名前からすると下の代理キーに近いような気がする
    • 代理キーとサロゲートキーを同じという人もいれば人工キーのことをサロゲートキーと呼ぶ人もいる
  • 代理キー・オルタネートキー
    • 主キーに選ばれなかった一意な情報
    • 人工キーのことを代理キーと読んでいる人がたまにいるけどこれは誤り
    • 代理キーに要求仕様の情報しか含まれていないこともある
  • 複合キー
    • 複数の情報を組み合わせて一意になる情報
    • 都市でいうと、市区郡の名前だけだと他の都道府県とかぶる可能性があるから「都道府県」「市区郡」を組み合わせて一意になるのでこれが複合キー
    • 自然キーのことを複合キーとか複合主キーと呼ぶ人もいるけど誤り。自然キーは複合キーになりがちだけど指紋情報なんかは単一で自然キーになる。
    • 関係ないけど「複合キー」を変換すると「複合機ー」になるのがいやだ
とりあえずここでは「自然キー」と「人工キー」に焦点を当てます。

特にサロゲートキーについては混乱してきたのでこれ以降使いません。

おまいはどっち派だよ

人工キー派

です。

「難しいことは置いといて、人工キーのほうがいろいろ楽だよね」でも「理論的には自然キーが正しいけど、現実的には人工キーを使うべきだよね」でもなく、「理論的にも現実的にも人工キーを使うべきだよね」という立場です。

もう少し詳しくいうと、原理原則が云々というレベルの話ではなく、それ以前の「主キーの要件を満たせるのは理論的に人工キーしかありえないよね?」という話。

主キーの要件

そもそも主キーが満たすべき要件として、一般的に以下の2つが言われています。
  1. 一意であること
  2. 不変であること
他にも「一意である以外の意味を持たないこと」のような条件が加わったりしますが、それを入れるとさらに人工キー派が勢いづきます。

一意であること

これについては説明の必要はないでしょう。主キーの定義の問題です。一意じゃなければ主キーじゃない。

不変であること

主キーの値が変更されると整合性を保つのが難しい場合があります。外部キー制約を使えばそのテーブル間では整合性はとれるでしょうが、
  • 外部キー制約を使えないRDBMSを使っている
  • 主キーが外部システムに記録されている
という状況では整合性を保つことは困難です。

前者の例は、MySQLのMyISAMストレージエンジン。MyISAMはトランザクションもサポートしていないので、さらにハードルが上がります。

後者の例は、複数のウェブサービス間でRPCやAPIを使ってマッシュアップしている場合。あるAPIで使われている主キーが変更された場合、利用者側の修正を保証するのは非常に困難です。

主キーが変わった場合に整合性を保証するのが無理ゲーなら、発想を変えて絶対に変わらないと保証できる値を主キーにすればいい。これが「不変であること」の意味です。

はたして絶対に変わらないと保証できる自然キーというものは存在するのでしょうか。
最初に書いた指紋の例なんかはWikipediaによると
人や指ごとに「紋様」は全て異なり、終生不変という特徴を持つと言われている
だそうですが、あくまで「現時点」で「同一の指紋を持つ別人」や「指紋が変化した人物」が見つかっていないというだけで、厳密に証明されているわけではありません。そもそも自然科学は経験則の集大成であって、証明できるものではありません。

商品なら型番とかEANコード(JANコード)なら変わらないんじゃない?と思った方!甘いですよ…
全ての商品にEANコードがついているとは限りませんし、「EANコードは後で取得するからとりあえず商品情報をシステムに登録しておこう」という場合もあります。
さらに言えば、以前作ったシステムでは型番すら後から変更させろ!という注文がありました。

一方、人工キーは絶対に変わりません。そもそも変える必要性がありません。自然キーよりはるかに簡単に、そして確実に不変性を保証できます。

3つ目の条件

上記の2つだけではなく、大事な3つ目の条件があります。

上記2点が、どのように仕様が変更されようとも保証されること

クライアントからどんなムチャクチャな仕様変更を受けようが、「一意であること」「不変であること」の2点が保証されなければいけません。

必然的に、仕様とは無関係の値を主キーにせざるを得ません。
それってつまり人工キーのことですよね。

自然キーにこだわろうとすると

自然キー派には
「安易に人工キーをつけるのは設計をサボっている証拠だ。きちんと仕様を精査して自然キーを突き止め、どうしても見つからないときにだけ人工キーをつけるべきだ」
という人もいるかもしれません。

しかし、実際のシステム開発の現場を想像してみてください。

予算も人手も足りないのに納期だけはしっかり決まっている。

コロコロ変わる仕様。

そもそも依頼元がふわっとしたイメージしかない。

そんな状態で「もうこれ以上仕様は変わりようがない!」という状態まで落とし込んだ後で開発を始められるでしょうか。

これにYESと答えられる人は、よほどのやり手かモンスタークライアントに遭ったことがないかのどちらかでしょう。
そういう方は、ぜひ某メガバンクの炎上案件をきっちり仕様を定義して鎮火していただきたいです。

むしろ、仕様が完全に確定するまでテーブルを設計しないのではなく、仕様が変わることを前提として、それに対応できる設計にすべきです。

いやいや、それでも自然キーであるべき!という方は、どのように仕様が変更されても一意・不変を保証できる自然キーの例を1つでもいいから挙げていただきたい。

そういう例は思い浮かばないどそもそもリレーショナルモデルの理論として云々…というなら、それは理論的に存在しないものを前提として理論立てているのであって、理論自体が最初から破綻しているということに他なりません。

現実的な話

以上が「主キーは人工キーにすべき」 という主張の理論的な理由ですが、実際にシステムを作るとパフォーマンス面等でやはり自然キーではツライという場面がいくつかあります。
  • 自然キーは文字列になることが多いので、検索が遅くなりがち&インデックスサイズが肥大しがち
  • MySQL(InnoDB)ではセカンダリインデックスに主キーの値がつくので、主キーがデカイとそちら側にも影響が出る
DBのプライマリキーは複合主キーではなく、サロゲートキーを使うべし | IsaB
私個人の結論としては、データベースは人ではなく、システムが応答し易いように作った方が良く、パフォーマンスを犠牲にしてまで人間側のデータ視認性にこだわるのは本末転倒だと思いますね。人間側に認識し易いデータは別途中継処理等を作って整形してあげるのがデータベースを利用するシステムの正しいカタチではないかと。
という意見に近いです。この場合の中継処理というのは例えばシステム管理画面のようなものでしょう。きっと。

おまけ

人工キーといってもオートインクリメント値とかUUIDとかあるけど何を使えばいいの?という問題については、システムの特性に合わせればいいんじゃないかと思ってます。

例えば、MySQLのInnoDBで頻繁にINSERTするような場合では、常に増加していくほうが効率的に処理できるのでオートインクリメントがいいとか。

例えば、分散システムだけどオートインクリメント値を1箇所で生成していてそれがボトルネックになるような場合は各ノードで勝手に生成できるUUIDがいいとか。そんなシステムあるのか知りませんしそもそも分散の意味がありませんが…

オートインクリメント値だと「何番目のレコードか」ってのがわかるから意味がある情報になっちゃうよ!と気になる場合はUUIDがいいんでしょうけど、DBMSによってはUUIDを効率的に格納できるデータ型(UUID ver4なら128bit整数)がなかったり、挿入する値が単調増加・単調減少でないとフラグメンテーションが発生して効率が悪かったりすることがあるのでそのへんはシステムと相談すればよろしいかと。

0 件のコメント:

コメントを投稿