本期问答如下:
- 备份session超时问题
- rm清理归档无影响
- 阶乘操作符已废弃
- 参数化语句分析
Q1.基础备份session超时
问题描述
使用pg_basebackup或pg_probackup第三方备份工具进行备份时提示空闲会话超时,错误信息如下:
FATAL: terminating connection due to idle-session timeout
问题解答
执行基础备份时,会调用pg_start_backup、pg_stop_backup、replication命令或progress进度函数进行query查询,在PostgreSQL 14或以上版本如果数据库设置了session超时参数(版本14引入的新参数)idle_session_timeout,那基础备份发起的query查询会超时失败。
如果用户环境因为安全性要求设置了空闲会话超时,可以单独对备份用户设置该参数,参考命令如下:
alter user backup set session_timeout to 0;
然后使用backup用户进行备份。
Q2.清理归档WAL文件使用rm进行删除对数据库有什么影响。
在PostgreSQL数据库里可以使用pg_archivecleanup工具进行归档清理,也可以自己写脚本进行删除。
对于使用脚本直接rm删除归档的方式,Oracle同事会存在疑虑:
不通过数据库工具pg_archivecleanup进行操作,数据库是否能感知,数据库是否会认为并没有删除呢?
通过archive_command归档的WAL文件存储在于外部路径,也没有与PGDATA有任何关联,不会与PostgreSQL数据库有直接影响,归档清理通常可以在做完PITR时间点恢复后再调用pg_archivecleanup工具去清理不需要的WAL文件。
另外归档配置除了archive_command,从PostgreSQL 15开始也可以尝试basic_archive模块提供的archive_library参数进行更加高效的归档管理。
Q3.阶乘操作符"!"不支持
问题描述
在PostgreSQL 14之前可以阶乘操作符"!",如下所示:
psql (13.10)
Type "help" for help.
postgres=# select 3!;
┌──────────┐
│ ?column? │
├──────────┤
│ 6 │
└──────────┘
(1 row)
在PostgreSQL 14或以上版本执行则会提示错误:
psql (15.3)
Type "help" for help.
postgres=# select 3!;
ERROR: syntax error at or near ";"
LINE 1: select 3!;
^
问题解答
查看特性提交邮件,PostgreSQL 14之前阶乘有三种方式可以使用:操作符"!"、factorial函数、numeric_fac函数。
"!"操作符是内置postfix风格的操作符,通常我们应该使用factorial函数,numeric_fac函数是一个SQL别名函数。
从PostgreSQL 14版本开始只保留factorial函数,因此最通用的使用方式是使用factorial函数。
postgres=# select factorial(3);
┌───────────┐
│ factorial │
├───────────┤
│ 6 │
└───────────┘
(1 row)
Q4.参数化语句如何分析性能
问题描述
当我们分析查询语句的性能时,如果不知道参数的值,很难使用EXPLAIN分析执行计划。
postgres=# explain SELECT oid FROM pg_class WHERE relname = $1;
ERROR: there is no parameter $1
LINE 1: explain SELECT oid FROM pg_class WHERE relname = $1;
^
问题解答
使用扩展查询语句可以分离SELECT, INSERT, UPDATE, DELETE语句(不包括ALTER)和语句中使用的常量,参数化语句可以增强安全性,它可以防止SQL注入,同时也可以提升性能。因为PostgreSQL根据plan_cache_mode的默认值可以在单个会话里基于6次启发式缓存参数化语句的通用执行计划。
不过在偏OLAP场景下,运行耗时的分析查询,获得最佳的执行计划更加重要,此时我们可以把plan_cache_mode的默认值auto调整为force_custom_plan。
在日常的数据库日志或pg_stat_statements中发现问题语句,为了便于EXPLAIN获得执行计划分析参数化语句的性能,可以设置plan_cache_mode的值为force_generic_plan,借助通用执行计划来忽略参数的值。
针对上面的语句,我们先使用PREPARE创建参数化语句:
PREPARE stmt(unknown) AS SELECT oid FROM pg_class WHERE relname = $1;
这里假定不知道参数的数据类型,使用伪类型"unknown",稍后让数据库根据上下文解析合适的数据类型。
再设置plan_cache_mode为"force_generic_plan"
SET plan_cache_mode = force_generic_plan;
最后使用NULL作为参数执行
postgres=# EXPLAIN EXECUTE stmt(NULL);
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├───────────────────────────────────────────────────────────────────────────────────────────┤
│ Index Scan using pg_class_relname_nsp_index on pg_class (cost=0.28..8.29 rows=1 width=4) │
│ Index Cond: (relname = $1) │
└───────────────────────────────────────────────────────────────────────────────────────────┘
(2 rows)
而即将发布的PostgreSQL 16对EXPLAIN增加了一个generic_plan选项,可以更加简便的获取通用执行计划,如下:
postgres=# EXPLAIN(generic_plan) SELECT oid FROM pg_class WHERE relname = $1;
┌───────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├───────────────────────────────────────────────────────────────────────────────────────────┤
│ Index Scan using pg_class_relname_nsp_index on pg_class (cost=0.27..8.29 rows=1 width=4) │
│ Index Cond: (relname = $1) │
└───────────────────────────────────────────────────────────────────────────────────────────┘
(2 rows)
最后推荐使用pgbadger日志分析工具,借助–dump-all-queries选项可以把参数值自动替换到语句中,再进行分析。
参考如下:

https://kmoppel.github.io/2023-06-07-til-filling-prepared-statement-placeholders-automatically-with-pgbadger/




