私の誤解を次の例で説明しようと思います。
Bitmap Heap Scan Node
のfundamentalsがわかりませんでした。クエリSELECT customerid, username FROM customers WHERE customerid < 1000 AND username <'user100';
について考えます。その計画は次のとおりです。
Bitmap Heap Scan on customers (cost=25.76..61.62 rows=10 width=13) (actual time=0.077..0.077 rows=2 loops=1)
Recheck Cond: (((username)::text < 'user100'::text) AND (customerid < 1000))
-> BitmapAnd (cost=25.76..25.76 rows=10 width=0) (actual time=0.073..0.073 rows=0 loops=1)
-> Bitmap Index Scan on ix_cust_username (cost=0.00..5.75 rows=200 width=0) (actual time=0.006..0.006 rows=2 loops=1)
Index Cond: ((username)::text < 'user100'::text)
-> Bitmap Index Scan on customers_pkey (cost=0.00..19.75 rows=1000 width=0) (actual time=0.065..0.065 rows=999 loops=1)
Index Cond: (customerid < 1000)
このノードについての私の理解:
説明したように there の場合、bitmap heap scan
はテーブルブロックを順番に読み取るため、Index Scan
を実行するだけで発生するランダムテーブルアクセスのオーバーヘッドは発生しません。
Index Scan
の実行後、PostgreSQLは不要なheap blocks reads
(またはホットキャッシュがある場合はhits
)を回避するために行を最適にフェッチする方法を認識しません。したがって、それを理解するために、bitmap
という構造(Bitmap Index Scan
)が生成されます。これは、私の場合、インデックスの2つのビットマップを生成し、BITWISE AND
を実行することによって生成されます。ビットマップが生成されているので、ビットマップは順番にテーブルを最適に読み取ることができ、不要なheap I/O-operations
を回避できます。
それは多くの質問が来る場所です。
質問:ビットマップのみです。 PostgreSQLはビットマップだけで行の物理的な順序について何かをどのようにして知るのでしょうか?または、ビットマップを生成して、その要素をページへのポインターに簡単にマップできるようにしますか?もしそうなら、それはすべてを説明しますが、それは私の推測です。
では、bitmap heap scan -> bitmap index scan
はシーケンシャルスキャンのようなものですが、テーブルの適切な部分のみであると言えますか?
PostgreSQLはビットマップだけで行の物理的な順序について何かをどのようにして知るのでしょうか?
ビットマップは、ヒープページごとに1ビットです。ビットマップインデックススキャンは、インデックスエントリが指すヒープページアドレスに基づいてビットを設定します。
したがって、ビットマップヒープスキャンを実行するときは、リニアテーブルスキャンを実行し、ビットマップを読み取って、特定のページに問題があるか、それともシークする必要があるかを確認します。
または、ビットマップを生成して、その要素をページへのポインターに簡単にマップできるようにしますか?
いいえ、ビットマップはヒープページに1:1で対応しています。
私はこれについていくつか書きました here 。
OK、このコンテキストでの「ビットマップ」の意味を誤解しているようです。
これは、ヒープページごと、または読み込まれたインデックスごとなどに作成される「101011」のようなビット文字列ではありません。
ビットマップ全体は単一の ビット配列 であり、スキャンされる関係にあるヒープページと同じ数のビットが含まれます。
最初のインデックススキャンによって1つのビットマップが作成され、すべてのエントリが0(false)から始まります。検索条件に一致するインデックスエントリが見つかると、そのインデックスエントリが指すヒープアドレスがオフセットとしてビットマップに検索され、そのビットが1(真)に設定されます。したがって、ビットマップインデックススキャンは、ヒープページを直接検索するのではなく、ビットマップ内の対応するビット位置を検索します。
2回目以降のビットマップインデックススキャンでは、他のインデックスとそれらに対する検索条件を使用して同じことを行います。
次に、各ビットマップがAND演算されます。結果のビットマップは、ヒープページごとに1ビットを持ちます。ビットは、個々のビットマップインデックススキャンすべてでtrueだった場合にのみtrueになります。つまり、すべてのインデックススキャンで一致した検索条件です。これらは、ロードして調査するためにわざわざ必要とする唯一のヒープページです。各ヒープページには複数の行が含まれる可能性があるため、各行を調べて、すべての条件に一致するかどうかを確認する必要があります。これが「recheck cond」の部分です。
これらすべてを理解するために重要なことの1つは、インデックスエントリのタプルアドレスが行のctid
を指していることです。これは、ヒープページ番号とヒープページ内のオフセットの組み合わせです。とにかくページ全体をチェックするため、ビットマップインデックススキャンはオフセットを無視し、そのページのいずれかの行が条件に一致する場合にビットを設定します。
グラフィカルな例
Heap, one square = one page:
+---------------------------------------------+
|c____u_____X___u___X_________u___cXcc______u_|
+---------------------------------------------+
Rows marked c match customers pkey condition.
Rows marked u match username condition.
Rows marked X match both conditions.
Bitmap scan from customers_pkey:
+---------------------------------------------+
|100000000001000000010000000000000111100000000| bitmap 1
+---------------------------------------------+
One bit per heap page, in the same order as the heap
Bits 1 when condition matches, 0 if not
Bitmap scan from ix_cust_username:
+---------------------------------------------+
|000001000001000100010000000001000010000000010| bitmap 2
+---------------------------------------------+
ビットマップが作成されたら、ビットごとのANDがビットマップに対して実行されます。
+---------------------------------------------+
|100000000001000000010000000000000111100000000| bitmap 1
|000001000001000100010000000001000010000000010| bitmap 2
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
|000000000001000000010000000000000010000000000| Combined bitmap
+-----------+-------+--------------+----------+
| | |
v v v
Used to scan the heap only for matching pages:
+---------------------------------------------+
|___________X_______X______________X__________|
+---------------------------------------------+
次に、ビットマップヒープスキャンは各ページの先頭にシークし、ページを読み取ります。
+---------------------------------------------+
|___________X_______X______________X__________|
+---------------------------------------------+
seek------->^seek-->^seek--------->^
| | |
------------------------
only these pages read
また、ページごとに1行より多くなる可能性があり、すべてが必ずしも条件に一致するわけではないため、各読み取りページは条件に対して再チェックされます。
PostgreSQLのビットマップスキャンの詳細については、私のブログ投稿 https://rajeevrastogi.blogspot.in/2018/02/bitmap-scan-in-postgresql.html?showComment=1518410565792#c4647352762092142586 を参照してください。
ビットマップスキャンの全体的なクイック機能の概要:
ビットマップヒープスキャンは、ビットマップインデックススキャンからタプルを要求します。
ビットマップインデックススキャンは、通常のインデックススキャンとほぼ同じ方法で、条件に従ってインデックスをスキャンします。ただし、ヒープデータに対応するTID(ページ番号とその中のオフセットで構成される)を返す代わりに、ビットマップにそれらのTIDを追加します。簡単に理解できるように、このビットマップにはすべてのページのハッシュ(ページ番号に基づいてハッシュ化)が含まれ、各ページエントリにはそのページ内のすべてのオフセットの配列が含まれていると考えることができます。
次に、ビットマップヒープスキャンはビットマップを読み取り、格納されているページ番号とオフセットに対応するヒープデータを取得します。次に、可視性、資格などをチェックし、これらすべてのチェックの結果に基づいてタプルを返します。