Bitmap Index Scan

Bitmap Index Scan是数据库中的一种扫描方式,它通过一次性获取所有符合条件的tuple-pointers并进行内存排序,以提高数据读取的局部性,从而在大数据量下提升效率。相比传统的Index Scan,Bitmap Scan在数据量大时更具优势,尤其是在AND和OR条件查询中。然而,对于LIMIT等操作,Bitmap Index Scan可能不是最佳选择。通过调整参数如enable_bitmapscan和random_page_cost,可以影响查询计划选择哪种扫描方式。在某些情况下,需要留意recheck cond过程,因为数据量大时可能会出现有损的Bitmap Heap Scan,这时可能需要调整work_mem来优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bitmap Index Scan

数据库里面的表的扫描方式主要是以下几种方式:sequential scans, index scans, and bitmap index scans,当然还有index only scan,这种算是index scans中比较特殊的一种,需要的信息在索引中都能找到,扫描索引即可,不需要去扫描表。

这篇文章主要谈谈Bitmap Index Scan的原理及适用的场景。

定义

A plain indexscan fetches one tuple-pointer at a time from the index,and immediately visits that tuple in the table. A bitmap scan fetches all the tuple-pointers from the index in one go, sorts them using an in-memory "bitmap" data structure, and then visits the table tuples in physical tuple-location order. The bitmap scan improves locality of reference to the table at the cost of more bookkeeping overhead to manage the "bitmap" data structure --- and at the cost that the data is no longer retrieved in index order, which doesn't matter for your query but would matter if you said ORDER BY.

A bitmapped index scan works in two stages. First the index or indexes are scanned to create a bitmap representing matching tuple.

这段解释是tom lane在postgres邮件组中的回答,我觉得是比较权威而且通俗易懂的解释。

核心是传统的index scan每次从索引中去取一个tuple的指针,然后立马去表中取数据,每一次会造成一次随机io。如果数据量较多的情况下,会比较低效。而bitmap scan一次性将符合条件的tuple-pointers全部取出来,然后在内存中进行地址排序,然后去取出数据,这时的读取数据由于进行的地址排序,读取时就变成了顺序的读。其实就是一个随机读转化为顺序读取的过程,但是取出的数据由于进行了地址的排序,就没有顺序。同时,对于limit这种sql,bitmap index scan这种就不适合,因为它一次会取出所有数据。

和传统索引扫描对比:

_2018_10_31_3_39_38

在读取数据量比较小时,index scan比较合适,在读取数据量比较大的情况下,bitmap index scan会更有优势。

下面通过实验验证

建立测试表

创建测试表
CREATE TABLE sampletable (x numeric);

插入数据
INSERT INTO sampletable SELECT random() * 10000 FROM generate_series(1, 10000000);

全表扫描

postgres=# explain (analyze,buffers) select * from sampletable where x = 12;
                                                           QUERY PLAN
--------------------------------------------------------------
<think>嗯,用户现在聚焦在PostgreSQL执行计划中的"Bitmap Heap Scan"操作上,这属于数据库查询优化的专业领域。用户提供的引用材料很有价值,特别是关于lossy bitmap的细节和Recheck Cond的解释。 用户可能正在分析一个慢查询,在EXPLAIN结果中看到了Bitmap Heap Scan,想理解它的含义和性能影响。从引用[1][3]看,用户对技术细节的挖掘很深,甚至注意到了9.4版本特有的lossy统计信息,说明有一定数据库基础。 需要重点解释三个核心点: 1) Bitmap Heap Scan的工作机制(为什么需要bitmap) 2) lossy和exact模式的区别(引用[1]的693 exact/3732 lossy很典型) 3) Recheck Cond的触发条件(引用[3]的stackoverflow解释很准确) 特别要注意引用[3]提到的关键机制:当bitmap过大时,PostgreSQL会从精确记录行位置(exact)退化为只记录页位置(lossy),这时就必须通过Recheck Cond逐行验证。这个设计平衡了内存使用和精确性。 用户可能隐含的深层需求是如何优化这种扫描。可以结合引用[1]的案例说明:当lossy比例过高(如3732页)时,意味着需要调整work_mem或优化索引。 在解释结构上,考虑用分层方式: - 先讲基础概念(什么是bitmap扫描) - 再拆解执行流程(Index Scan -> Heap Scan) - 最后分析性能指标(lossy/exact块数、Recheck开销) 需要警惕的易错点:Bitmap Heap Scan本身不是问题,lossy模式才是潜在瓶颈。要强调“何时需要优化”的判断标准(如lossy比例>20%或Recheck耗时占比高)。</think>PostgreSQL 中的 `Bitmap Heap Scan on 表名` 是查询执行计划中的关键操作,它表示**通过位图索引扫描结果访问物理表数据的过程**。以下是详细解析(结合引用内容): --- ### 一、`Bitmap Heap Scan` 的作用机制 1. **工作流程** 当查询条件涉及多个索引或范围查询时,优化器可能按此顺序执行: ```sql Bitmap Index ScanBitmap Heap Scan → Recheck Cond ``` - **Bitmap Index Scan**: 扫描索引并生成**位图**(bitmap),标记满足条件的**数据页位置**(非具体行)。 *示例输出片段*(引用[1][2]): ``` -> Bitmap Index Scan on idx_name -- 在索引上生成位图 Index Cond: (a >= 100000 AND a <= 200000) ``` - **Bitmap Heap Scan**: 根据位图定位到**物理数据页**,从磁盘/缓存中读取数据(称为"堆表"访问)。 *示例输出片段*: ``` Bitmap Heap Scan on orders -- 基于位图访问物理表 Recheck Cond: (total > 100) Heap Blocks: exact=693 lossy=3732 -- 精确/模糊块数量 ``` 2. **Recheck Cond 的触发原因**(引用[3]) - 当位图过大时,为节省内存会转为 **lossy 模式**(只记录页位置,不记录具体行)。 - 此时需在数据页中逐行验证条件(`Recheck Cond`),确保返回正确数据。 - *性能提示*:`lossy` 值过高会增加 CPU 开销,需调整 `work_mem` 参数或优化索引。 --- ### 二、关键输出字段解读(引用[1][3]) | **字段** | **含义** | |-------------------------|--------------------------------------------------------------------------| | `Heap Blocks: exact=693` | 精确匹配的数据页数量(无需逐行验证) | | `Heap Blocks: lossy=3732` | 模糊匹配的数据页数量(需通过 `Recheck Cond` 逐行过滤) | | `Rows Removed by Index Recheck: 758222` | 因 `Recheck` 被过滤掉的行数(反映 `lossy` 模式的额外开销) | | `Recheck Cond: (a >= 100000)` | 重新验证的条件(通常因 `lossy` 模式触发) | --- ### 三、性能优化方向 1. **减少 `lossy` 块比例** - 增大 `work_mem` 参数,允许存储更精细的位图(减少模糊块)。 - 优化查询条件,缩小索引扫描范围(如避免宽范围查询)。 2. **索引策略调整** - 对高频过滤列创建更高效的索引(如 BRIN 对范围查询友好)。 - 使用覆盖索引(`INCLUDE` 子句)避免回表访问。 3. **监控 `Recheck` 开销** 若 `Rows Removed by Index Recheck` 值过高,说明大量数据被二次过滤,需检查: - 索引是否碎片化(执行 `REINDEX`)。 - 统计信息是否过期(执行 `ANALYZE table_name`)。 --- ### 四、示例场景分析 ```sql EXPLAIN ANALYZE SELECT * FROM orders WHERE total BETWEEN 100 AND 200; ``` 可能输出: ``` Bitmap Heap Scan on orders (cost=12.36..50.24 rows=500 width=8) Recheck Cond: ((total >= 100) AND (total <= 200)) Heap Blocks: exact=120 lossy=30 -> Bitmap Index Scan on idx_total -- 位图索引扫描 Index Cond: ((total >= 100) AND (total <= 200)) ``` **解读**: 1. 通过 `idx_total` 索引生成位图(`Bitmap Index Scan`)。 2. 访问物理表时,120个数据页精确定位(`exact`),30个页需逐行验证(`lossy`)。 3. 若 `lossy` 比例过高(如 >20%),需按前述方法优化。 --- ### 总结 - **`Bitmap Heap Scan` 本质**:通过位图定位物理数据页的访问操作。 - **`lossy` 模式**:为节省内存牺牲精度,需 `Recheck Cond` 二次过滤。 - **优化核心**:减少 `lossy` 块、更新统计信息、合理设计索引[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值