本文共 3624 字,大约阅读时间需要 12 分钟。
一、Bitmap Heap Scan/ Bitmap Index Scan
在查看GP的执行计划会看到Bitmap Heap Scan/ Bitmap Index Scan/Bitmap Or/Bitmap And.这些关键字是什么意思呢?
Bitmap Index Scan/Bitmap Or/Bitmap And -> Bitmap Heap Scan。 前者的cost最红会汇总到后面的Cost。
如下图:
定义
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这种就不适合,因为它一次会取出所有数据。(原因是Bitmap Index Scan需要完成Index Rows的扫描在内存中建立Bitmap后才能访问表数据,而Index Scan或者Seq Scan则没有预先步骤直接扫描一行输出一行,因此Bitmap Index Scan在存在limit子句时性能可能会较差)
Index Scan 和bitMap Index Scan的比较?
参考:
什么时候会发生Bitmap Heap Scan?
当某一字段的重复值很多时,执行计划会走此种索引。这只是从宏观上从经验上。具体可以参考上面的第一篇文章,从微观上详细阐述了为何会发生bitmap index scan,以及为何会发生Bitmap Heap Scan,以及Recheck Con的使用情景。
大致意思是:如果index scan扫描的数据过多,计划器会选择透过bitmap scan 扫描很多tuple。如何决定是要透过index scan 还是bitmap scan执行呢?计划期会根据 i/o ,collection(物理行和逻辑行的相关性),等列的局部属性都会影响。当使用bitmap每次扫描到的tuple足够多时,(个人理解:计划期认为扫描的tuple足够占据一个page时)就会发生BitMap Heap Scan。Recheck Cond这时候就发生了,用来检查所选条件的数据是否都在这个page上,执行计划难免不准,double check毋庸置疑。当发生bitmap lossy的时候,(个人理解就是标记的此页有不满足条件的tuple)才会真正发生recheck。
在bitmap lossy情况中,我们只记住哪些page包含匹配的tuple,而不是逐个记住每个tuple。当这种情况发生时,表访问阶段必须检查page上的每个tuple,并真正的发生recheck cond,以此来确认要返回哪些tuple。
需要注意的是: postger的index扫描很特殊,虽然是一行一行的扫描,但可以选择不立即返回,它可以记住match到哪一个page,当完成对index的扫描。的时候再从磁盘刷新出来,这样便只读取具有match的tuple对应的page一次。减少了页面的读取次数,其实最近从硬盘刷新出来的资料会放在buffer中,如果在未刷回硬盘的时候再读取match到的page时速度应该还是可以的。
请看下例
explain select relkind,* from pg_class_temp where relkind = 'r'--pg_class中的relkind字段,重复值很多,在这个字段上建立索引--relkind是什么意思呢? --1、来判断一个对象是表还是视图还是索引......。--2、 r =普通表,i = 索引,S =序列,v = 视图, c =复合类型,s = 特殊,t =TOAST表create index pg_class_relkind_idx on pg_class_temp(relkind);--enable_seqscan禁止顺序扫描,确保执行计划是通过pg_class_relkind_idx来查询数据set optimizer=off
执行计划如下所示。
此处需要注意的是要关闭seqScan。否则数据量较小的情况下,执行器会直接seqScan。为了让测试结果更准确优化器也最好关掉。
以下是占用GP master CPU较高的sql之一对应的exlpain。可以看,此段sqlslice2的work_mem大小约为313M。而GP的设定是32MB。所以说此段sql中slice 的recheck cond是真实发生的,这验证了前文所述。如何优化?
Slice statistics: (slice0) Executor memory: 21780K bytes. (slice1) Executor memory: 3654123K bytes avg x 48 workers, 3869945K bytes max (seg38). Work_mem: 32776K bytes max.Statement statistics: Memory used: 128000K bytesSettings: enable_bitmapscan=on; enable_seqscan=off; optimizer=offOptimizer status: legacy query optimizerTotal runtime: 294592.942 ms
在postgresql 9.4版本的执行计划中已经可以看到具体的bitmap lossy。例如
Bitmap Heap Scan on aa (cost=107.68..4818.05 rows=5000 width=4) (actual time=27.629..213.606 rows=100001 loops=1) Recheck Cond: ((a >= 100000) AND (a <= 200000)) Rows Removed by Index Recheck: 758222 Heap Blocks: exact=693 lossy=3732 -> Bitmap Index Scan on aai (cost=0.00..106.43 rows=5000 width=0) (actual time=27.265..27.265 rows=100001 loops=1) Index Cond: ((a >= 100000) AND (a <= 200000))
源码级别的探讨参见:
转载地址:http://njhqj.baihongyu.com/