PostgreSQL 11 preview - 增加强制custom plan GUC开关(plancache_mode),对付倾斜

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介:

标签

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


背景

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

《PostgreSQL 11 preview - 强制auto prepared statment开关(自动化plan cache)(类似Oracle cursor_sharing force)》

但是对于比较复杂的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,但是并不一定适合所有场景,有时候这种倾斜会一直下去导致执行计划不正确。可以看如下文章中的例子。

《PostgreSQL plan cache 源码浅析 - 如何确保不会计划倾斜》

《执行计划选择算法 与 绑定变量 - PostgreSQL prepared statement: SPI_prepare, prepare|execute COMMAND, PL/pgsql STYLE: custom & generic plan cache》

那么如何解决这个问题?

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

1、数据库端prepare, execute。

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

3、使用UDF函数。

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

《PostgreSQL 11 preview - 强制auto prepared statment开关(自动化plan cache)(类似Oracle cursor_sharing force)》

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

https://commitfest.postgresql.org/17/990/

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:《PostgreSQL Oracle 兼容性之 - DBMS_SQL(存储过程动态SQL中使用绑定变量)》

参考

《PostgreSQL plan cache 源码浅析 - 如何确保不会计划倾斜》

《执行计划选择算法 与 绑定变量 - PostgreSQL prepared statement: SPI_prepare, prepare|execute COMMAND, PL/pgsql STYLE: custom & generic plan cache》

《PostgreSQL 11 preview - 强制auto prepared statment开关(自动化plan cache)(类似Oracle cursor_sharing force)》

https://commitfest.postgresql.org/17/990/

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
SQL 弹性计算 关系型数据库
PostgreSQL 12 preview - CTE 增强,支持用户语法层控制 materialized 优化
标签 PostgreSQL , CTE , materialized , not materialized , push down 背景 PostgreSQL with 语法,能跑非常复杂的SQL逻辑,包括递归,多语句物化计算等。 在12以前的版本中,WITH中的每一个CTE(common table express),都是直接进行物化的,也就是说外层的条件不会推到CTE(物化节点)里
996 0
|
弹性计算 关系型数据库 数据库连接
PostgreSQL 12 preview - Move max_wal_senders out of max_connections for connection slot handling
标签 PostgreSQL , max_wal_senders , max_connections , sorry, too many clients already 背景 如果你需要使用PG的流复制,上游节点的max_wal_senders参数,用来限制这个节点同时最多可以有多少个wal sender进程。 包括逻辑复制、物理复制、pg_basebackup备份等,只要是使用stre
374 0
|
弹性计算 安全 关系型数据库
PostgreSQL 12 preview - 可靠性提升 - data_sync_retry 消除os层write back failed status不可靠的问题
标签 PostgreSQL , data_sync_retry , write back , retry , failed status 背景 有些OS系统,对fsync的二次调用不敏感,因为OS层可能有自己的CACHE,如果使用了buffer write,并且出现write back failed的情况,有些OS可能在下次fsync时并不能正确的反馈fsync的可靠性与否。(因为这个B
567 0
|
SQL 缓存 Java
修改PostgreSQL字段长度导致cached plan must not change result type错误
修改PostgreSQL字段长度可能导致cached plan must not change result type错误
7275 0
|
SQL Oracle 算法
PostgreSQL 12 preview - plan_cache_mode参数控制强制使用plan cache或强制custom plan (force_custom_plan and force_generic_plan)
标签 PostgreSQL , plan_cache_mode 背景 plan cache在OLTP中,可以大幅降低生成sql parser, 执行计划的开销。 但是在某些场景中,plan cache可能成为问题,比如AP类型的场景中,由于SQL 输入条件的变化(通常AP业务涉及的条件可能比较容易出现这样的问题),可能导致plan cache并不是最佳的执行计划。
1383 0
|
物联网 关系型数据库 数据库
|
关系型数据库 分布式数据库 PolarDB
《阿里云产品手册2022-2023 版》——PolarDB for PostgreSQL
《阿里云产品手册2022-2023 版》——PolarDB for PostgreSQL
362 0

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版