博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PostgreSQL 11 preview - 增加强制custom plan GUC开关(plancache_mode),对付倾斜
阅读量:6469 次
发布时间:2019-06-23

本文共 5227 字,大约阅读时间需要 17 分钟。

标签

PostgreSQL , plan cache , generic plan , custom plan , plancache_mode


背景

对于高并发的小事务,使用绑定变量(prepared statement)来缓存执行计划,可以降低简单SQL在sql parser, plan上的开销。

但是对于比较复杂的SQL或者说输入的参数会导致执行计划倾斜的SQL,使用绑定变量会导致性能抖动。

例子,下面的数据在ID=1上面有非常严重的倾斜,其他值比较均匀。

create table test (id int , info text, crt_time timestamp);    insert into test select generate_series(1,1000000);    insert into test select 1 from generate_series(1,10000000);

因此select count(*) from test where id=1时,可能用全表扫描更合适。而当id=其他值时,使用索引更好。

PostgreSQL在使用CACHED PLAN时,依旧会使用传入参数代入CACHE PLAN进行计算,得到成本,然后对比之前5次custom plan的成本,如果相差较大(有阈值),则会重新发起custom plan,但是并不一定适合所有场景,有时候这种倾斜会一直下去导致执行计划不正确。可以看如下文章中的例子。

那么如何解决这个问题?

我们知道数据库有几种使用绑定变量的方法:

1、数据库端prepare, execute。

2、驱动层使用prepare, execute接口。

3、使用UDF函数。

4、设置强制使用prepared statement的开关。

PostgreSQL 11可能引入一个GUC参数,可以让你强制使用custom plan。因为PATCH还没有提交,最后提交的时候会怎么实现现在还不得而知。

Hi,    this patch is based on discussions related to plpgsql2 project.    Currently we cannot to control plan cache from plpgsql directly. We can use  dynamic SQL if we can enforce oneshot plan - but it means little bit less  readable code (if we enforce dynamic SQL from performance reasons). It  means so the code cannot be checked by plpgsql check too.    The plan cache subsystem allows some control by options  CURSOR_OPT_GENERIC_PLAN and CURSOR_OPT_CUSTOM_PLAN. So we just a interface  how to use these options from PLpgSQL. I used Ada language feature (used in  PL/SQL too) - PRAGMA statement. It allows to set compiler directives. The  syntax of PRAGMA statements allows to set a level where entered compiler  directive should be applied. It can works on function level or block level.    Attached patch introduces PRAGMA plan_cache with options: DEFAULT,  FORCE_CUSTOM_PLAN, FORCE_GENERIC_PLAN. Plan cache is partially used every  time - the parser/analyzer result is cached every time.    Examples:    CREATE OR REPLACE FUNCTION foo(a int)  RETURNS int AS  $$  DECLARE ..  BEGIN       DECLARE       /* block level (local scope) pragma */       PRAGMA plan_cache(FORCE_CUSTOM_PLAN);     BEGIN       SELECT /* slow query - dynamic sql is not necessary */     END;     END;    Benefits:    1. remove one case where dynamic sql is necessary now - security, static  check  2. introduce PRAGMAs - possible usage: autonomous transactions, implicit  namespaces settings (namespace for auto variables, namespace for function  arguments).    Comments, notes?    Regards    Pavel

PATCH新增参数如下

+static const struct config_enum_entry plancache_mode_options[] = {  +	{"default", PLANCACHE_DEFAULT, false},  +	{"force_generic_plan", PLANCACHE_FORCE_GENERIC_PLAN, false},  +	{"force_custom_plan", PLANCACHE_FORCE_CUSTOM_PLAN, false},  +	{NULL, 0, false}  +};

参数的使用方法

+--  +-- Test plan cache strategy  +--  +create table test_strategy(a int);  +insert into test_strategy select 1 from generate_series(1,1000) union all select 2;  +create index on test_strategy(a);  +analyze test_strategy;  +prepare test_strategy_pp(int) as select count(*) from test_strategy where a = $1;  +-- without 5 evaluation pg uses custom plan  +explain (costs off) execute test_strategy_pp(2);  +                            QUERY PLAN                              +------------------------------------------------------------------  + Aggregate  +   ->  Index Only Scan using test_strategy_a_idx on test_strategy  +         Index Cond: (a = 2)  +(3 rows)  +  +-- we can force to generic plan  +set plancache_mode to force_generic_plan;  +explain (costs off) execute test_strategy_pp(2);  +           QUERY PLAN              +---------------------------------  + Aggregate  +   ->  Seq Scan on test_strategy  +         Filter: (a = $1)  +(3 rows)  +  +-- we can fix generic plan by 5 execution  +set plancache_mode to default;  +execute test_strategy_pp(1); -- 1x  + count   +-------  +  1000  +(1 row)  +  +execute test_strategy_pp(1); -- 2x  + count   +-------  +  1000  +(1 row)  +  +execute test_strategy_pp(1); -- 3x  + count   +-------  +  1000  +(1 row)  +  +execute test_strategy_pp(1); -- 4x  + count   +-------  +  1000  +(1 row)  +  +execute test_strategy_pp(1); -- 5x  + count   +-------  +  1000  +(1 row)  +  +-- we should to get really bad plan  +explain (costs off) execute test_strategy_pp(2);  +           QUERY PLAN              +---------------------------------  + Aggregate  +   ->  Seq Scan on test_strategy  +         Filter: (a = $1)  +(3 rows)  +  +-- but we can force to custom plan  +set plancache_mode to force_custom_plan;  +explain (costs off) execute test_strategy_pp(2);  +                            QUERY PLAN                              +------------------------------------------------------------------  + Aggregate  +   ->  Index Only Scan using test_strategy_a_idx on test_strategy  +         Index Cond: (a = 2)  +(3 rows)  +  +drop table test_strategy;

小结

通过设置一个开关,可以让原本使用generic plan的SQL,强制使用custom plan。

比如我们的SQL如果本身就是分析型(或者说本身就是SLOW SQL)的情况下,实际上generic plan带来的好处是微乎其微的,反而可能因为generic plan用的是cache plan会带来不便。因为generic plan的执行计划被固定,特别不适合那种因为输入条件的变化而导致执行计划变化的SQL(通常是复杂SQL)。这种情况下,我们就可以考虑强制使用custom plan.

那么有人会说,在UDF中使用动态SQL不就好了吗(动态SQL每次都需要custom plan),要知道动态SQL在UDF中是不被CHECK的,所以可能出一些语法或者什么问题,很难检查.而使用这个开关,我们就不用担心这个问题了.即能检查SQL语法,又能强制使用custom plan。

一个反例是动态SQL中使用cache plan:

参考

转载地址:http://ycjko.baihongyu.com/

你可能感兴趣的文章
最容易理解的对卷积(convolution)的解释
查看>>
《机器学习实战》知识点笔记目录
查看>>
完美解决NC502手工sql的查询引擎排序及合计问题
查看>>
PE文件之资源讲解
查看>>
windows 7/mac编译cocos2d-x-3.2*的android工程报错
查看>>
MYSQL导入导出.sql文件(转)
查看>>
git review报错一例
查看>>
《信息安全系统设计基础》 课程教学
查看>>
Linux平台下使用rman进行oracle数据库迁移
查看>>
全栈工程师学习Linux技术的忠告
查看>>
iOS自定制tabbar与系统的tabbar冲突,造成第一次点击各个item图片更换选中,第二次选中部分item图片不改变...
查看>>
C# Dictionary用法总结
查看>>
SVN服务器使用(二)
查看>>
反射获取内部类以及调用内部类方法
查看>>
C语言 - pthread
查看>>
App里面如何正确显示用户头像
查看>>
DATAGUARD维护:从库宕机后如何恢复到管理恢复模式
查看>>
U-BOOT之一:BootLoader 的概念与功能
查看>>
我的路上
查看>>
Velocity处理多余空白和多余空白行问题
查看>>