当前位置: 首页 > news >正文

华夏望子成龙网站开发背景长春电商网站建设多少钱

华夏望子成龙网站开发背景,长春电商网站建设多少钱,wordpress android读取,厂房装修东莞网站建设扫描算子在上层计算和底层存储之间#xff0c;向下扫描底层存储的数据#xff0c;向上作为计算的输入源#xff0c;在SQL的执行层中#xff0c;起着关键的作用。顺序、索引、位图等不同类型的扫描算子适配不同的数据分布场景。然而#xff0c;扫描算子背后的实现原理是怎样…扫描算子在上层计算和底层存储之间向下扫描底层存储的数据向上作为计算的输入源在SQL的执行层中起着关键的作用。顺序、索引、位图等不同类型的扫描算子适配不同的数据分布场景。然而扫描算子背后的实现原理是怎样的如何评估不同扫描方法的代价和选择呢 在往期的PG内幕直播讲解中我们曾为大家介绍过PostgreSQL中的索引扫描。本次的直播我们和大家分享了PostgreSQL中位图扫描的实现方式讲解了位图扫描的源码实现并从代价估算的角度对比了不同扫描方式。以下内容根据直播文字整理。 扫描方法分类及代价估算 在PostgreSQL的查询优化过程中优化器会根据不同等价算子构建许多符合条件的查询路径并从中选择出代价最小的路径转化为一个计划传递给执行器执行。而评价路径优劣的依据是基于算子和系统统计信息估计出的不同路径的代价Cost。 常见的扫描方式有以下三种 顺序扫描 当对无索引的字段进行查询或者判断到查询将返回大量数据时查询优化器将会使用顺序扫描方法把表中所有数据块从前到后全部读一遍然后找到符合条件的数据块。顺序扫描适合数据选择率高、命中大量数据的场景。 索引扫描 索引扫描先利用字段的索引数据定位到数据的位置然后再去数据表中读取数据。它适合选择率低、命中相对少数据的场景。 位图扫描 尽管索引数据一般比较少但是它需要随机IO操作相比顺序扫描所采用的顺序IO而言成本要更高所以索引扫描的代价并不总是低。那如何在二者之间做平衡呢对于选择率不高不低命中率适中的场景通常会使用到位图扫描。 位图扫描原理是将索引扫描中的随机IO尽量转换成顺序IO降低执行计划的代价。它首先访问索引数据过滤出符合提交的数据的索引信息(CTID)然后根据CTID来进行聚合和排序将要访问数据页面有序化同时数据访问的随机IO也转换成顺序IO。 接下来我们从代价估算的角度来看一下不同扫描方法的差异 图1代价估算示例表 如图1所示示例表中包含a、 b 、c三个字段 索引信息a、 b 、c 三个字段分别有一个btree索引 统计信息表内包含200w行数据10811个数据页全部可见。 代价估算示例 最简单的扫描 (select * from t1) xiaoming# explain select * from t1;QUERY PLAN -------------------------------------------------------------Seq Scan on t1 (cost0.00..30811.00 rows2000000 width12) (1 row) 这是一个最简单的顺序扫描的示例它的处理逻辑是从表的segment文件中按照顺序读取page页面到内存中然后在内存中经过简单的CPU处理(MVCC判断)返回适合的heap tuple数据。 从过程分析来看执行代价可以归为两部分IO_cost 和 CPU_cost。对于这个示例它的IO pattern 是顺序扫描IO_cost 顺序扫描一个页面的代价(seq_page_cost) * 顺序扫描多少页面(relpages)它对于tuple的处理只是简单地判断MVCC所以它的CPU_cost处理一个tuple的代价(cpu_tuple_cost) * 多少个tuple(reltuples)。 我们使用下列SQL来计算这个计划的IO_cost 和 CPU_cost xiaoming# SELECT relpages, current_setting(seq_page_cost) AS seq_page_cost,relpages * current_setting(seq_page_cost)::real AS io_cost FROM pg_class WHERE relnamet1;relpages | seq_page_cost | io_cost ----------------------------------10811 | 1 | 10811 (1 row) cpu_cost cpu_tuple_cost * reltuples xiaoming# select reltuples, current_setting(cpu_tuple_cost) as cpu_tuple_cost, reltuples * current_setting(cpu_tuple_cost)::real AS cpu_cost from pg_class where relnamet1;reltuples | cpu_tuple_cost | cpu_cost -------------------------------------2e06 | 0.01 | 20000 (1 row) total_cost io_cost cpu_cost 30811, 刚好等于explain语句输出的cost(cost0.00..30811.00 rows2000000 width12)。 不同扫描算子的代价对比 接下来我们通过同一个SQL select * from t1 where a 10000; 的不同执行计划的代价来理解扫描算子的过程。 顺序扫描 xiaoming# explain select * from t1 where a 10000;QUERY PLAN -----------------------------------------------------------Seq Scan on t1 (cost0.00..35811.00 rows20557 width12)Filter: (a 10000) (2 rows) 从Plan的输出可以看到顺序扫描比我们代价估算示例中的Plan多了一个Filter的阶段整个Plan的cost增加到了35811。因为是顺序扫描读取的数据是不变的IO_cost是固定等于10811所以增加的是CPU_cost。 从计算顺序扫描代价的函数代码中看每一个tuple的代价cpu_per_tuple cpu_tuple_cost qpqual_cost.per_tuple 其中qpqual_cost.per_tuple 和表达式的复杂度有关。在之前的示例中因为没有表达式所以qpqual_cost.per_tuple 0。在当前这个代表式 where a 10000下我们可以反推出 qpqual_cost.per_tuple 0.0025。 disk_run_cost spc_seq_page_cost * baserel-pages; /* CPU costs */ get_restriction_qual_cost(root, baserel, param_info, qpqual_cost); startup_cost qpqual_cost.startup; cpu_per_tuple cpu_tuple_cost qpqual_cost.per_tuple; cpu_run_cost cpu_per_tuple * baserel-tuples; /* tlist eval costs are paid per output row, not per tuple scanned */ startup_cost path-pathtarget-cost.startup; cpu_run_cost path-pathtarget-cost.per_tuple * path-rows; ... ... total_cost startup_cost cpu_run_cost disk_run_cost; 索引扫描 xiaoming# explain select * from t1 where a 10000;QUERY PLAN ----------------------------------------------------------------------------Index Scan using idx_a_t1 on t1 (cost0.43..32243.95 rows20557 width12)Index Cond: (a 10000) (2 rows) 索引扫描分为两个阶段扫描索引数据 根据索引数据扫描heap表数据所以它的cost也可以分为两个阶段计算 扫描索引数据的cost 对于 Btree索引来说cost等于获取所需btree page的代价btree pages的数量估算relpages * 选择率。并且这些页面在磁盘上并不是按顺序存储的因此索引数据的扫描模式是随机的。成本估算还包括处理每个index tuple的成本 cpu_index_tuple_cost以及每行的过滤成本 cpu_operator_cost。 所以扫描索引数据的index_cost relpages * selectivity rate * random_page_cost reltuples * selectivity rate * (cpu_index_tuple_cost cpu_operator_cost)。其中 relpages 是索引表的页面数reltuples 是索引表的tuple数selectivity rate 20557 / reltuples 下面的SQL是计算索引数据的cost逻辑 SELECT round(current_setting(random_page_cost)::real * pages current_setting(cpu_index_tuple_cost)::real * tuples current_setting(cpu_operator_cost)::real * tuples ) as bitmap_index_scan FROM (SELECT relpages * 0.01027850 AS pages, reltuples * 0.01027850 AS tuplesFROM pg_class WHERE relname idx_a_t1 ) c;bitmap_index_scan -------------------358 (1 row) 扫描数据的cost 对于heap data来说我们能想到对于heap data的扫描最坏的情况是每次ctid对应的heap tuple都是随机访问io_cost_max reltuples * selectivity rate * random_page_cost; 最好的情况ctid指向的heap tuple在磁盘上是有序的: io_cost_min relpages * selectivity rate * seq_page_cost。 扫描heap data的io cost在[io_cost_min, io_cost_max]区间内优化器是如何估算的呢下面是cost_index函数里的代码 // io_cost csquared indexCorrelation * indexCorrelation; run_cost max_IO_cost csquared * (min_IO_cost - max_IO_cost); // cpu_cost cpu_per_tuple cpu_tuple_cost qpqual_cost.per_tuple; cpu_run_cost cpu_per_tuple * tuples_fetched; 其中核心是indexCorrelation这个字段它的取值范围是 [-1,1]。当 indexCorrelation 的值接近 1 时表示索引的顺序与表数据的顺序高度相关即索引的排序与表数据的排序非常相似。当 indexCorrelation 的值接近 -1 时表示索引的顺序与表数据的顺序高度负相关即索引的排序与表数据的排序完全相反。当 indexCorrelation 的值接近 0 时表示索引的顺序与表数据的顺序没有明显的相关性。 对于索引扫描cache也会影响索引代价估算。思考一种情况如果cache足够大所有的页面只需要访问一次因为后续所有对于这个页面的访问都是访问数据库缓存。 位图扫描 xiaoming# explain select * from t1 where a 10000;QUERY PLAN --------------------------------------------------------------------------------------------------Bitmap Heap Scan on t1 (cost363.74..11559.98 rows20557 width12) (actual time0.988..4.097 rows20000 loops1)Recheck Cond: (a 10000)Heap Blocks: exact110- Bitmap Index Scan on idx_a_t1 (cost0.00..358.61 rows20557 width0) (actual time0.954..0.955 rows20000 loops1)Index Cond: (a 10000)Planning Time: 0.100 msExecution Time: 4.971 ms 从Plan来看Bitmap Scan也分为两个阶段Bitmap Index Scan和Bitmap Heap Scan。 通过对比三种扫描算子的Plan输出可以发现当前SQL使用位图扫描的代价是最低的 Bitmap scan cost(11559.98) index scan cost(32243.95) seq scan cost(35811) 位图扫描源码解析 位图扫描分为Bitmap Index Scan和Bitmap Heap Scan 两个阶段。 Bitmap Index Scan扫描btree index的数据构建并返回TID Bitmap Bitmap Heap Scan依赖下层算子返回的TID Bitmap扫描heap data返回符合条件的tuple数据。 Bitmap Index Scan Scan算子都有相同的三个阶段Init/Exec/End 在Init阶段初始化扫描需要的数据结构将查询条件转换成ScanKey在Exec阶段执行真正的扫描动作在End阶段清理相关的资源。 Bitmap Index Scan也不例外ExecInitBitmapIndexScan函数实现了Bitmap Index Scan的Init逻辑特定于Bitmap Index Scan扫描的数据结构struct BMScanOpaqueData主要是记录扫描过程中tid bitmap访问位置信息。 typedef struct BMScanOpaqueData {// 记录当前扫描的位置BMScanPosition bm_currPos;bool cur_pos_valid;/* XXX: should we pull out mark pos? */BMScanPosition bm_markPos;// bmmarkpos() -- save the current scan position. bool mark_pos_valid; } BMScanOpaqueData; MultiExecBitmapIndexScan函数实现了Exec逻辑主要通过调用index_getbitmap(scandesc, bitmap)函数获取bitmap然后返回bitmap给上一级算子。因为示例表的索引都是btree索引index_getbitmap指向的是btgetbitmap索引扫描函数。 btgetbitmap函数逻辑很简单首先调用_bt_first/_bt_next逐条获取item接着通过tbm_add_tuples添加到TIDBitmap里最终构建一个完整的bitmap核心就三个函数_bt_first/_bt_next/tbm_add_tuples。 _bt_first函数是索引扫描的起始首先会调用_bt_preprocess_keys预处理扫描键若扫描键条件无法满足则设置BTScanOpaque-qual_ok为false并提前结束扫描。若没找到有用的边界keys则需要调用_bt_endpoint从第一页开始否则调用_bt_search从BTree的root节点_bt_getroot开始扫描直至找到符合扫描键和快照的第一个叶子节点之后调用二分查找_bt_binsrch找到符合扫描键的页内item偏移最后将找到的页面载入到buffer中并返回tuple。 _bt_next函数会从当前页中尝试获取下一条tuple若当前页已没有tuple则调用_bt_steppage_bt_steppage会拿到下一页块号之后调用_bt_readnextpage读取该文件块中的内容之后_bt_next尝试从中获取下一条tuple。重复上述过程直至扫描结束。 tbm_add_tuples函数添加CTID构建TIDBitmap数据结构细节稍后讲解。 ExecEndBitmapIndexScan函数则用来清理相应的资源。 数据结构TIDBitmap 从上面的代码我们可以看到btgetbitmap会一次返回符合条件的所有tid组成的TIDBitmap而且TIDBitmap指向的内存区域大小是有限制的等于work_mem * 1024 Byte. work_mem默认值等于Heap Page Size。这些限制的存在使得TIDBitmap在设计上也有一些trade off。 typedef enum {TBM_EMPTY, /* no hashtable, nentries 0 */TBM_ONE_PAGE, /* entry1 contains the single entry */TBM_HASH /* pagetable is valid, entry1 is not */ } TBMStatus; typedef struct PagetableEntry {BlockNumber blockno; /* page number (hashtable key) */char status; /* hash entry status */bool ischunk; /* T lossy storage, F exact */bool recheck; /* should the tuples be rechecked? */tbm_bitmapword words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)]; } PagetableEntry; struct TIDBitmap {NodeTag type; /* to make it a valid Node */MemoryContext mcxt; /* memory context containing me */TBMStatus status; /* see codes above */struct pagetable_hash *pagetable; /* hash table of PagetableEntrys */int nentries; /* number of entries in pagetable */int nentries_hwm; /* high-water mark for number of entries */int maxentries; /* limit on same to meet maxbytes */int npages; /* number of exact entries in pagetable */int nchunks; /* number of lossy entries in pagetable */TBMIteratingState iterating; /* tbm_begin_iterate called? */uint32 lossify_start; /* offset to start lossifying hashtable at */PagetableEntry entry1; /* used when status TBM_ONE_PAGE *//* these are valid when iterating is true: */PagetableEntry **spages; /* sorted exact-page list, or NULL */PagetableEntry **schunks; /* sorted lossy-chunk list, or NULL */dsa_pointer dsapagetable; /* dsa_pointer to the element array */dsa_pointer dsapagetableold; /* dsa_pointer to the old element array */dsa_pointer ptpages; /* dsa_pointer to the page array */dsa_pointer ptchunks; /* dsa_pointer to the chunk array */dsa_area *dsa; /* reference to per-query dsa area */ }; 在这个数据结构中有几个重点需要关注的字段 TBMStatus status 字段 TBM_EMPTY代表当前TIDBitmap是空TBM_ONE_PAGE 代表 TIDBitmap中只有一个page的bitmap信息TBM_HASH 代表TIDBitmap中bitmap信息是存储在hash table里 为什么会有TBM_ONE_PAGE和TBM_HASH的区别因为如果TIDBitmap只存储一个PageTableEntry时不需要花费时间构建动态hash表查找时也不需要通过hash查找只需要返回entry1即可。 struct pagetable_hash pagetable page table是一个hash table按照page维度存储bitmaphash table的key是BlockNumber类型。value是一个PagetableEntry结构。一般来说可以使用hash table中的一个PagetableEntry用来存储一个page中哪些tid是符合查询需求的block no对应 page number, PagetableEntry中bitmap words的第n bit代表page中第n1个tuple。这样当我们构建bitmap时相同block no的tid会被聚合到同一个key对应PagetableEntry中btgetbitmap扫描完成所有存在的TID都按照page聚合类似图1的做法。 我们在前面看到TIDBitmap的容量使用是有限制的(tbm_create调用指定)如果一个page对应一个PageTableEntry在有大量page需要构建bitmap的时候内存使用肯定会超出。所以TIDBitmap里有一个maxentries字段代表TIDBitmap最多可以有多少个PageTableEntry结构来存储bitmap。 为了满足超出maxentries个 page的bitmap 标记需求当tbm_add_tuples添加tuple id时page数量超出maxentries bitmap就会进入调用tbm_lossify函数来使部分PageTableEntry从exact page 变成lossy page状态。即这些PageTableEntry不再代表某个page的bitmap而是代表一组page的状态。 PagetableEntry的数据结构在exact page和 lossy page状态下具有不同的含义 typedef struct PagetableEntry {BlockNumber blockno; /* page number (hashtable key) */char status; /* hash entry status */bool ischunk; /* T lossy storage, F exact */bool recheck; /* should the tuples be rechecked? */tbm_bitmapword words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)]; } PagetableEntry; Exact page状态PageTableEntry对应一个page的bitmap blockno 就是page numberischunk 等于false代表是exactwords 的第n bit代表page的offset n1的tupleLossy page状态PageTableEntry等于一组page的状态blockno 是一个chunk_pageno, 对应一组page。这一组page的page no范围就是chunk_page_nochunk_page_no PAGES_PER_CHUNK tbm_mark_page_lossy函数实现了PageTableEntry从exact page向lossy page的转换 ischunk等于true代表是lossywords的第n bit代表 page no chunk_page_no n 的这个page是有符合查询条件的tuple的 npages和nchunks字段分别代表hash table里有多少exact page和lossy page。 PagetableEntry **spages 和 PagetableEntry **schunks 这两个字段主要是在BitmapHeapScan阶段使用的spages和schunks在初始化bitamp iterator时构建主要过程就是遍历hash table根据exact page和lossy page的状态分别添加到spages和schunks里然后按照block no对spages和schunks进行排序。后续在Bitmap Heap Scan阶段就可以顺序访问了。 dsa_*相关变量都是和并行化扫描有关的。 了解数据结构后tbm_add_tuples要做的事情就很清晰了: 根据page no在hash table里找到相应的bucket然后找到对应的PageTableEntry(不存在则会创建)。但是如果此时只有一个PageTableEntry(即TBM_ONE_PAGE状态)则直接返回entry1变量不需要通过hash查找。拿到entry后根据tid解析出offset然后按照offset -1 去设置bitmap words的比特位即可。如果在步骤1中创建一个新的PageTableEntry发现npages的数量超出了tbm-maxentries的值则会调用tbm_lossify()函数将TIDBITMap中的部分PageTableEntry转换成lossy page同时按照exact page的减少和lossy page的增加相应的修改npages和nchunks的值。如果在步骤2中因为tbm_lossy后部分PageTableEntry是lossy状态的如果此时步骤2拿到的是一个lossy page则按照page no chunk_page_no n 的转换规则设置对应的n个比特位。如果最后tbm的全部entry都变成lossy状态了则会触发tbm的扩容操作。maxentries nentries * 2。 Bitmap Add BitmapAnd节点获取从BitmapIndexScan节点生成的位图并输出一个对所有输入位图进行AND操作的位图。 xiaoming# explain select * from t1 where a 10000 and b 10000;QUERY PLAN --------------------------------------------------------------------Bitmap Heap Scan on t1 (cost717.57..1469.55 rows211 width12)Recheck Cond: ((b 10000) AND (a 10000))- BitmapAnd (cost717.57..717.57 rows211 width0)- Bitmap Index Scan on idx_b_t1 (cost0.00..358.61 rows20557 width0)Index Cond: (b 10000)- Bitmap Index Scan on idx_a_t1 (cost0.00..358.61 rows20557 width0)Index Cond: (a 10000) Bitmap Or 同样的BitmapOr节点获取从BitmapIndexScan节点生成的位图并输出一个对所有输入位图进行OR操作的位图。其中第一个输入位图用于存储OR操作的结果并返回给调用者。 Bitmap Heap Scan Bitmap Heap Scan 采用Bitmap Index Scan生成的bitmap(或者经过 BitmapAnd 和 BitmapOr 节点通过一系列位图集操作后生成的bitmap)来查找相关数据。位图的每个page可以是精确的直接指向tuple的或有损的指向包含至少一行与查询匹配的page。 xiaoming# explain select * from t1 where a 10000;QUERY PLAN --------------------------------------------------------------------------------------------------Bitmap Heap Scan on t1 (cost363.74..11559.98 rows20557 width12) (actual time0.988..4.097 rows20000 loops1)Recheck Cond: (a 10000)Heap Blocks: exact110- ... Bitmap Index Scan || Bitmap And || Bitmap Or Bitmap Heap Scan阶段的实现函数BitmapHeapNext static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node) {//下级节点返回TIDBitmap结构tbm (TIDBitmap *) MultiExecProcNode(outerPlanState(node));node-tbm tbm;// 初始化扫描迭代器构造tbm-spages和tbm-schunks用于扫描// 并两个数组按照page id排序node-tbmiterator tbmiterator tbm_begin_iterate(tbm);node-tbmres tbmres tbm_iterate(tbmiterator);// 读取一个blocklossy page就是在这个case下处理的// 读取一整个heap page, 保存在scan的buffer中用于下次扫描table_scan_bitmap_next_block(scan, tbmres)// 如果是lossy的则会循环读取直到找到一个满足Filter条件的tuple.// 如果是exact的则直接根据tmbres中的当前entry指针直接seek// 到next offset指向的tuple..table_scan_bitmap_next_tuple(scan, tbmres, slot)return slot; } 代码核心逻辑如下 下层算子的执行函数MultiExecProcNode()返回了一个TIDBitmap调用tbm_generic_begin_iterate()基于tbm构建了一个iterator每次循环获取一个指向具体页面的TBMIterateResult结构通过table_scan_bitmap_next_block一次读取一个page到ScanDesc的rs_buffer里然后调用table_scan_bitmap_next_tuple根据TBMIterateResult里的偏移在内存buffer里获取相应的tuple根据bitmap中PageTableEntry的状态(即exact还是lossy), 读取指定的page如果是exact page则直接读取指定offset的tuple如果是lossy的则根据filter过滤掉不必要的tuple然后调用recheck检查其他查询条件调用recheck函数判断tuple是否满足查询条件不满足则继续next满足则返回tuple给上层节点。 代价分析 Bitmap Index Scan的代价估算就是Index Scan的访问索引数据的代价即如下计算公式 SELECT round(current_setting(random_page_cost)::real * pages current_setting(cpu_index_tuple_cost)::real * tuples current_setting(cpu_operator_cost)::real * tuples ) as bitmap_index_scan FROM (SELECT relpages * 0.01027850 AS pages, reltuples * 0.01027850 AS tuplesFROM pg_class WHERE relname idx_a_t1 ) c;bitmap_index_scan -------------------358 (1 row) Bitmap Heap Scan的cost计算比较复杂逻辑在cost_bitmap_heap_scan这个函数。首先是Bitmap Heap Scan读取page的数量的估算计算公式 图2page数量的估算公式 公式中的sel是选择率, 在我们这个例子里sel0.01027850。其次单个页面的获取成本估计在 seq_page_cost 和 random_page_cost 之间如果tid分布很松散则趋近于random_page_cost; 如果tid分布聚集性好则趋近于seq_page_cost。在同时存在exact和lossy bitmap时对于exact bitmap, 处理tuple的数量等于sel * reltuples。对于lossy bitmap则需要对lossy page上的每一个tuple进行recheck操作。在我们的例子中不会超出TID Bitmap的 max_entries数量所以我们所有的bitmap都是精确的。 下面这个SQL就是模拟Bitmap Scan的cost计算逻辑(entry全是exact page的情况)。计算的total_cost值11560刚好和我们前面Plan给出来的值11559.98相近。 WITH t AS (SELECT relpages,least((2 * relpages * reltuples * 0.01027850) /(2 * relpages reltuples * 0.01027850), relpages) AS pages_fetched,round(reltuples * 0.01027850) AS tuples_fetched,current_setting(random_page_cost)::real AS rnd_cost,current_setting(seq_page_cost)::real AS seq_costFROM pg_class WHERE relname t1 ), s AS(select pages_fetched, rnd_cost - (rnd_cost - seq_cost) *sqrt(pages_fetched / relpages) AS cost_per_page, tuples_fetched tuples_fetched from t ), costs(startup_cost, run_cost) AS (SELECT( SELECT round(358 /* child node cost */ 0.1 * current_setting(cpu_operator_cost)::real *reltuples * 0.01027850)FROM pg_class WHERE relname idx_a_t1),( SELECT round(cost_per_page * pages_fetched current_setting(cpu_tuple_cost)::real * tuples_fetched current_setting(cpu_operator_cost)::real * tuples_fetched)FROM s) ) SELECT startup_cost, run_cost, startup_cost run_cost AS total_cost FROM costs;startup_cost | run_cost | total_cost ------------------------------------363 | 11197 | 11560 (1 row) 位图扫描限制 目前大多数的分析场景都是基于一个前提存储的随机IO cost远远大于顺序 IO cost的基础上(在pg中是4倍的差距默认random_page_cost4, seq_page_cost1)。但是今天大量新的存储硬件或者云原生存储的使用场景下随机IO和顺序IO的差距已经不再是这种关系。当我们在启用bitmap scan的时候一定要仔细考虑这其中的问题。例如下面的场景 新型Nvme SSD的随机IO能力已经非常强顺序IO和随机IO的性能差异已经显著缩小io的延迟也很低。在存算分离架构中访问云存储的网络延迟、IO延迟比较大优化器要考虑IO合并和prefetching对cost的影响。云存储的IO Pattern也很并发的从不同节点读写具备更高的性能。单次IO请求的延迟比较高。 总结 正常情况下PostgreSQL 优化器可以选择出来一种最优的方式来执行但不保证总是可以生成最优化的执行计划。 任何优化都是一个系统工程而不是一个单点工程通过不同资源的消耗比例来提升整体性能Bitmap Scan也并非完美无瑕其优化理念是通过Bitmap的生成过程中增加内存和CPU成本来降低IO成本。 对于高性能存储或者内存资源充足的情况而言并不一定总是发生物理IO那么IO就不会成为瓶颈如果去做Bitmap的生成反倒是一种浪费。此时可以根据具体的IO能力考虑禁用Bitmap scan等方案从而实现整体计划的最优化。
http://www.zqtcl.cn/news/160290/

相关文章:

  • 做网站侵权腾讯企点打不开
  • iis 网站拒绝显示此网页上海网站建设类岗位
  • 营销型网站建设推荐google关键词
  • 网站上线是前端还是后端来做如何做垂直门户网站
  • 网站建设与管理2018海尔集团网站 建设目的
  • ps做网站大小wordpress调用 php文件
  • php网站忘记后台密码江苏网页制作报价
  • 网站模板 哪个好完备的常州网站推广
  • 衡水淘宝的网站建设濮阳市城乡一体化示范区主任
  • 公司网上注册在哪个网站商洛市商南县城乡建设局网站
  • 怎么才能让网站图文展示大连网站建设设计
  • 俱乐部网站 模板seo产品是什么意思
  • 新手学做网站的教学书建造师查询官网
  • win2012 iis添加网站群辉做网站服务器
  • 网站优化课程培训山东网站备案公司
  • top wang域名做网站好事业单位门户网站建设评价
  • 有什么网站可以做简历网站备案表格
  • 网站开发用什么图片格式最好厦门人才网个人会员
  • 关于网站开发的文献深圳网络推广代运营
  • 网站做app的重要性做静态网站有什么用
  • 一键搭建网站系统教做衣服的网站有哪些
  • 城乡建设部网站施工员证书查询中铁建设集团有限公司招标平台
  • 广东省建设项目安全标准自评网站哪个网站可以免费做简历
  • 带产品展示的个人网站模板购物网站开发背景
  • 哪个域名注册网站好seo广告投放是什么意思
  • 网站建设ydwzjs电子邮箱怎么申请
  • 福建省建住房建设部网站wordpress 算数验证码
  • 东莞企业如何建网站网站正在建设中...为什么护卫神
  • 引流用的电影网站怎么做wordpress浏览速度
  • 微信小程序怎拼做搬家网站东莞建网站公司