PostgreSQLを拡張して、最大1000ビットのビット文字列にインデックスを付けるようにしています。 (これらのビット文字列は高次元ベクトルの量子化によって作成されるため、各次元に対して最大4ビットが割り当てられます)。挿入はそれほど頻繁ではありませんが、検索は主に使用される操作です。検索で、ビット文字列と完全に一致するすべての行を取得したいと思います。
GIN(自分のデータ型と組み合わせて)にとっては完璧な仕事のようですが、どう思いますか?
検索では、ビット文字列と完全に一致するすべての行を取得したいと思います。
デフォルトのタイプであるBツリーインデックスを使用します。ここにGINインデックスのケースはありません。
_bit varying
_ type の場合、最大1000ビットは、ディスク上で最大133バイト(またはそれ以上)のストレージサイズになります。
_SELECT pg_column_size(repeat('1', 1000)::varbit) -- 133
_
それほどではない。プレーンなBツリーインデックスで十分です。しかし、おそらくカラムが十分に大きいため、次のトリックによってパフォーマンスが向上します。
ビットストリング列のごく一部が、検索をいくつかのヒットに絞り込むのに十分なほど特徴的である場合、 式のインデックスmightによりパフォーマンスが向上します。小さいインデックスはRAM=に収まり、すべての処理を高速化するためです。小さなテーブルを気にしないでください。オーバーヘッドによってメリットが失われます。しかし、大きなテーブル。
与えられたテーブル:
_CREATE TABLE tbl(id serial PRIMARY KEY, b_col varbit);
_
最初の10ビットで検索をいくつかのヒットに絞り込むのに十分な場合は、- 式のインデックスb_col::bit(10)
を作成できます。 bin(n)
にキャストすると、bitstring
がnビットに切り捨てられます。
_CREATE INDEX tbl_b_col10_idx ON tbl ((b_col::bit(10)))
_
インデックス定義のキャスト演算子には、追加の括弧が必要です。見る:
次に、クエリの代わりに
_SELECT * FROM tbl WHERE b_col = '1111011110111101'::varbit; -- 16 bit
_
あなたは使うでしょう:
_SELECT *
FROM tbl
WHERE b_col::bit(10) = '1111011110111101'::bit(10) -- utilize index
AND b_col = '1111011110111101'::varbit; -- filter to exact match
_
bit(n)
にキャストする場合、短い値にはright(最下位ビット)まで_0
_が埋め込まれることに注意してください。
実際のアプリケーションでは、これは数百のビットで意味をなし始めます。転換点をテストします。
ほとんどのインストールは8バイトのMAXALIGN
(64ビットOS)( 詳細はこちら )で動作するため、8バイトを超えないデータのインデックスサイズは同じです。効果的には、行ごとに:
4バイトのアイテムポインター インデックスタプルヘッダー用に8バイト(またはヒープタプル用に23 + 1バイト) ?データの実際のスペース ?最も近い8バイトの倍数へのパディング
さらに、ページおよびインデックス/テーブルごとに若干のオーバーヘッドがあります。詳細 マニュアル内 または SOに関するこの関連回答内 。
したがって、上記のアプローチをさらに最適化できるはずです。最初の64ビット(または最後または最も特徴的で機能するもの)をbigint
にキャストし、インデックスを作成しますこの表現について。
_CREATE INDEX tbl_b_col64_idx ON tbl ((b_col::bit(64)::bigint))
_
varbit
とbigint
の間にキャストが定義されていないため、2回キャストします(b_col::bit(64)::bigint
)。 SOに関するこの関連回答の詳細:
事実上、これは非常に高速で単純なハッシュ関数であり、ハッシュ値は値の範囲を検索することもできます。正確な要件に応じて、さらに一歩進んでanyIMMUTABLE
ハッシュ関数-md5()
などを使用できます。 上記のリンクの回答の詳細。
それに伴うクエリ:
_SELECT *
FROM tbl
WHERE b_col::bit(64)::bigint = '1111011110111101'::bit(64)::bigint -- utilize index
AND b_col = '1111011110111101'::varbit; -- narrow down to exact match
_
結果のインデックスは最初の例と同じ大きさになるはずですが、次の3つの理由により、クエリはかなり高速になるはずです。
インデックスは通常、はるかに少ないヒットを返します(64ビットの情報と10ビットの情報)
Postgresは整数演算を使用できます。これは、単純な_=
_操作の場合でも高速になります。 (それを確認するためのテストは行われませんでした。)
タイプinteger
には、varbit
- 5 or 8 bytes のようなオーバーヘッドはありません。 (私のインストールでは、960ビットまで5バイト、それ以上は8バイト)。
事実上、インデックスを最小サイズに保つには、24ビットのみをvarbit
インデックスにパックできます-bigint
インデックスの64ビットの情報。
CLUSTER
このような場合、CLUSTER
はパフォーマンスを向上させるはずです。
_CLUSTER TABLE tbl USING tbl_b_col10_idx;
_
これは1回限りの操作であり、設計の間隔で繰り返す必要があります。使用したい場合は、必ず CLUSTER
のマニュアルをお読みください。または、代替 pg_repack を検討してください。詳細:
値の最初の64ビットがほとんどの場合一意である場合、インデックススキャンはほとんどの場合単一行を返すため、CLUSTER
はほとんど役に立ちません。そうでない場合、CLUSTER
はたくさん助けます。その結果、最適化されたインデックスが少ない最初の例の効果ははるかに大きくなります。