今天我们要探讨的是 custom执行计划和通用执行计划。这一技术在 Oracle中被称为绑定变量窥视。但 Postgresql中并没有这样的定义,更严格地说,Postgresql叫做custom执行计划和通用执行计划。

什么是custom执行计划,什么是通用执行计划,我们先来看一个例子,我创建了一个100011行的表,其中有两列分别为 id、 name。在name列就2种类型的值,一种值为“aaa”,有整整100000行, 而值为bbb列的仅有11行。这就是我们常说的数据倾斜。在oracle数据库中,配合绑定变量窥视我们常常需要收集倾斜列的直方图。

以下测试基于版本:

KingbaseES V008R006C005B0041 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46), 64-bit

create table a(id numeric,name varchar(40));
insert into a select i, 'aaa' from generate_series (1,100000) i;
insert into a select i, 'bbb' from generate_series (100001,100011) i;
create index idx_a1 on a(name);
analyze a;

下一步是使用 prepare语句。利用该方法可以避免对语句反复解析。这个功能类似oracle 的绑定变量,(一次硬解析后在library cache产生的执行计划可为以后sql通用。避免多次硬解析,这样找到相同的执行计划planhash value叫做软解析。当然还有软软解析,这里略过。)

test=# prepare test_stmt as select * from a where name = $1;
PREPARE
select * from pg_prepared_statements;

我们执行如下语句,连续6次都查询name为'aaa'的数据。注意是6次。

test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Seq Scan on a (cost=0.00..1794.14 rows=99994 width=10) (actual time=0.009..25.862 rows=100000 loops=1)
Filter: ((name)::text = 'aaa'::text)
Rows Removed by Filter: 11
Planning Time: 0.217 ms
Execution Time: 34.710 ms
(5 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Seq Scan on a (cost=0.00..1794.14 rows=99994 width=10) (actual time=0.009..16.401 rows=100000 loops=1)
Filter: ((name)::text = 'aaa'::text)
Rows Removed by Filter: 11
Planning Time: 0.073 ms
Execution Time: 23.340 ms
(5 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Seq Scan on a (cost=0.00..1794.14 rows=99994 width=10) (actual time=0.009..30.001 rows=100000 loops=1)
Filter: ((name)::text = 'aaa'::text)
Rows Removed by Filter: 11
Planning Time: 0.093 ms
Execution Time: 39.383 ms
(5 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Seq Scan on a (cost=0.00..1794.14 rows=99994 width=10) (actual time=0.009..23.365 rows=100000 loops=1)
Filter: ((name)::text = 'aaa'::text)
Rows Removed by Filter: 11
Planning Time: 0.073 ms
Execution Time: 32.397 ms
(5 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Seq Scan on a (cost=0.00..1794.14 rows=99994 width=10) (actual time=0.009..19.287 rows=100000 loops=1)
Filter: ((name)::text = 'aaa'::text)
Rows Removed by Filter: 11
Planning Time: 0.099 ms
Execution Time: 27.462 ms
(5 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.082..35.540 rows=100000 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.114 ms
Execution Time: 45.546 ms
(4 rows)

由于 aaa占用了该表的大部分数据,因此优化器选择使用全表扫描,这是优化器的算法决定的,这也存在合理性。在第六次的时候,请注意 Filter部分,(name)::text = 'aaa'::text变为 text=$1。此时优化器将生成通用执行计划,并使用绑定变量。那么之前的5次则被称为 custom执行计划。为什么第六次才生成通用执行计划?我们可以在 PostgreSQL的 plancache. c源码中找到说明:

The logic for choosing generic or custom plans is in choose_custom_plan

在choose_custom_plan函数里我们可以看到/* Generate costom plans until we have done at least 5 (arbitrary)*/ if (planaource->num_custom_plans < 5) return true;

请注意,这里的限定值小于5次,返回 true,选择 custom执行计划,而大于5次之后,则选择通用执行计划。因此,5次之后执行计划就会固定。为什么第六次使用通用执行计划,执行计划却改为索引扫描的方式?实际上这和一个参数有关plan_cache_mode。目前查看参数值时auto。

test=# show plan_cache_mode;
plan_cache_mode
-----------------
auto
(1 row)

在参数是auto的前提下,不管我执行aaa或bbb的列值,执行计划都是一样,执行计划固定了。如果每次不管变量值怎么变化,都选择索引扫描方式,显然这不是我们想要的。因为数据倾斜,如果执行计划不变,那么是不明智的,会出现低效解析行为。

如下,使用通用执行计划后,我们关注不管索引扫描还是全表扫描,预估cost值是50006,有意思的是这个值是实际rows的一半。从第六次执行计划开始,这个cost就没再变过,显然这是不合理的。当然有可能优化器认为这种算法对于不同的扫描方式对应的Execution Time差的不是很多,所以固定执行计划为通用执行计划。

还有一个关键是使用通用执行计划后Planning Time很小,这是否说明了”软解析的功能呢!“生成执行计划时间大大减少。


test=# explain (analyze) execute test_stmt ('bbb');
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.021..0.024 rows=11 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.015 ms
Execution Time: 0.048 ms
(4 rows) test=# explain (analyze) execute test_stmt ('bbb');
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.020..0.022 rows=11 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.014 ms
Execution Time: 0.041 ms
(4 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.032..23.592 rows=100000 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.013 ms
Execution Time: 31.333 ms
(4 rows)

设置 plan_cache_mode=force_custom_plan

继续测试另外一种情况,将plan_cache_mode设置为force_custom_plan。可以看到执行计划会根据绑定变量的值的分布进行变化,这种情况执行计划是合理的。但是代价是每次执行都要重新解析语句,我们知道在oracle里这叫硬解析,都听说过一句话,硬解析是万恶之源!对应的在Kingbase里数据倾斜,谓词条件经常变化,最好使用custom执行计划。

set plan_cache_mode=force_custom_plan;

test=# explain (analyze) execute test_stmt ('bbb');
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..8.65 rows=13 width=10) (actual time=0.020..0.022 rows=11 loops=1)
Index Cond: ((name)::text = 'bbb'::text)
Planning Time: 0.079 ms
Execution Time: 0.036 ms
(4 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Seq Scan on a (cost=0.00..1794.14 rows=99998 width=10) (actual time=0.010..16.897 rows=100000 loops=1)
Filter: ((name)::text = 'aaa'::text)
Rows Removed by Filter: 11
Planning Time: 0.077 ms
Execution Time: 24.020 ms
(5 rows)

设置 plan_cache_mode=force_generic_plan

可以看到,这种情况下,执行计划就被固定了。和最开始执行到第六次的执行计划一样,不管条件怎么变化,优化器都采用了通用执行计划。

test=# set plan_cache_mode =force_generic_plan ;
test=# explain (analyze) execute test_stmt ('bbb');
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.022..0.024 rows=11 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.016 ms
Execution Time: 0.044 ms
(4 rows) test=# explain (analyze) execute test_stmt ('bbb');
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.032..0.035 rows=11 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.015 ms
Execution Time: 0.055 ms
(4 rows) test=# explain (analyze) execute test_stmt ('aaa');
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_a1 on a (cost=0.42..1710.52 rows=50006 width=10) (actual time=0.037..23.191 rows=100000 loops=1)
Index Cond: ((name)::text = $1)
Planning Time: 0.016 ms
Execution Time: 30.997 ms
(4 rows) 关闭prepare语句
deallocate all;

结论:

如果在Kingbase中使用prepare语句(类似绑定变量功能),

对于数据分布均匀,且参数经常改变的情况适合使用这个功能。

建议对于数据倾斜的情况,将plan_cache_mode设置为force_custom_plan。或者不用这个功能。

当然在实现任何功能前还是建议进行充分测试。

PostgreSQL 绑定变量浅析的更多相关文章

  1. PostgreSQL 绑定变量窥探

    今天我们要探讨的是 custom执行计划和通用执行计划.这一技术在 Oracle中被称为绑定变量窥视.但 Kingbase中并没有这样的定义,更严格地说,Kingbase叫做custom执行计划和通用 ...

  2. Error querying database. Cause: java.sql.SQLException: ORA-01745: 无效的主机/绑定变量名

    今天调试程序是遇到了,下面的一个问题.我将对应的SQL语句拿到Toad下也能正常的执行,感觉有点莫名其妙,根据异常信息的提示查看对应的映射结果集也没发现错误,然后百度了一下,也有许多朋友也遇到过这样的 ...

  3. OTL翻译(5) -- otl_stream流相关绑定变量

    声明绑定变量 本章节将详细的说明如何在otl_stream流里面声明绑定变量. SQL语句.SQL语句块或存储过程在程序里面使用的时候总是带有占位符.OTL里面带有一个小的解析器用来解析这些占位符,并 ...

  4. otl_stream流相关绑定变量

    声明绑定变量 本章节将详细的说明如何在otl_stream流里面声明绑定变量. SQL语句.SQL语句块或存储过程在程序里面使用的时候总是带有占位符.OTL里面带有一个小的解析器用来解析这些占位符,并 ...

  5. Oracle --获取绑定变量的值.

    SELECT * FROM DBA_HIST_SQLBIND WHERE SNAP_ID>67073 AND SNAP_ID<=67079 AND SQL_ID='3DR3410F086P ...

  6. ORACLE绑定变量隐式转换导致性能问题

    年后一次系统升级后,监控数据库的工具DPA发现数据库的Total Wait时间突然飙增,如下截图所示,数据库的总体等待时间对比升级前飙增了非常多 另外就是发现出现了较多的等待事件,主要有latch: ...

  7. 使用EXECUTE IMMEDIATE来生成含有绑定变量的SQL

    一个SQL,通过SPM固定它的执行计划,可以通过DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE实现.也可以通地此功能在不修改原SQL的情况下对其加HINT来固定执行计划.D ...

  8. MYBATIS报ORA-01745: 无效的主机/绑定变量名 异常

    异常:Cause: java.sql.SQLSyntaxErrorException: ORA-01745: 无效的主机/绑定变量名 原因,sql语句中,两个填充变量间没有写逗号.

  9. Oracle绑定变量

    select * from table where id = ? 类似于上面这样的sql,如果不用绑定变量,每次执行时Oracle会认为是不同的sql,会在每次执行时生成一遍执行计划,而执行计划的生成 ...

  10. PLSQL_性能优化系列07_Oracle Parse Bind Variables解析绑定变量

    2014-09-25 Created By BaoXinjian

随机推荐

  1. swiper3踩坑

    1. 公司一个项目里用的 swiper3 然后同一项目下还装了vue-awesome-swiper需求swiper4...研究半天终于搞明白第一个问题 2. 然后我寻思就按照swiper3官网文档来写 ...

  2. 数据科学家赚多少?基于pandasql和plotly的薪资分析与可视化 ⛵

    作者:韩信子@ShowMeAI 数据分析实战系列:https://www.showmeai.tech/tutorials/40 AI 岗位&攻略系列:https://www.showmeai. ...

  3. A_A01_001 KEIL4-KEIL5软件安装

    @ 目录 一.软件下载 二.交流学习 三.防止电脑误删文件操作步骤 四.KEIL4安装 五.KEIL5安装 六.注意事项 一.软件下载 KEIL4/KEIL5网盘链接 戳它跳转 提取码:omni 二. ...

  4. 现代 CSS 高阶技巧,不规则边框解决方案

    本文是 CSS Houdini 之 CSS Painting API 系列第四篇. 现代 CSS 之高阶图片渐隐消失术 现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 现代 CSS ...

  5. css images图片铺满 不变型 以及头像裁剪 属性

    一,图片的引入 background:url(img_flwr.gif); background-repeat:no-repeat; //平铺 二,图片的大小不不变形 background-size: ...

  6. JavaScript:代码应该编写在哪里?

    我们可以将JS的代码,编写在三个地方. 但是无论编写在哪里,最后它都会嵌入进网页代码中,被浏览器执行. 编写在script标签中 我们可以直接在HTML的script标签中,编写大段JS代码. 编写在 ...

  7. 网络编程 UDP套接字

    第十二章 UDP套接字 12.1 前言 上一章讲述了TCP通信方式,它是基于流的面向连接的网络通信.UDP是IP协议上的另一种传输协议. TCP和UDP都是端到端的通信协议,都处于TCP/IP网络模型 ...

  8. 含辞未吐,声若幽兰,史上最强免费人工智能AI语音合成TTS服务微软Azure(Python3.10接入)

    所谓文无第一,武无第二,云原生人工智能技术目前呈现三足鼎立的态势,微软,谷歌以及亚马逊三大巨头各擅胜场,不分伯仲,但目前微软Azure平台不仅仅只是一个PaaS平台,相比AWS,以及GAE,它应该是目 ...

  9. APP上架因收集个人信息问题被拒绝该怎么解决?

    近年来,随着信息技术的快速发展和移动互联网应用的普及,越来越多的应用大量收集.使用个人信息,给人们生活带来便利的同时,也出现了对个人信息的非法收集.滥用.泄漏等问题,个人信息安全面临严重威胁. 201 ...

  10. [seaborn] seaborn学习笔记2-散点图Scatterplot

    2 散点图Scatterplot(代码下载) 散点图能够显示2个维度上2组数据的值.每个点代表一个观察点.X(水平)和Y(垂直)轴上的位置表示变量的值.研究这两个变量之间的关系是非常有用的.在seab ...