web-dev-qa-db-ja.com

PostgreSQLビット文字列のインデックス作成(それぞれ最大20,000ビット)

化合物(数百万行)を含むテーブルを作成しています。これらの化合物のうち、特定の所定の機能/フラグメントに固定長のビット文字列でフラグが立てられています。このビット文字列には2000〜20000ビットが含まれるため、そこでより正確な数値を決定するには、さらに調査を行う必要があります。特定の特定の機能がある、または特定の機能がない化合物を検索する場合、このビット文字列の選択したサブセットで検索が行われます。これは毎回異なるサブセットになる可能性があります。

PostgreSQL(9.6または10)でこれらの検索を効率的にできるインデックスタイプはありますか?

挿入はまれで、バッチプロセスで行われますが、検索は最もよく使用される操作であり、できれば迅速で、誤検出や誤検出がないことが望ましいです。

私にはこれは漠然とGINインデックスの作業のように聞こえますが、このインデックスタイプについての私の理解では、それが本当に当てはまるかどうかを確認するには不十分です。

実際には別の解決策がある可能性があります。それは、フラグメント識別子(ビット文字列に固定位置があるため、数値識別子も持つ)+化合物IDのペアを含む個別の 'fragment_index'テーブルを作成することです。私の心配は、そのテーブルが巨大になり(フラグメントで平均50ヒットの20Mコンパウンド= 1G行)、それに対する複数の結合(フラグメントごとに1つ)があり、結合がコンパウンドテーブルとの一致の最大80%を返す可能性があることです。 (場合によってはこれが発生する可能性があります)、まったく機能しません。

私は、これを道路上で得る方向に向けて何か提案をいただければ幸いです。

更新:コード化されたショートコードを含むvarchar配列でtrigramモジュールを使用してGINインデックスを試しましたが、主にフィルター操作後に残ったデータの量に応じて、さまざまな結果が得られました。

意味のある例を示すために、テーブルが次のようになっているとします。

CREATE TABLE testcompounds (
 id serial primary key,
 cd_structure text,
 features_as_text varchar(128),
 features_as_bits bit varying(32)
);

CREATE INDEX flags_testcompounds on testcompounds using gin (features_as_text gin_trgm_ops);


CREATE TABLE fragments (
 id serial primary key,
 smarts text,
 keystring varchar(4),
 frequency int 
);


insert into fragments (keystring,smarts) values('AAA', '*=O');
insert into fragments (keystring,smarts) values('AAB', '[#6]=O');
insert into fragments (keystring,smarts) values('AAC', '[#7]=O');
...
insert into fragments (keystring,smarts) values('AAN', '[#6]-F');
insert into fragments (keystring,smarts) values('AAO', '[#6]-Cl');
insert into fragments (keystring,smarts) values('AAP', '[#6]-Br');
...
etc.

そしてfeatures_as_textとfeatures_as_bitsフィールドは完全に入力されています。

これに対して実行できるクエリの例は次のとおりです。

select id, cd_structure from testcompounds 
where 
    (features_as_bits & (B'00000000000000000000000000000001' << (2-1)) = (B'00000000000000000000000000000001' << (2-1))) 

AND (features_as_bits & (B'00000000000000000000000000000001' << (18-1)) = (B'00000000000000000000000000000000')) 
AND (features_as_bits & (B'00000000000000000000000000000001' << (19-1)) = (B'00000000000000000000000000000000')) 
AND (features_as_bits & (B'00000000000000000000000000000001' << (5-1)) = (B'00000000000000000000000000000000')) 
AND (features_as_bits & (B'00000000000000000000000000000001' << (6-1)) = (B'00000000000000000000000000000000')) 
AND (features_as_bits & (B'00000000000000000000000000000001' << (7-1)) = (B'00000000000000000000000000000000')) 
AND (features_as_bits & (B'00000000000000000000000000000001' << (8-1)) = (B'00000000000000000000000000000000')) 
AND (features_as_bits & (B'00000000000000000000000000000001' << (9-1)) = (B'00000000000000000000000000000000')) 

つまり、機能2があり、機能18、19、5、6、7、8、9がないものをすべて取得します。

4

ビット演算のインデックス戦略をいくつかテストしました。少し時間がかかります。

まず第一に、

  • これは、私のRDS環境でのテストにすぎません。データベースのパフォーマンスは、環境とデータ(CPU、メモリ、ディスク、テーブルの行数、列のカーディナリティなど)によって異なります。
  • PostgreSQLに慣れていません。特に、ブルームインデックスのどのビット長が最適かわかりません。 (誰かが私を助けてくれませんか?)
  • 一般に、インデックスサイズは小さい方が望ましいです。したがって、テーブルのパーティション分割は良い解決策になるかもしれません。

環境


  • バージョン:PostgreSQL 10.3

  • ホスト:Amazon RDS db.m4.largeの新しいインスタンス(vCPU:2、メモリ:8GB、ディスク:GP2-SSD 100GB)

テーブル/インデックスの定義


拡張機能を作成する

CREATE EXTENSION IF NOT EXISTS intarray;
CREATE EXTENSION IF NOT EXISTS bloom;

テーブルを作成

CREATE TABLE IF NOT EXISTS t_bitwise (
  id BIGINT
  ,c_int INTEGER
  ,c_bit BIT(31)
  ,c_int_arr _int4
  ,c_bool_0 BOOLEAN ,c_bool_1 BOOLEAN ,c_bool_2 BOOLEAN ,c_bool_3 BOOLEAN ,c_bool_4 BOOLEAN
  ,c_bool_5 BOOLEAN ,c_bool_6 BOOLEAN ,c_bool_7 BOOLEAN ,c_bool_8 BOOLEAN ,c_bool_9 BOOLEAN
  ,c_bool_10 BOOLEAN ,c_bool_11 BOOLEAN ,c_bool_12 BOOLEAN ,c_bool_13 BOOLEAN ,c_bool_14 BOOLEAN
  ,c_bool_15 BOOLEAN ,c_bool_16 BOOLEAN ,c_bool_17 BOOLEAN ,c_bool_18 BOOLEAN ,c_bool_19 BOOLEAN
  ,c_bool_20 BOOLEAN ,c_bool_21 BOOLEAN ,c_bool_22 BOOLEAN ,c_bool_23 BOOLEAN ,c_bool_24 BOOLEAN
  ,c_bool_25 BOOLEAN ,c_bool_26 BOOLEAN ,c_bool_27 BOOLEAN ,c_bool_28 BOOLEAN ,c_bool_29 BOOLEAN
  ,c_bool_30 BOOLEAN
  ,PRIMARY KEY (id)
);

各ブール列に1つのbtreeインデックスを作成します

CREATE INDEX IF NOT EXISTS idx_btree_on_bool_1 ON t_bitwise (c_bool_1);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_2 ON t_bitwise (c_bool_2);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_3 ON t_bitwise (c_bool_3);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_4 ON t_bitwise (c_bool_4);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_5 ON t_bitwise (c_bool_5);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_6 ON t_bitwise (c_bool_6);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_7 ON t_bitwise (c_bool_7);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_8 ON t_bitwise (c_bool_8);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_9 ON t_bitwise (c_bool_9);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_10 ON t_bitwise (c_bool_10);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_11 ON t_bitwise (c_bool_11);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_12 ON t_bitwise (c_bool_12);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_13 ON t_bitwise (c_bool_13);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_14 ON t_bitwise (c_bool_14);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_15 ON t_bitwise (c_bool_15);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_16 ON t_bitwise (c_bool_16);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_17 ON t_bitwise (c_bool_17);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_18 ON t_bitwise (c_bool_18);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_19 ON t_bitwise (c_bool_19);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_20 ON t_bitwise (c_bool_20);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_21 ON t_bitwise (c_bool_21);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_22 ON t_bitwise (c_bool_22);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_23 ON t_bitwise (c_bool_23);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_24 ON t_bitwise (c_bool_24);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_25 ON t_bitwise (c_bool_25);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_26 ON t_bitwise (c_bool_26);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_27 ON t_bitwise (c_bool_27);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_28 ON t_bitwise (c_bool_28);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_29 ON t_bitwise (c_bool_29);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_30 ON t_bitwise (c_bool_30);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_31 ON t_bitwise (c_bool_31);
CREATE INDEX IF NOT EXISTS idx_btree_on_bool_32 ON t_bitwise (c_bool_32);

Bool列に1つの複合btreeインデックスを作成する

CREATE INDEX IF NOT EXISTS idx_btree_on_composite_bool ON t_bitwise (
  c_bool_32 ,c_bool_31
  ,c_bool_30 ,c_bool_29 ,c_bool_28 ,c_bool_27 ,c_bool_26 ,c_bool_25 ,c_bool_24 ,c_bool_23 ,c_bool_22 ,c_bool_21
  ,c_bool_20 ,c_bool_19 ,c_bool_18 ,c_bool_17 ,c_bool_16 ,c_bool_15 ,c_bool_14 ,c_bool_13 ,c_bool_12 ,c_bool_11
  ,c_bool_10 ,c_bool_9 ,c_bool_8 ,c_bool_7 ,c_bool_6 ,c_bool_5 ,c_bool_4 ,c_bool_3 ,c_bool_2 ,c_bool_1
)
;

ブール列に1つのブルームインデックスを作成する

CREATE INDEX IF NOT EXISTS idx_bloom_on_bool ON t_bitwise
  USING bloom (
    CAST(c_bool_1 AS INTEGER) ,CAST(c_bool_2 AS INTEGER) ,CAST(c_bool_3 AS INTEGER) ,CAST(c_bool_4 AS INTEGER) ,CAST(c_bool_5 AS INTEGER)
    ,CAST(c_bool_6 AS INTEGER) ,CAST(c_bool_7 AS INTEGER) ,CAST(c_bool_8 AS INTEGER) ,CAST(c_bool_9 AS INTEGER) ,CAST(c_bool_10 AS INTEGER)
    ,CAST(c_bool_11 AS INTEGER) ,CAST(c_bool_12 AS INTEGER) ,CAST(c_bool_13 AS INTEGER) ,CAST(c_bool_14 AS INTEGER) ,CAST(c_bool_15 AS INTEGER)
    ,CAST(c_bool_16 AS INTEGER) ,CAST(c_bool_17 AS INTEGER) ,CAST(c_bool_18 AS INTEGER) ,CAST(c_bool_19 AS INTEGER) ,CAST(c_bool_20 AS INTEGER)
    ,CAST(c_bool_21 AS INTEGER) ,CAST(c_bool_22 AS INTEGER) ,CAST(c_bool_23 AS INTEGER) ,CAST(c_bool_24 AS INTEGER) ,CAST(c_bool_25 AS INTEGER)
    ,CAST(c_bool_26 AS INTEGER) ,CAST(c_bool_27 AS INTEGER) ,CAST(c_bool_28 AS INTEGER) ,CAST(c_bool_29 AS INTEGER) ,CAST(c_bool_30 AS INTEGER)
    ,CAST(c_bool_31 AS INTEGER) ,CAST(c_bool_32 AS INTEGER)
  ) WITH (length=128)
;

Int列に1つのブルームインデックスを作成する

CREATE OR REPLACE FUNCTION bitcheck_int(INTEGER, INTEGER)
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE
SECURITY INVOKER
PARALLEL SAFE
COST 0.01
AS $$
  SELECT CASE
    WHEN $1 & (1 << ($2 - 1)) > 0 THEN $2
    ELSE -$2
  END
$$
;
CREATE INDEX IF NOT EXISTS idx_bloom_on_int ON t_bitwise
  USING bloom (
    bitcheck_int(c_int, 1)
    ,bitcheck_int(c_int, 2)
    ,bitcheck_int(c_int, 3)
    ,bitcheck_int(c_int, 4)
    ,bitcheck_int(c_int, 5)
    ,bitcheck_int(c_int, 6)
    ,bitcheck_int(c_int, 7)
    ,bitcheck_int(c_int, 8)
    ,bitcheck_int(c_int, 9)
    ,bitcheck_int(c_int, 10)
    ,bitcheck_int(c_int, 11)
    ,bitcheck_int(c_int, 12)
    ,bitcheck_int(c_int, 13)
    ,bitcheck_int(c_int, 14)
    ,bitcheck_int(c_int, 15)
    ,bitcheck_int(c_int, 16)
    ,bitcheck_int(c_int, 17)
    ,bitcheck_int(c_int, 18)
    ,bitcheck_int(c_int, 19)
    ,bitcheck_int(c_int, 20)
    ,bitcheck_int(c_int, 21)
    ,bitcheck_int(c_int, 22)
    ,bitcheck_int(c_int, 23)
    ,bitcheck_int(c_int, 24)
    ,bitcheck_int(c_int, 25)
    ,bitcheck_int(c_int, 26)
    ,bitcheck_int(c_int, 27)
    ,bitcheck_int(c_int, 28)
    ,bitcheck_int(c_int, 29)
    ,bitcheck_int(c_int, 20)
    ,bitcheck_int(c_int, 31)
    ,bitcheck_int(c_int, 32)
  ) WITH (length=128)
;

ビット列に1つのブルームインデックスを作成する

CREATE OR REPLACE FUNCTION bitcheck_bit(BIT(32), INTEGER)
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE
SECURITY INVOKER
PARALLEL SAFE
COST 0.01
AS $$
  SELECT CASE
    WHEN $1::INTEGER & (1 << ($2 - 1)) > 0 THEN $2
    ELSE -$2
  END
$$
;
CREATE INDEX IF NOT EXISTS idx_bloom_on_bit ON t_bitwise
  USING bloom (
    bitcheck_bit(c_bit, 1)
    ,bitcheck_bit(c_bit, 2)
    ,bitcheck_bit(c_bit, 3)
    ,bitcheck_bit(c_bit, 4)
    ,bitcheck_bit(c_bit, 5)
    ,bitcheck_bit(c_bit, 6)
    ,bitcheck_bit(c_bit, 7)
    ,bitcheck_bit(c_bit, 8)
    ,bitcheck_bit(c_bit, 9)
    ,bitcheck_bit(c_bit, 10)
    ,bitcheck_bit(c_bit, 11)
    ,bitcheck_bit(c_bit, 12)
    ,bitcheck_bit(c_bit, 13)
    ,bitcheck_bit(c_bit, 14)
    ,bitcheck_bit(c_bit, 15)
    ,bitcheck_bit(c_bit, 16)
    ,bitcheck_bit(c_bit, 17)
    ,bitcheck_bit(c_bit, 18)
    ,bitcheck_bit(c_bit, 19)
    ,bitcheck_bit(c_bit, 20)
    ,bitcheck_bit(c_bit, 21)
    ,bitcheck_bit(c_bit, 22)
    ,bitcheck_bit(c_bit, 23)
    ,bitcheck_bit(c_bit, 24)
    ,bitcheck_bit(c_bit, 25)
    ,bitcheck_bit(c_bit, 26)
    ,bitcheck_bit(c_bit, 27)
    ,bitcheck_bit(c_bit, 28)
    ,bitcheck_bit(c_bit, 29)
    ,bitcheck_bit(c_bit, 20)
    ,bitcheck_bit(c_bit, 31)
    ,bitcheck_bit(c_bit, 32)
  ) WITH (length=128)
;

Int配列列に1つのGINインデックスを作成します

CREATE INDEX IF NOT EXISTS idx_gin_on_int_arr ON t_bitwise
  USING GIN(c_int_arr gin__int_ops)
;

Int列に1つのGIN intインデックスを作成します

CREATE OR REPLACE FUNCTION convert_int_to_int_arr(INTEGER)
RETURNS _int4
LANGUAGE SQL
IMMUTABLE
STRICT
SECURITY INVOKER
PARALLEL SAFE
COST 0.01
AS $$
  SELECT ARRAY(
    SELECT
      CASE
        WHEN (1 << (i - 1)) & $1 > 0 THEN i
        ELSE -i
      END
    FROM generate_series(1, 32) AS i
  )
$$
;
CREATE INDEX IF NOT EXISTS idx_gin_int_on_int ON t_bitwise
  USING GIN(convert_int_to_int_arr(c_int) gin__int_ops)
;

ビット列に1つのGIN intインデックスを作成します

CREATE OR REPLACE FUNCTION convert_bit_to_int_arr(BIT(32))
RETURNS _int4
LANGUAGE SQL
IMMUTABLE
STRICT
SECURITY INVOKER
PARALLEL SAFE
COST 0.01
AS $$
  SELECT ARRAY(
    SELECT
      CASE
        WHEN (1 << (i - 1)) & $1::INTEGER > 0 THEN i
        ELSE -i
      END
    FROM generate_series(1, 32) AS i
  )
$$
;
CREATE INDEX IF NOT EXISTS idx_gin_int_on_bit ON t_bitwise
  USING GIN(convert_bit_to_int_arr(c_bit) gin__int_ops)
;

インサート


インサート

時間がかかります。

INSERT INTO t_bitwise (
  id ,c_int ,c_bit ,c_int_arr
  ,c_bool_1 ,c_bool_2 ,c_bool_3 ,c_bool_4 ,c_bool_5 ,c_bool_6 ,c_bool_7 ,c_bool_8 ,c_bool_9 ,c_bool_10
  ,c_bool_11 ,c_bool_12 ,c_bool_13 ,c_bool_14 ,c_bool_15 ,c_bool_16 ,c_bool_17 ,c_bool_18 ,c_bool_19 ,c_bool_20
  ,c_bool_21 ,c_bool_22 ,c_bool_23 ,c_bool_24 ,c_bool_25 ,c_bool_26 ,c_bool_27 ,c_bool_28 ,c_bool_29 ,c_bool_30
  ,c_bool_31 ,c_bool_32
) SELECT
  id
  ,t.Rand_int
  ,t.Rand_int::BIT(32)
  /*
  ,ARRAY(
    SELECT
        (1 << (i - 1)) & t.Rand_int
    FROM generate_series(1, 32) AS i
    WHERE
      ((1 << (i - 1)) & t.Rand_int > 0)
  )
  */
  ,ARRAY(
    SELECT
        CASE
        WHEN (1 << (i - 1)) & t.Rand_int > 0 THEN i
        ELSE -i
      END
    FROM generate_series(1, 32) AS i
  )
  ,(1 << 0) & t.Rand_int > 0 ,(1 << 1) & t.Rand_int > 0 ,(1 << 2) & t.Rand_int > 0 ,(1 << 3) & t.Rand_int > 0
  ,(1 << 4) & t.Rand_int > 0 ,(1 << 5) & t.Rand_int > 0 ,(1 << 6) & t.Rand_int > 0 ,(1 << 7) & t.Rand_int > 0
  ,(1 << 8) & t.Rand_int > 0 ,(1 << 9) & t.Rand_int > 0 ,(1 << 10) & t.Rand_int > 0 ,(1 << 11) & t.Rand_int > 0
  ,(1 << 12) & t.Rand_int > 0 ,(1 << 13) & t.Rand_int > 0 ,(1 << 14) & t.Rand_int > 0 ,(1 << 15) & t.Rand_int > 0
  ,(1 << 16) & t.Rand_int > 0 ,(1 << 17) & t.Rand_int > 0 ,(1 << 18) & t.Rand_int > 0 ,(1 << 19) & t.Rand_int > 0
  ,(1 << 20) & t.Rand_int > 0 ,(1 << 21) & t.Rand_int > 0 ,(1 << 22) & t.Rand_int > 0 ,(1 << 23) & t.Rand_int > 0
  ,(1 << 24) & t.Rand_int > 0 ,(1 << 25) & t.Rand_int > 0 ,(1 << 26) & t.Rand_int > 0 ,(1 << 27) & t.Rand_int > 0
  ,(1 << 28) & t.Rand_int > 0 ,(1 << 29) & t.Rand_int > 0 ,(1 << 30) & t.Rand_int > 0 ,(1 << 31) & t.Rand_int > 0
FROM (
  SELECT
    id
    ,(random() * 4294967295)::BIGINT::BIT(32)::INTEGER AS Rand_int
  FROM generate_series(1, 30000000) AS id
) AS t
;

分析する

SELECT
  gin_clean_pending_list('idx_gin_on_int_arr')
  ,gin_clean_pending_list('idx_gin_int_on_int')
  ,gin_clean_pending_list('idx_gin_int_on_bit')
;
VACUUM FULL
;
ANALYZE t_bitwise
;
SELECT COUNT(*) FROM t_bitwise
;

インデックスサイズ

SELECT
    indexname,
    pg_size_pretty(pg_relation_size(quote_ident(t.tablename)::text)) AS table_size,
    pg_size_pretty(pg_relation_size(quote_ident(indexrelname)::text)) AS index_size
FROM pg_tables t
LEFT OUTER JOIN pg_class c ON t.tablename = c.relname
LEFT OUTER JOIN (
  SELECT
    c.relname AS ctablename, ipg.relname AS indexname, indexrelname
  FROM pg_index x
  JOIN pg_class c ON c.oid = x.indrelid
  JOIN pg_class ipg ON ipg.oid = x.indexrelid
  JOIN pg_stat_all_indexes psai ON x.indexrelid = psai.indexrelid
) AS foo ON t.tablename = foo.ctablename
WHERE t.schemaname = 'public' AND t.tablename = 't_bitwise' AND indexname != 't_bitwise_pkey'
;

結果

          indexname          | table_size | index_size 
-----------------------------+------------+------------
 idx_btree_on_bool_1         | 6893 MB    | 643 MB
 idx_btree_on_bool_2         | 6893 MB    | 643 MB
 idx_btree_on_bool_3         | 6893 MB    | 643 MB
 idx_btree_on_bool_4         | 6893 MB    | 643 MB
 idx_btree_on_bool_5         | 6893 MB    | 643 MB
 idx_btree_on_bool_6         | 6893 MB    | 643 MB
 idx_btree_on_bool_7         | 6893 MB    | 643 MB
 idx_btree_on_bool_8         | 6893 MB    | 643 MB
 idx_btree_on_bool_9         | 6893 MB    | 643 MB
 idx_btree_on_bool_10        | 6893 MB    | 643 MB
 idx_btree_on_bool_11        | 6893 MB    | 643 MB
 idx_btree_on_bool_12        | 6893 MB    | 643 MB
 idx_btree_on_bool_13        | 6893 MB    | 643 MB
 idx_btree_on_bool_14        | 6893 MB    | 643 MB
 idx_btree_on_bool_15        | 6893 MB    | 643 MB
 idx_btree_on_bool_16        | 6893 MB    | 643 MB
 idx_btree_on_bool_17        | 6893 MB    | 643 MB
 idx_btree_on_bool_18        | 6893 MB    | 643 MB
 idx_btree_on_bool_19        | 6893 MB    | 643 MB
 idx_btree_on_bool_20        | 6893 MB    | 643 MB
 idx_btree_on_bool_21        | 6893 MB    | 643 MB
 idx_btree_on_bool_22        | 6893 MB    | 643 MB
 idx_btree_on_bool_23        | 6893 MB    | 643 MB
 idx_btree_on_bool_24        | 6893 MB    | 643 MB
 idx_btree_on_bool_25        | 6893 MB    | 643 MB
 idx_btree_on_bool_26        | 6893 MB    | 643 MB
 idx_btree_on_bool_27        | 6893 MB    | 643 MB
 idx_btree_on_bool_28        | 6893 MB    | 643 MB
 idx_btree_on_bool_29        | 6893 MB    | 643 MB
 idx_btree_on_bool_30        | 6893 MB    | 643 MB
 idx_btree_on_bool_31        | 6893 MB    | 643 MB
 idx_btree_on_bool_32        | 6893 MB    | 643 MB
 idx_btree_on_composite_bool | 6893 MB    | 1423 MB
 idx_bloom_on_bool           | 6893 MB    | 633 MB
 idx_bloom_on_int            | 6893 MB    | 633 MB
 idx_bloom_on_bit            | 6893 MB    | 633 MB
 idx_gin_on_int_arr          | 6893 MB    | 1030 MB
 idx_gin_int_on_int          | 6893 MB    | 1030 MB
 idx_gin_int_on_bit          | 6893 MB    | 1030 MB

sQLのテスト


すべての実行時間結果は、secondクエリ結果から取得されます。これは、最初のクエリは通常、インデックスをメモリにロードするのに時間がかかるためです。

intカラムのフルスキャン

1ビットフィルタリング(Parallel Seq Scan、実行時間:122930.731 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_int & 1 = 1
;

16ビットフィルタリング(Parallel Seq Scan、122896.131 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_int & 255 = 255
  AND (c_int >> 8) & 255 = 0
;

bool列のbtreeインデックス

1ビットフィルタリング(シーケンススキャン、実行時間:122853.069 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_bool_1 IS TRUE
;

16ビットフィルタリング(並列シーケンス、実行時間:122834.960ミリ秒)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_bool_1 IS TRUE
  AND c_bool_2 IS TRUE
  AND c_bool_3 IS TRUE
  AND c_bool_4 IS TRUE
  AND c_bool_5 IS TRUE
  AND c_bool_6 IS TRUE
  AND c_bool_7 IS TRUE
  AND c_bool_8 IS TRUE
  AND c_bool_9 IS FALSE
  AND c_bool_10 IS FALSE
  AND c_bool_11 IS FALSE
  AND c_bool_12 IS FALSE
  AND c_bool_13 IS FALSE
  AND c_bool_14 IS FALSE
  AND c_bool_15 IS FALSE
  AND c_bool_16 IS FALSE
;

bool列の複合btreeインデックス

16ビットフィルタリング(idx_btree_on_composite_boolを使用、実行時間:293.317 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_bool_32 IN (TRUE, FALSE)
  AND c_bool_31 IN (TRUE, FALSE)
  AND c_bool_30 IN (TRUE, FALSE)
  AND c_bool_29 IN (TRUE, FALSE)
  AND c_bool_28 IN (TRUE, FALSE)
  AND c_bool_27 IN (TRUE, FALSE)
  AND c_bool_26 IN (TRUE, FALSE)
  AND c_bool_25 IN (TRUE, FALSE)
  AND c_bool_24 IN (TRUE, FALSE)
  AND c_bool_23 IN (TRUE, FALSE)
  AND c_bool_22 IN (TRUE, FALSE)
  AND c_bool_21 IN (TRUE, FALSE)
  AND c_bool_20 IN (TRUE, FALSE)
  AND c_bool_19 IN (TRUE, FALSE)
  AND c_bool_18 IN (TRUE, FALSE)
  AND c_bool_17 IN (TRUE, FALSE)
  AND c_bool_16 IS FALSE
  AND c_bool_15 IS FALSE
  AND c_bool_14 IS FALSE
  AND c_bool_13 IS FALSE
  AND c_bool_12 IS FALSE
  AND c_bool_11 IS FALSE
  AND c_bool_10 IS FALSE
  AND c_bool_9 IS FALSE
  AND c_bool_8 IS TRUE
  AND c_bool_7 IS TRUE
  AND c_bool_6 IS TRUE
  AND c_bool_5 IS TRUE
  AND c_bool_4 IS TRUE
  AND c_bool_3 IS TRUE
  AND c_bool_2 IS TRUE
  AND c_bool_1 IS TRUE
;

ブール列のブルームインデックス

1ビットフィルタリング(シーケンススキャン、実行時間:122726.850ミリ秒)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  CAST(c_bool_1 AS INTEGER) = 1
;

16ビットフィルタリング(idx_bloom_on_boolを使用、実行時間:373.581 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  CAST(c_bool_1 AS INTEGER) = 1
  AND CAST(c_bool_2 AS INTEGER) = 1
  AND CAST(c_bool_3 AS INTEGER) = 1
  AND CAST(c_bool_4 AS INTEGER) = 1
  AND CAST(c_bool_5 AS INTEGER) = 1
  AND CAST(c_bool_6 AS INTEGER) = 1
  AND CAST(c_bool_7 AS INTEGER) = 1
  AND CAST(c_bool_8 AS INTEGER) = 1
  AND CAST(c_bool_9 AS INTEGER) = 0
  AND CAST(c_bool_10 AS INTEGER) = 0
  AND CAST(c_bool_11 AS INTEGER) = 0
  AND CAST(c_bool_12 AS INTEGER) = 0
  AND CAST(c_bool_13 AS INTEGER) = 0
  AND CAST(c_bool_14 AS INTEGER) = 0
  AND CAST(c_bool_15 AS INTEGER) = 0
  AND CAST(c_bool_16 AS INTEGER) = 0
;

int列のブルームインデックス

1ビットフィルタリング(シーケンススキャン、実行時間:122660.620ミリ秒)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  bitcheck_int(c_int, 1) = 1
;

16ビットフィルタリング(idx_bloom_on_intを使用、実行時間:391.335 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  bitcheck_int(c_int, 1) = 1
  AND bitcheck_int(c_int, 2) = 2
  AND bitcheck_int(c_int, 3) = 3
  AND bitcheck_int(c_int, 4) = 4
  AND bitcheck_int(c_int, 5) = 5
  AND bitcheck_int(c_int, 6) = 6
  AND bitcheck_int(c_int, 7) = 7
  AND bitcheck_int(c_int, 8) = 8
  AND bitcheck_int(c_int, 9) = -9
  AND bitcheck_int(c_int, 10) = -10
  AND bitcheck_int(c_int, 11) = -11
  AND bitcheck_int(c_int, 12) = -12
  AND bitcheck_int(c_int, 13) = -13
  AND bitcheck_int(c_int, 14) = -14
  AND bitcheck_int(c_int, 15) = -15
  AND bitcheck_int(c_int, 16) = -16
;

ビット列のブルームインデックス

1ビットフィルタリング(シーケンススキャン、実行時間:122434.644 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  bitcheck_bit(c_bit, 1) = 1
;

16ビットフィルタリング(idx_bloom_on_bitを使用、実行時間:397.157 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  bitcheck_bit(c_bit, 1) = 1
  AND bitcheck_bit(c_bit, 2) = 2
  AND bitcheck_bit(c_bit, 3) = 3
  AND bitcheck_bit(c_bit, 4) = 4
  AND bitcheck_bit(c_bit, 5) = 5
  AND bitcheck_bit(c_bit, 6) = 6
  AND bitcheck_bit(c_bit, 7) = 7
  AND bitcheck_bit(c_bit, 8) = 8
  AND bitcheck_bit(c_bit, 9) = -9
  AND bitcheck_bit(c_bit, 10) = -10
  AND bitcheck_bit(c_bit, 11) = -11
  AND bitcheck_bit(c_bit, 12) = -12
  AND bitcheck_bit(c_bit, 13) = -13
  AND bitcheck_bit(c_bit, 14) = -14
  AND bitcheck_bit(c_bit, 15) = -15
  AND bitcheck_bit(c_bit, 16) = -16
;

Int配列のGINインデックス

1ビットフィルタリング(idx_gin_on_int_arrが使用され、実行時間:440942.779 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_int_arr @> (ARRAY[1])::_int4
;

16ビットフィルタリング(idx_gin_on_int_arrを使用、実行時間:15322.953 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  c_int_arr @> (ARRAY[1,2,3,4,5,6,7,8,-9,-10,-11,-12,-13,-14,-15,-16])::_int4
;

IntのGIN intインデックス

1ビットフィルタリング(idx_gin_int_on_intが使用され、実行時間:489909.621 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  convert_int_to_int_arr(c_int) @> (ARRAY[1])::_int4
;

16ビットフィルタリング(idx_gin_int_on_intが使用され、実行時間:15259.772 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  convert_int_to_int_arr(c_int) @> (ARRAY[1,2,3,4,5,6,7,8,-9,-10,-11,-12,-13,-14,-15,-16])::_int4
;

ビットのGIN intインデックス

1ビットフィルタリング(idx_gin_int_on_bitが使用され、実行時間:506071.625 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  convert_bit_to_int_arr(c_bit) @> (ARRAY[1])::_int4
;

16ビットフィルタリング(idx_gin_int_on_bitが使用され、実行時間:15292.945 ms)

EXPLAIN ANALYZE SELECT 1 FROM t_bitwise
WHERE
  convert_bit_to_int_arr(c_bit) @> (ARRAY[1,2,3,4,5,6,7,8,-9,-10,-11,-12,-13,-14,-15,-16])::_int4
;

私の考え

  • ブルームインデックスはインデックスサイズが小さく、実行時間がそれほど悪くないため、他よりも優れているようです。 (とにかく、カーディナリティが低い場合、クエリプランはフルスキャンになります)
  • あなたの列は20000ビットです!テストできません。ブルームインデックスは最大32の正規列または式列を持つことができるため、ブルームインデックスを使用する場合は分割する必要があります。
2
takaomag

化合物(数百万行)を含むテーブルを作成しています。これらの化合物のうち、特定の所定の機能/フラグメントに固定長のビット文字列でフラグが立てられています。このビット文字列は2000から20000ビットの範囲にあります。そこでより正確な数値を決定するには、さらに調査を行う必要があります。特定の特定の機能がある、または特定の機能がない化合物を検索する場合、このビット文字列の選択サブセットで検索が実行されます。これは毎回異なるサブセットになる可能性があります。

ここでは bloom index を使用できると思います。

CREATE EXTENSION bloom;

ブードゥー法を実行して、引数のブール値を返します。

// (Bitwise AND it against the bit flag)
CREATE FUNCTION myT1 (int)
RETURNS bool AS $$
  SELECT true -- YOU WRITE CODE HERE
$$ LANGUAGE SQL
IMMUTABLE;

// (Bitwise AND it against the bit flag)
CREATE FUNCTION myT2 (int)
RETURNS bool AS $$
  SELECT $1 & b'10'::bit(32) -- YOU WRITE CODE HERE
$$ LANGUAGE SQL
IMMUTABLE;

// (Bitwise AND it against the bit flag)
CREATE FUNCTION myTn (int)
RETURNS bool AS $$
  SELECT $1 & b'01'::bit(32) -- YOU WRITE CODE HERE
$$ LANGUAGE SQL
IMMUTABLE;

CREATE INDEX b ON f
  USING bloom (myT1(id), myT2, myTn(id))
  WITH (length=80, col1=1);

これは、PostgreSQLの列制限を回避しながら、任意の条件で大きな検索文字列へのインデックス付きアクセスを提供するために機能する場合がありますが、面倒です。インデックスは偽陽性を返します。ただし、クエリは再チェックされるので、心配する必要はありません。

2
Evan Carroll