PostgreSQL的学习心得和知识总结(一百六十三)|深入理解PostgreSQL数据库之 GUC参数compute_query_id 的使用和实现


注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往
7、pg百科 compute_query_id,点击前往


1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文内容基于PostgreSQL 17.0源码开发而成


深入理解PostgreSQL数据库之 GUC参数compute_query_id 的使用和实现

  • 文章快速说明索引
  • 功能使用背景说明
  • 功能实现源码解析



文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


学习内容:(详见目录)

1、深入理解PostgreSQL数据库之 GUC参数compute_query_id 的使用和实现


学习时间:

2024年12月11日 20:49:11


学习产出:

1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习


注:下面我们所有的学习环境是Centos8+PostgreSQL master+Oracle19C+MySQL8.0

postgres=# select version();version                                    
------------------------------------------------------------------------------PostgreSQL 17.0 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.1.0, 64-bit
(1 row)postgres=##-----------------------------------------------------------------------------#SQL> select * from v$version;          BANNER        Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
BANNER_FULL	  Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0	
BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
CON_ID 0#-----------------------------------------------------------------------------#mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.06 sec)mysql>

功能使用背景说明

最近在学习pg_hint_plan源码的时候,看到了如下GUC的要求:

// contrib/pg_hint_plan/pg_hint_plan.c...DefineCustomBoolVariable("pg_hint_plan.enable_hint_table","Let pg_hint_plan look up the hint table.",NULL,&pg_hint_plan_enable_hint_table,false,PGC_USERSET,0,enable_hint_table_check,assign_enable_hint_table,NULL);
...

该GUC参数的check函数,如下:

static bool
enable_hint_table_check(bool *newval, void **extra, GucSource source)
{if (*newval){EnableQueryId();if (!IsQueryIdEnabled()){GUC_check_errmsg("table hint is not activated because queryid is not available");GUC_check_errhint("Set compute_query_id to on or auto to use hint table.");return false;}}return true;
}

这里的要求就是:

由于 queryid 不可用,因此未激活表提示


compute_query_id 设置为 on 或 auto 以使用提示表。


其中compute_query_id是在PostgreSQL14的时候引入的。接下来我们先把这块内容分析完成之后,再回头去学习pg_hint_plan源码。下面是由大管家引入时候的相关提交记录:

在这里插入图片描述

Make use of in-core query id added by commit 5fd9dfa5f5Use the in-core query id computation for pg_stat_activity,
log_line_prefix, and EXPLAIN VERBOSE.Similar to other fields in pg_stat_activity, only the queryid from the
top level statements are exposed, and if the backends status isn't
active then the queryid from the last executed statements is displayed.Add a %Q placeholder to include the queryid in log_line_prefix, which
will also only expose top level statements.For EXPLAIN VERBOSE, if a query identifier has been computed, either by
enabling compute_query_id or using a third-party module, display it.Bump catalog version.Discussion: https://postgr.es/m/20210407125726.tkvjdbw76hxnpwfi@nolAuthor: Julien RouhaudReviewed-by: Alvaro Herrera, Nitin Jadhav, Zhihong Yu

翻译一下,其目的就是:

  • 利用提交 5fd9dfa5f5 添加的内核查询 ID
  • 使用 pg_stat_activity、log_line_prefix 和 EXPLAIN VERBOSE 的核心内查询 ID 计算
  • 与 pg_stat_activity 中的其他字段类似,仅显示来自顶级语句的 queryid,如果后端状态不活动,则显示来自最后执行的语句的 queryid
  • 添加 %Q 占位符以将 queryid 包含在 log_line_prefix 中,这也将仅显示顶级语句
  • 对于 EXPLAIN VERBOSE,如果已计算查询标识符(通过启用 compute_query_id 或使用第三方模块),则显示它

如上,在PostgreSQL14将原pg_stat_statements插件的Query Identifier计算模块剥离到内核中, 使得内部可以直接使用query id功能。那么query id是什么呢?

  • 例如多条sql支持某些输入的条件不一样,其他部分都一样,可以认为是同类sql,那么通过query id来表达会比较方便
  • 注意指的不是绑定变量的sql
  • 同样需要注意,queryid和sqlid是两个概念。关于sqlid的解释 我们后面再找机会详解

SHA-1: 5fd9dfa5f50e4906c35133a414ebec5b6d518493* Move pg_stat_statements query jumbling to core.Add compute_query_id GUC to control whether a query identifier should be
computed by the core (off by default).  It's thefore now possible to
disable core queryid computation and use pg_stat_statements with a
different algorithm to compute the query identifier by using a
third-party module.To ensure that a single source of query identifier can be used and is
well defined, modules that calculate a query identifier should throw an
error if compute_query_id specified to compute a query id and if a query
idenfitier was already calculated.Discussion: https://postgr.es/m/20210407125726.tkvjdbw76hxnpwfi@nolAuthor: Julien RouhaudReviewed-by: Alvaro Herrera, Nitin Jadhav, Zhihong Yu

翻译一下:

  • 添加 compute_query_id GUC 以控制查询标识符是否应由核心计算(默认情况下关闭)。因此,现在可以禁用核心 queryid 计算,并使用 pg_stat_statements 和不同的算法通过第三方模块来计算查询标识符。
  • 为了确保可以使用单一的查询标识符源并且定义良好,如果指定 compute_query_id 来计算查询 ID 并且已经计算了查询 ID,则计算查询标识符的模块应该抛出错误。

下面看一下使用示例1,如下:

postgres=# select version();version                                    
------------------------------------------------------------------------------PostgreSQL 17.0 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.1.0, 64-bit
(1 row)postgres=# create table t1 (id int primary key);
CREATE TABLE
postgres=# insert into t1 select generate_series(1,1000);
INSERT 0 1000
postgres=# create table t2 as select * from t1;
SELECT 1000
postgres=#
postgres=# explain (verbose, costs off) select * from t1, t2 where t1.id = t2.id;QUERY PLAN             
-----------------------------------Hash JoinOutput: t1.id, t2.idInner Unique: trueHash Cond: (t2.id = t1.id)->  Seq Scan on public.t2Output: t2.id->  HashOutput: t1.id->  Seq Scan on public.t1Output: t1.id
(10 rows)postgres=# show compute_query_id ;compute_query_id 
------------------auto
(1 row)postgres=# set compute_query_id = on;
SET
postgres=# explain (verbose, costs off) select * from t1, t2 where t1.id = t2.id;QUERY PLAN               
----------------------------------------Hash JoinOutput: t1.id, t2.idInner Unique: trueHash Cond: (t2.id = t1.id)->  Seq Scan on public.t2Output: t2.id->  HashOutput: t1.id->  Seq Scan on public.t1Output: t1.idQuery Identifier: -1771263242468121122
(11 rows)postgres=#

使用示例2,如下:

在这里插入图片描述


使用示例3,如下:

在这里插入图片描述

这里有一点需要注意(这点后面还会再次解释一下):

对于log_statement输出的行,%Q 总是报告零标识符, 因为log_statement在标识符能被计算之前生成输出,包括无效标识符不能计算的无效语句。

详细可以参见中文手册:

  • log_line_prefix 点击前往

功能实现源码解析

  • PostgreSQL Documentation: compute_query_id parameter
    在这里插入图片描述

启用查询标识符的内核计算:

  • 查询标识符可以在pg_stat_activity视图中显示,或使用EXPLAIN命令,或者在通过log_line_prefix参数进行配置的情况下在日志中发出
  • pg_stat_statements扩展也需要计算查询标识符
  • 请注意,如果内核查询标识符计算方法不可接受,也可以使用外部模块。在这种情况下,必须始终禁用内核计算
  • 有效值为 off(始终禁用)、on(始终启用)、auto(允许 pgstatstatements 等模块自动启用它)和 regress(与 auto 具有相同的效果,但查询标识符不会显示在 EXPLAIN 输出中,以便于自动回归测试)
  • 默认值为 auto

为确保只计算和显示一个查询标识符,计算查询标识符的扩展应该在已经计算查询标识符时抛出错误。


接下来我们直接看一下PostgreSQL 17.0的相关源码,如下:

// src/backend/utils/misc/guc_tables.c{{"compute_query_id", PGC_SUSET, STATS_MONITORING,gettext_noop("Enables in-core computation of query identifiers."),NULL},&compute_query_id,COMPUTE_QUERY_ID_AUTO, compute_query_id_options,NULL, NULL, NULL},

下面我们选择执行一个SQL,调试一下query id计算的过程。如下:

在这里插入图片描述

此时的函数堆栈,如下:

IsQueryIdEnabled()
parse_analyze_fixedparams(RawStmt * parseTree, const char * sourceText, const Oid * paramTypes, int numParams, QueryEnvironment * queryEnv)
pg_analyze_and_rewrite_fixedparams(RawStmt * parsetree, const char * query_string, const Oid * paramTypes, int numParams, QueryEnvironment * queryEnv)
exec_simple_query(const char * query_string) 
...

如上,在语义分析阶段,才去计算id值。而log_statement的打印如下:

// .../** Do basic parsing of the query or queries (this should be safe even if* we are in aborted transaction state!)*/parsetree_list = pg_parse_query(query_string); // 词法语法解析/* Log immediately if dictated by log_statement */ // hereif (check_log_statement(parsetree_list)){ereport(LOG,(errmsg("statement: %s", query_string),errhidestmt(true),errdetail_execute(parsetree_list)));was_logged = true;}
.../** Run through the raw parsetree(s) and process each one.*/foreach(parsetree_item, parsetree_list){...// 计算过程在语义分析阶段querytree_list = pg_analyze_and_rewrite_fixedparams(parsetree, query_string,NULL, 0, NULL);...}
...

计算逻辑,如下:

在这里插入图片描述


计算结果如下:

在这里插入图片描述

此时的函数堆栈,如下:

hash_bytes_extended(const unsigned char * k, int keylen, uint64 seed)
hash_any_extended(const unsigned char * k, int keylen, uint64 seed)
JumbleQuery(Query * query)
parse_analyze_fixedparams(RawStmt * parseTree, const char * sourceText, const Oid * paramTypes, int numParams, QueryEnvironment * queryEnv)
pg_analyze_and_rewrite_fixedparams(RawStmt * parsetree, const char * query_string, const Oid * paramTypes, int numParams, QueryEnvironment * queryEnv)
exec_simple_query(const char * query_string) 
...
// src/common/hashfn.c/** hash_bytes_extended() -- hash into a 64-bit value, using an optional seed*		k		: the key (the unaligned variable-length array of bytes) // 键(未对齐的可变长度字节数组)*		len		: the length of the key, counting by bytes // 密钥的长度,以字节为单位*		seed	: a 64-bit seed (0 means no seed)** Returns a uint64 value.  Otherwise similar to hash_bytes.*/
uint64
hash_bytes_extended(const unsigned char *k, int keylen, uint64 seed);

接下来,我们再看一下(仅更换了常量的)另一个SQL计算,如下:

在这里插入图片描述

原因比较简单,计算id的hash函数传参都是一样的:

	/* Compute query ID and mark the Query node with it */_jumbleNode(jstate, (Node *) query);query->queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,jstate->jumble_len,0));

JumbleState *jstate的来源是上面函数_jumbleNode进行处理的:

			case T_Query:_jumbleQuery(jstate, expr);break;
// src/backend/nodes/queryjumblefuncs.funcs.cstatic void
_jumbleQuery(JumbleState *jstate, Node *node)
{Query *expr = (Query *) node;JUMBLE_FIELD(commandType);JUMBLE_NODE(utilityStmt);JUMBLE_NODE(cteList);JUMBLE_NODE(rtable);JUMBLE_NODE(jointree);JUMBLE_NODE(mergeActionList);JUMBLE_NODE(mergeJoinCondition);JUMBLE_NODE(targetList);JUMBLE_NODE(onConflict);JUMBLE_NODE(returningList);JUMBLE_NODE(groupClause);JUMBLE_FIELD(groupDistinct);JUMBLE_NODE(groupingSets);JUMBLE_NODE(havingQual);JUMBLE_NODE(windowClause);JUMBLE_NODE(distinctClause);JUMBLE_NODE(sortClause);JUMBLE_NODE(limitOffset);JUMBLE_NODE(limitCount);JUMBLE_FIELD(limitOption);JUMBLE_NODE(rowMarks);JUMBLE_NODE(setOperations);
}
-- 下面前两个SQL的query id一致postgres=# select * from pg_sleep(20);pg_sleep 
----------(1 row)postgres=# select * from pg_sleep(2);pg_sleep 
----------(1 row)postgres=# select pg_sleep(11);pg_sleep 
----------(1 row)postgres=#

他们的query结构,按照上面顺序依次如下:

query_20,如下:
在这里插入图片描述


query_2,如下:

在这里插入图片描述


query2_11,如下:

在这里插入图片描述


在这里插入图片描述

如上,前两个SQL仅常量值的不一样,可是他们的Query id一样。就是因为在计算过程中,Const仅计算了consttypelocation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/64185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多线程编程杂谈(上)

问题 线程执行的过程中可以强制退出吗? 主动退出?被动退出? 问题抽象示例 需要解决的问题 g_run 全局变量需要保护吗? 如何编码使得线程中每行代码的执行可被 g_run 控制? 线程代码在被 g_run 控制并 "强制退…

【Git】:企业级开发和多人协作开发啊

目录 多人协作 模拟配置多人协作环境 多人同一分支开发 多人不同分支开发 远程分支删除后的问题 企业级开发模型 系统开发环境 分支设计规范 多人协作 模拟配置多人协作环境 目前,我们所完成的工作如下: 基本完成 Git 的所有本地库的相关操作&#xff…

【Sql优化】数据库优化方法、Explain使用

文章目录 一、金字塔优化模型二、SQL优化的利器:Explain工具1. Explain 的作用2. Explain 的用法 三、SQL优化方法(后续文章细讲)1. 创建索引减少扫描量2. 调整索引减少计算量3. 索引覆盖4. 干预执行计划5. SQL改写 四、通过 Explain 优化案例…

Deepmotion技术浅析(五):运动追踪

运动追踪是 DeepMotion 动作捕捉和 3D 重建流程中的核心模块之一。该模块的主要任务是在视频序列中跟踪人体的运动轨迹,捕捉人体各部分随时间的变化,并生成连续的 3D 运动数据。DeepMotion 的运动追踪技术结合了计算机视觉、深度学习和物理模拟等方法&am…

Android 系统应用重名install安装失败分析解决

Android 系统应用重名install安装失败分析解决 文章目录 Android 系统应用重名install安装失败分析解决一、前言1、Android Persistent apps 简单介绍 二、系统 persistent 应用直接安装需求分析解决1、系统应用安装报错返回的信息2、分析解决 三、其他1、persistent系统应用in…

使用Nexus3搭建npm私有仓库

一、npm介绍 npm的全称是Node Package Manager,它是一个开放源代码的命令行工具,用于安装、更新和管理Node.js模块。npm是Node.js的官方模块管理器,它允许用户从一个集中的仓库中下载和安装公共的Node.js模块,并将这些模块集成到…

【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?

在人工智能领域,我们一直在追求让机器像人类一样思考。然而,即使是最先进的AI,也常常被诟病缺乏“常识”,难以理解复杂问题,更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方,…

蓝桥杯刷题——day5

蓝桥杯刷题——day5 题目一题干解题思路一代码解题思路二代码 题目二题干解题思路代码 题目一 题干 给定n个整数 a1,a2,⋯ ,an,求它们两两相乘再相加的和,即: 示例一: 输入: 4 1 3 6 9 输出: 117 题目链…

监测预警智能分析中心建设项目方案

随着科技的不断进步,地理信息与遥感技术在国家治理、环境保护、灾害预警等领域发挥着越来越重要的作用。监测预警智能分析中心的建设,旨在通过集成先进的遥感技术、地理信息系统(GIS)、大数据分析和人工智能(AI&#x…

【漫话机器学习系列】009.词袋模型(Bag of Words)

词袋模型(Bag of Words, 简称 BoW) 词袋模型是一种常见的文本表示方法,主要用于自然语言处理(NLP)和信息检索领域。它将文本数据转换为特征向量,忽略语序,仅考虑词的出现与否或出现频率。 1. 基…

分治算法(单选题)

2-1 分数 2 下列多少种排序算法用了分治法? 堆排序插入排序归并排序快速排序选择排序希尔排序 A.2 B.3 C.4 D.5 正确答案 A 2-2 分数 2 分治法的设计思想是将一个难以直接解决的大问题分割成规模较小的子问题,分别解决问题,最后将子…

UNIX简史

从1991年Linux出现至今,由于众多IT巨头以及技术社区的推动,Linux已经成为非常成熟、可用于各种关键领域的操作系统,适当了解其发展历史,对于理顺其技术流派、从而更好地学习和使用Linux具有重要意义。由于其基于UNIX系统二十多年的…

C# OpenCV机器视觉:畸变矫正

在一个阳光明媚的早晨,阿强决定去拍照。他拿起相机,穿上他最喜欢的羊毛大衣,准备记录下生活中的美好瞬间。可是,当他兴奋地查看照片时,发现自己拍的每一张都像是被外星人用变形金刚的力量扭曲过一样!“这是…

tryhackme——Defensive Security Intro(防御安全简介)

任务一:Introduction to Defensive Security防御安全简介 此room的两个要点: Preventing intrusions from occurring 防止入侵发生Detecting intrusions when they occur and responding properly 检测发生的入侵并正确响应 防御安全还有更多内容。 除上…

使用rust语言创建python模块(pyo3+maturin)

1. 首先使用conda创建python虚拟环境(已创建的可省略) >conda create --prefixE:\python_envs\rust_python python3.11 2. 激活python虚拟环境 conda activate rust_python 3. 安装maturin pip install maturin 4. 创建rust项目 >cd E:\py…

关于Postgresql旧版本安装

抛出问题 局点项目现场,要求对如下三类资产做安全加固,需要在公司侧搭建测试验证环境,故有此篇。 bclinux 8.2 tomcat-8.5.59 postgrel -11 随着PG迭代,老旧版本仅提供有限维护。如果想安装老版本可能就要费劲儿一些。现在&…

使用echarts实现3d柱状图+折线图

以下代码有问题请直接问国内直连GPT/Claude HTML 需要注意threeDchart一定要设置宽度高度&#xff0c;不然图不显示,然后echarts版本不要太低&#xff0c;不然也不显示 <div id"threeDchart" class"threeDchart"></div>js set3DBarChart2(dat…

2024.1212-02-虚拟私人网(VPN) 虚拟局域网 及隧道技术(四)--GRE47 Etherip97 原理及应用

虚拟局域网 及隧道技术&#xff08;四&#xff09;-GRE47 & Etherip97原理及应用 概述原理及应用EOIP/Etherip概念区别 隧道协议标准EtherIP &#xff08;IP protocol number 97&#xff09;GRE 开源工具katlogic-eoip 验证环境GRE&#xff08;EOIP&#xff09;演示验证Eth…

【从零开始入门unity游戏开发之——C#篇01】理论开篇,理解什么是编程

文章目录 前言前置条件进制什么是十进制、二进制二进制有什么用&#xff1f;为什么计算机用二进制而不用十进制&#xff1f;二进制转十进制十进制转二进制二进制运算 计算机中的数据存储单位什么是编程&#xff1f;什么是代码&#xff1f;什么是编程语言&#xff1f;常见的编程…

黑盒白盒测试

任务1 黑盒测试之等价类划分法 【任务需求】 【问题】例&#xff1a;某报表处理系统要求用户输入处理报表的日期&#xff0c;日期限制在2003年1月至2008年12月&#xff0c;即系统只能对该段期间内的报表进行处理&#xff0c;如日期不在此范围内&#xff0c;则显示输入错误信息…