環境
分散アプリケーションからのデータを保存するデータベース(PostgreSQL 9.6)を設計しています。アプリケーションの分散された性質のSERIAL
ため、潜在的な競合状態のため、自動インクリメント整数()を主キーとして使用することはできません。
自然な解決策は、UUID、またはグローバルに一意の識別子を使用することです。Postgresには組み込みのUUID
typeが付属しており、これがぴったりです。
私がUUIDで抱えている問題は、デバッグに関連しています。それは人間に優しい文字列です。識別子ff53e96d-5fd7-4450-bc99-111b91875ec5
は何も教えてくれませんが、ACC-f8kJd9xKCd
が一意であるとは限りませんが、ACC
オブジェクトを扱っていることを教えてくれます。
プログラミングの観点からは、いくつかの異なるオブジェクトに関連するアプリケーションクエリをデバッグするのが一般的です。プログラマーACC
がORD
(order)テーブルで(account)オブジェクトを誤って検索するとします。人間が読み取れる識別子を使用して、プログラマーは問題を即座に特定しますが、UUIDを使用して、何が問題なのかを理解するのに少し時間を費やします。
UUIDの「保証された」一意性は必要ありません。私はない、競合なしで鍵を生成するためのいくつかの部屋を必要とするが、UUIDは過剰です。また、最悪のシナリオでは、衝突が発生した場合、世界の終わりにはなりません(データベースがそれを拒否し、アプリケーションが回復できます)。したがって、トレードオフを考慮して、より小さくても人間に優しい識別子が私のユースケースにとって理想的なソリューションになるでしょう。
アプリケーションオブジェクトの特定
私が思いついた識別子の形式は次のとおりです。{domain}-{string}
ここ{domain}
で、はオブジェクトドメイン(アカウント、注文、製品)に置き換えられ{string}
、ランダムに生成された文字列です。場合によっては{sub-domain}
、ランダムな文字列の前にaを挿入することも理にかなっています。レッツは、の長さを無視{domain}
し、{string}
一意性を保証する目的のために。
インデックス作成/クエリのパフォーマンスに役立つ場合、形式のサイズを固定できます。
問題
知っています:
- のような形式の主キーが必要です
ACC-f8kJd9xKCd
。 - これらの主キーは、いくつかのテーブルの一部になります。
- これらすべてのキーは、6NFデータベースのいくつかの結合/関係で使用されます。
- ほとんどのテーブルのサイズは、中規模から大規模(平均で最大100万行、最大で最大1億行)です。
パフォーマンスに関して、このキーを保存する最良の方法は何ですか?
以下に4つの解決策を示しますが、データベースに関する経験が少ないため、どれが最適かはわかりません。
考慮された解決策
1.文字列として保存(VARCHAR
)
(Postgresはの間に違いはありませんCHAR(n)
とVARCHAR(n)
、私は無視していますCHAR
)。
いくつかの調査の後VARCHAR
、特に結合操作での文字列比較は、を使用するよりも遅いことがわかりましたINTEGER
。これは理にかなっていますが、この規模で心配する必要があるのでしょうか?
2.バイナリとして保存(bytea
)
Postgresとは異なり、MySQLにはネイティブUUID
タイプがありません。BINARY
36 バイトのフィールドではなく、16バイトのフィールドを使用してUUIDを保存する方法を説明する投稿がいくつかありますVARCHAR
。これらの投稿は、キーをバイナリとして保存するというアイデアを与えてくれました(bytea
Postgresで)。
これによりサイズを節約できますが、パフォーマンスに関心があります。どの比較が高速であるかについての説明、つまりバイナリまたは文字列の説明を見つけることができなかった。バイナリ比較の方が速いと思います。もしそうであれば、プログラマは毎回データをエンコード/デコードする必要がありbytea
ますがVARCHAR
、おそらくの場合よりも優れています。
私は間違っているかもしれないが、私は両方だと思うbytea
とVARCHAR
、バイト(または文字単位)による(平等)のバイトを比較します。この段階的な比較を「スキップ」し、単に「全体」を比較する方法はありますか?(私はそうは思いませんが、チェックの費用はかかりません)。
として保存するのbytea
が最善の解決策だと思いますが、私が無視している他の選択肢があるのではないかと思います。また、ソリューション1で述べたのと同じ懸念が当てはまります。比較のオーバーヘッドは心配するほど十分ですか?
「クリエイティブ」ソリューション
動作する2つの非常に「創造的な」ソリューションを思い付きました。どの程度であるかわかりません(つまり、テーブル内で数千行以上にスケーリングするのが難しい場合)。
3. UUID
「ラベル」を付けて保存する
UUIDを使用しない主な理由は、プログラマーがアプリケーションをよりよくデバッグできるようにするためです。しかし、両方を使用できる場合:データベースはすべてのキーをUUID
s としてのみ格納しますが、クエリが実行される前/後にオブジェクトをラップします。
たとえば、プログラマはを要求しACC-{UUID}
、データベースはそのACC-
部分を無視し、結果を取得して、すべてをとして返します{domain}-{UUID}
。
おそらく、ストアドプロシージャまたは関数を使用したハッカーでこれが可能になるかもしれませんが、いくつかの質問が思い浮かびます。
- これ(各クエリでドメインを削除/追加する)はかなりのオーバーヘッドですか?
- これも可能ですか?
ストアドプロシージャや関数を使用したことがないため、これが可能かどうかもわかりません。誰かが光を当てることはできますか?プログラマと保存されたデータの間に透明なレイヤーを追加できれば、それは完璧なソリューションのようです。
4.(私のお気に入り)IPv6として保存 cidr
はい、あなたはそれを正しく読みました。IPv6アドレス形式は私の問題を完全に解決することがわかりました。
- 最初の数オクテットでドメインとサブドメインを追加し、残りをランダム文字列として使用できます。
- 衝突確率は OKです。(ただし、2 ^ 128は使用しませんが、それでも大丈夫です。)
- 等値比較は(できれば)最適化されているため、単にを使用するよりもパフォーマンスが向上する可能性があります
bytea
。 contains
ドメインとその階層がどのように表されるかに応じて、実際にいくつかの興味深い比較を実行できます。
たとえば0000
、ドメイン「製品」を表すためにコードを使用するとします。キー0000:0db8:85a3:0000:0000:8a2e:0370:7334
は製品を表します0db8:85a3:0000:0000:8a2e:0370:7334
。
ここでの主な質問はbytea
、と比較して、cidr
データ型を使用する上で主な利点または欠点はありますか?
varchar
他の多くの問題の1つです。pgのドメインについては知りませんでした。特定のクエリが正しいオブジェクトを使用しているかどうかを検証するためにドメインが使用されているように見えますが、それでも整数以外のインデックスを持つことに依存しています。serial
ここで使用する「安全な」方法があるかどうかはわかりません(1つのロックステップなし)。
varchar
。それをFK
integer
型にして、ルックアップテーブルを追加することを検討してください。そうすることで、人間が読みやすくなりPK
、挿入/更新の異常(存在しないドメインを置く)からコンポジットを保護できます。
ACC-f8kJd9xKCd
。」←それは古き良き複合PRIMARY KEYの仕事のようです。