Bitmap index scan
While Index Scan
is effective at high correlation, it falls short when the correlation drops to the point where the scanning pattern is more random than sequential and the number of page fetches increases. One solution here is to collect all the tuple IDs beforehand, sort them by page number, and then use them to scan the table. This is how Bitmap Scan, the second basic index scan method, works.
Bitmap scans are a multi-step process that consist of a Bitmap Heap Scan, one or more Bitmap Index Scans and optionally BitmapOr and BitmapAnd operations.
How bitmap scan work?
In order to understand what how bitmap index scan work we first need to understand a little bit more about how Postgres stores its data. The columns of a table in Postgres are separated into groups called heap pages. We can picture our table being composed of several pages as shown in the following diagram:
When we create an index on a column, Postgres can, in general, very efficiently figure out the pages where the rows resulting from a query are located. It can also tell their position within the pages.
Using an Index Scan
with a b-tree, for each row in the result Postgres will ask the b-tree for the heap page and row location on the page, load the heap page and then retrieve the row.
Heap pages are stored in disk and thus loading a page in memory is costly. Thus, when using Index Scan
, if the query results in a lot of rows, the query might become very slow because an Index Scan will make a page load for each row in the result.
The following diagram shows a scenario where the query results in four rows (show in color) located in three different pages. Each color represents a consecutive group of rows that belong to the result of the query. Using an Index Scan
Postgres will load pages 2 and 7 once and page 5 twice.
Note: Random access is also the main problem causing slow performance in Index Scan
when it returns multiple rows.
Consider the following plan:
1 2 3 4 5 6 7 8 9 10 11 |
|
The query plan that we obtained above shows an alternative strategy where first, Postgres uses the index to figure out the heap pages that contain results. Then it will read each of those pages entirely in order to fetch the results.
The advantage here is that each page is loaded only once into memory and pages can be loaded sequentially. The disadvantage is that each of those pages will be fully read, meaning that Postgres will inspect some rows that do not belong to the result.
We can thus summarize the scans as follows:
- Bitmap Index Scan: Identify all heap pages that contain results that match the query
- Bitmap Heap Scan: Scan each heap page fully and recheck conditions
The whole bitmap is a single bit array, with as many bits as there are heap pages in the relation being scanned.
Bitmap starting off with all entries 0 (false). Whenever an index entry that matches the search condition is found, the heap address pointed to by that index entry is looked up as an offset into the bitmap, and that bit is set to 1 (true). So rather than looking up the heap page directly, the bitmap index scan looks up the corresponding bit position in the bitmap.
Then the bitmap contains information for heap pages we need to bother to load and examine.
Since each heap page might contain multiple rows, we then have to examine each row to see if it matches all the conditions - that's what the Recheck Cond
part is about.
Bitmap operations
A query may include multiple fields in its filter conditions. These fields may each have a separate index. Bitmap Scan allows us to take advantage of multiple indexes at once. Each index gets a row version bitmap built for it, and the bitmaps are then ANDed and ORed together. Example:
1 2 3 4 5 6 7 |
|
Graphical example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Once the bitmaps are created a bitwise AND is performed on them:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
The bitmap heap scan then seeks to the start of each page and reads the page:
1 2 3 4 5 6 7 |
|
Each read page is then re-checked against the condition since there can be >1 row per page and not all necessarily match the condition.