web-dev-qa-db-ja.com

MD5フィールドに最適なデータ型は何ですか?

私たちは、読み取りが重いことがわかっているシステム(1分あたり数万回の読み取りのオーダー)を設計しています。

  • ある種の中央レジストリとして機能するテーブルnamesがあります。各行には、textフィールドrepresentationと、そのkeyのMD5ハッシュである一意のrepresentationがあります。1 このテーブルには現在数千万のレコードがあり、アプリケーションの存続期間中に数十億に成長すると予想されています。
  • namesテーブルを参照する他のテーブル(スキーマとレコード数が非常に変化する)が数十あります。これらのテーブルのいずれかの特定のレコードは、_name_key_を持つことが保証されています。これは、namesテーブルへの外部キーです。

1:ちなみに、ご想像のとおり、このテーブルのレコードは、いったん書き込まれると不変です。

namesテーブル以外のテーブルでは、最も一般的なクエリは次のパターンに従います。

_SELECT list, of, fields 
FROM table 
WHERE name_key IN (md5a, md5b, md5c...);
_

読み取りパフォーマンスを最適化したいと思います。私が最初にやるべきことは、インデックスのサイズを最小化することだと思います(ただし、そこで間違っていることが証明されてもかまいません)。

質問:
key列と_name_key_列の最適なデータ型は何ですか?
hex(32)よりもbit(128)を使用する理由はありますか? BTREEまたはGIN

35
bobocopy

データ型 uuidperfectlyタスクに適しています。 varcharまたはtextの表記では、RAMの37バイトではなく、16バイトしか占有しません。 (またはディスク上の33バイトですが、奇数の場合、多くの場合、効果的にパディングが必要になります40バイト。)そして、uuid型には、いくつかの利点があります。

例:

_SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash
_

詳細と詳細説明:

Md5の暗号化コンポーネントが必要ない場合は、他の(より安価な)ハッシュ関数を検討するかもしれませんが、私はmd5を使用して(ほとんどは読み取り専用)使用します。

警告の言葉:(_immutable once written_)の場合機能依存(疑似自然)PKで結構です。しかし、同じことがpainであり、textの更新が可能です。タイプミスを修正することを考えてください。PKとすべての依存インデックス、_dozens of other tables_のFK列、およびその他の参照も変更する必要があります。テーブルとインデックスの膨張、ロックの問題、遅い更新、失われた参照、...

textが通常の操作で変更される可能性がある場合は、 surrogate PK を選択することをお勧めします。 bigserial 列を提案します(範囲_-9223372036854775808 to +9223372036854775807_-これは 9千兆223兆3000兆30兆36何か)億 )_billions of rows_の個別の値。 anyの場合は良い考えかもしれません:16バイトの代わりに8数十のFK列とインデックスの場合!)または ランダムなUUIDより大きなより大きなカーディナリティまたは分散システムの場合。上記のmd5を常に(uuidとして)additionallyに保存して、元のテキストからメインテーブルの行をすばやく検索できます。関連:

あなたの場合query


対処するには @ Danielのコメント :ハイフンなしの表現が必要な場合は、表示用にハイフンを削除します。

_SELECT replace('90b7525e-84f6-4850-c2ef-b407fae3f271', '-', '')
_

しかし、私は気にしません。デフォルトの表現で十分です。そして、問題は本当にここでの表現ではありません。

他の当事者が異なるアプローチをとり、ハイフンなしの文字列をミックスに投げる必要がある場合、それも問題ではありません。 Postgresは、uuidの入力としていくつかの適切なテキスト表現を受け入れます。 ドキュメント

PostgreSQLは、入力に次の代替形式も受け入れます。大文字の数字の使用、標準フォーマットは中括弧で囲み、ハイフンの一部またはすべてを省略し、4桁のグループの後にハイフンを追加します。次に例を示します。

_A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
a0eebc999c0b4ef8bb6d6bb9bd380a11
a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}
_

さらに、md5()関数はtextを返します。 decode() を使用してbyteaに変換し、thatは:

_SELECT decode(md5('Store hash for long string, maybe for index?'), 'hex')

\220\267R^\204\366HP\302\357\264\007\372\343\362q
_

元のテキスト表現を取得するには、もう一度encode()を実行する必要があります。

_SELECT encode(my_md5_as_bytea, 'hex');
_

さらに、byteaとして保存された値は、RAMに20バイト(およびディスクに17バイト、 24 with padding )を占める 内部varlenaオーバーヘッド これは、単純なインデックスのサイズとパフォーマンスにとって特に好ましくありません。

すべては、ここではuuidを優先して機能します。

44

MD5をtextまたはvarchar列に格納します。さまざまな文字データ型の間にパフォーマンスの違いはありません。 md5値が特定の長さを超えないようにするために、varchar(xxx)を使用してmd5値の長さを制限したい場合があります。

大きなINリストは通常​​、実際には高速ではありません。次のようなことを行う方が良いでしょう。

with md5vals (md5) as (
  values ('one'), ('two'), ('three')
)
select t.*
from the_table t
  join md5vals m on t.name_key  = m.md5;

時々より高速であると言われるもう一つのオプションは配列を使うことです:

select t.*
from the_table t
where name_key = ANY (array['one', 'two', 'three']);

等しいかどうかを比較しているだけなので、通常のBTreeインデックスで十分です。両方のクエリは、このようなインデックスを利用できる必要があります(特に、行のごく一部しか選択していない場合)。

別のオプションは、4つのINTEGERまたは2つのBIGINT列を使用することです。

0
happy_marmoset