随着PostgreSQL-16beta1版本的发布,我们可以发现,对于我们时常使用的explain增加了一个GENERIC_PLAN选项。这个选项是允许了包含参数占位符的语句,如select * from tab01 where id=$1;等等这种语句,让其生成不依赖于这些参数值的通用计划。
这个补丁具体可见https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3c05284d83b230728e59a25e828992037ef77096
如下是PostgreSQL官方文档上对其的解释,意思是:允许语句包含像$1这样的参数占位符,并生成一个不依赖于这些参数值的通用计划。有关通用计划和支持参数的语句类型的详细信息,请参阅PREPARE。此参数不能与ANALYZE一起使用。默认为FALSE。
这段解释里提到了generic plans ,即通用计划。通用计划是查询计划的变体。它们与实际参数值无关。例如,当使用具有不同参数值的prepare语句时,在第 6 次执行时,它将决定是依赖通用计划还是为每组参数制定自定义计划。通用计划可以提供 explain 计划而不需要参数值,并且可以在执行中重复使用。
在PostgreSQL-16版本之前,虽然explain虽然没有GENERIC_PLAN选项,但是PostgreSQL-12版本开始就有了一个plan_cache_mode参数,允许用户使用强制custom plan或者强制使用plan cache,或自动模式(默认)。force_plan cache,类似于oracle的cursor_sharing = force。force_custom plan,可用在plan cache执行计划不准的场景。设置plan_cache_mode=force_generic_plan的时候,不用关注具体传入绑定变量的具体值是什么,优化器按照绑定变量去解析执行计划。所以当我们在分析数据库里的慢SQL的时候,可能在pg_stat_statements里获取到了一条绑定变量的SQL,有时候会临时session级别修改下plan_cache_mode=force_generic_plan,然后去查看下这条SQL的一个执行计划。即下边这样去做的。
但其实在生产上,使用auto_explain并且设置合理的log_min_duration_statement相对来说更有效,因为这样就会知道实际的查询计划,比通用计划的质量好得多。
而随着PostgreSQL-16beta1版本的发布,在explain里增加了一个GENERIC_PLAN的选项,我们可以更加简便的去获取通用执行计划。而不需要采用临时设置plan_cache_mode的方式。
但是通过上边GENERIC_PLAN的用法我们不难发现一个问题,在以前版本使用修改plan_cache_mode=force_generic_plan这种方式去获取计划的时候,其实也是给它传入了一个参数类型的,所以对于新版本的GENERIC_PLAN选项在使用的时候,某些时候可能需要显式指定参数的类型,可以通过强制转换来完成。这个PostgreSQL-16beta1的官方文档里也有提到。具体可参考:https://www.postgresql.org/docs/16/sql-explain.html