PostgreSQL计算 queryid 原理

news2025/1/18 8:59:13

数据库版本

  • PG 16.1

queryid 是什么

queryid 是将 sql 规范化 (normalization) 后,通过哈希函数计算出来的 64 位整数。

SELECT id, data FROM tbl_a WHERE id < 300 ORDER BY data; 这条 SQL 为例。当我们在 PG 中执行这条 sql 时,内核在语义分析阶段会将其规范化后计算哈希值。这个 sql 中,如果将 id<300 改成 id<400 ,它们对应的规范化 sql 仍然是同样的,queryid 也是同样的。

如果启用了 pg_stat_statements 插件,会看到其规范化后的 sql 语句 SELECT id, data FROM tbl_a WHERE id < $1 ORDER BY data; 和计算出来的 queryid。
在这里插入图片描述

如何计算 queryid

众所周知,PG 在处理 SQL 时,会经过 词法分析->语法分析->语义分析->生成计划->执行计划 等阶段,而 queryid 的计算就是在语义分析阶段的 JumbleQuery 函数中完成的。

还以上文的 SQL SELECT id, data FROM tbl_a WHERE id < 300 ORDER BY data; 为例,语义分析阶段后,内核已经得到了 SQL 对应的 query 结构,如下图所示。(借用 interdb.jp [1] 文章中的图)

在这里插入图片描述
内核对所有需要 jumble 的结构都设定了对应 jumble 的方法,维护在 queryjumblefuncs.switch.c 文件中,下面简单列几行

case T_Alias:
	_jumbleAlias(jstate, expr);
	break;
case T_RangeVar:
	_jumbleRangeVar(jstate, expr);
	break;
case T_TableFunc:
	_jumbleTableFunc(jstate, expr);
	break;

static void
_jumbleAlias(JumbleState *jstate, Node *node)
{
	Alias *expr = (Alias *) node;

	JUMBLE_STRING(aliasname);
	JUMBLE_NODE(colnames);
}

而对应到每个最终的结构上时,使用 AppendJumble 函数进行处理。将所有的输入都转成 unsigned char* 存到 jstate->jumble 变量里。如果发现长度满了就把之前的哈希一下从头开始存

static void
AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
{
	unsigned char *jumble = jstate->jumble;
	Size		jumble_len = jstate->jumble_len;

	/*
	 * Whenever the jumble buffer is full, we hash the current contents and
	 * reset the buffer to contain just that hash value, thus relying on the
	 * hash to summarize everything so far.
	 */
	while (size > 0)
	{
		Size		part_size;

		if (jumble_len >= JUMBLE_SIZE)
		{
			uint64		start_hash;

			start_hash = DatumGetUInt64(hash_any_extended(jumble,
														  JUMBLE_SIZE, 0));
			memcpy(jumble, &start_hash, sizeof(start_hash));
			jumble_len = sizeof(start_hash);
		}
		part_size = Min(size, JUMBLE_SIZE - jumble_len);
		memcpy(jumble + jumble_len, item, part_size);
		jumble_len += part_size;
		item += part_size;
		size -= part_size;
	}
	jstate->jumble_len = jumble_len;
}

上述过程完成后,就得到了一个完整的 jumble 字符串。然后用内核的 hash_any_extend 对这个字符串进行哈希,得到的 64 位无符号整数就是 queryid(输出时会将其转为 int64)。

pg_stat_statement 生成规范化 sql

PG 内核所做的只是将一个 SQL 解析成 Query 结构,然后将所需要的部分拼起来哈希一下,得到一个 queryid,并没有规范化 sql 的概念。而 pg_stat_statement 的 generate_normalized_query 函数可以帮我们做到这一点,从
SELECT id, data FROM tbl_a WHERE id < 300 ORDER BY data;
生成
SELECT id, data FROM tbl_a WHERE id < $1 ORDER BY data; 规范化的 sql。

下面是函数的源码,代码很长,其实就是做了一件事:将 sql 中的常量替换成 $1、$2…

static char *
generate_normalized_query(JumbleState *jstate, const char *query,
						  int query_loc, int *query_len_p)
{
	char	   *norm_query;
	int			query_len = *query_len_p;
	int			i,
				norm_query_buflen,	/* Space allowed for norm_query */
				len_to_wrt,		/* Length (in bytes) to write */
				quer_loc = 0,	/* Source query byte location */
				n_quer_loc = 0, /* Normalized query byte location */
				last_off = 0,	/* Offset from start for previous tok */
				last_tok_len = 0;	/* Length (in bytes) of that tok */

	/*
	 * Get constants' lengths (core system only gives us locations).  Note
	 * this also ensures the items are sorted by location.
	 */
	fill_in_constant_lengths(jstate, query, query_loc);

	/*
	 * Allow for $n symbols to be longer than the constants they replace.
	 * Constants must take at least one byte in text form, while a $n symbol
	 * certainly isn't more than 11 bytes, even if n reaches INT_MAX.  We
	 * could refine that limit based on the max value of n for the current
	 * query, but it hardly seems worth any extra effort to do so.
	 */
	norm_query_buflen = query_len + jstate->clocations_count * 10;

	/* Allocate result buffer */
	norm_query = palloc(norm_query_buflen + 1);

	for (i = 0; i < jstate->clocations_count; i++)
	{
		int			off,		/* Offset from start for cur tok */
					tok_len;	/* Length (in bytes) of that tok */

		off = jstate->clocations[i].location;
		/* Adjust recorded location if we're dealing with partial string */
		off -= query_loc;

		tok_len = jstate->clocations[i].length;

		if (tok_len < 0)
			continue;			/* ignore any duplicates */

		/* Copy next chunk (what precedes the next constant) */
		len_to_wrt = off - last_off;
		len_to_wrt -= last_tok_len;

		Assert(len_to_wrt >= 0);
		memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
		n_quer_loc += len_to_wrt;

		/* And insert a param symbol in place of the constant token */
		n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
							  i + 1 + jstate->highest_extern_param_id);

		quer_loc = off + tok_len;
		last_off = off;
		last_tok_len = tok_len;
	}

	/*
	 * We've copied up until the last ignorable constant.  Copy over the
	 * remaining bytes of the original query string.
	 */
	len_to_wrt = query_len - quer_loc;

	Assert(len_to_wrt >= 0);
	memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
	n_quer_loc += len_to_wrt;

	Assert(n_quer_loc <= norm_query_buflen);
	norm_query[n_quer_loc] = '\0';

	*query_len_p = n_quer_loc;
	return norm_query;
}

参考资料

[1] https://www.interdb.jp/pg/pgsql03/01.html

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

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

相关文章

技术性屏蔽百度爬虫已经一周了!

很久前明月就发现百度爬虫只抓取、只收录就是不给流量了&#xff0c;加上百度搜索体验越来越差&#xff0c;反正明月已经很久没有用过百度搜索&#xff0c;目前使用的浏览器几乎默认搜索都已经修改成其他搜索引擎了&#xff0c;真要搜索什么&#xff0c;一般都是必应谷歌结合着…

【设计模式深度剖析】【11】【行为型】【解释器模式】| 以算术表达式求值为例加深理解

&#x1f448;️上一篇:状态模式 设计模式-专栏&#x1f448;️ 文章目录 解释器模式定义英文原话直译 解释器模式中的角色1. 抽象表达式&#xff08;AbstractExpression&#xff09;2. 终端表达式&#xff08;TerminalExpression&#xff09;3. 非终端表达式&#xff08;Non…

Linux 特殊变量 $?

一. 说明 在 Linux 和其他类 Unix 系统中&#xff0c;$? 是一个特殊的变量&#xff0c;用于获取上一个命令的退出状态码。 退出状态码是一个整数值&#xff0c;通常用来表示命令的执行结果。 ⏹退出状态码的含义 0&#xff1a;命令成功执行。0以外的数字&#xff1a;命令执…

[SAP ABAP] 插入内表数据

语法格式 INSERT <wa> INTO <itab> INDEX <idx>. <wa>&#xff1a;代表工作区 <itab>&#xff1a;代表内表 <idx>&#xff1a;代表索引值 示例1 结果显示&#xff1a; 语法格式 INSERT <wa> INTO TABLE <itab>. <wa>&…

文件创建与查看

touch touch命令用于创建一个新的文件。 语法&#xff1a;touch Linux路径 其中路径可以是相对路径、绝对路径或者特殊路径符都可以。 改图展示了通过 touch test.txt 命令创建了一个 test.txt文件&#xff0c;其中深色的代表文件夹&#xff0c;白色的代表文件。 使用 ls -lh…

昇思25天学习打卡营第5天|模型训练|保存与加载

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) 模型训练 模型训练一般分为四个步骤&#xff1a; 构建数据集。定义神经网络模型。定义超参、损失函数及优化器。输入数据集进行训练与评估。 现在我们有了数据集和模型后&#xff0c;可以进行…

QTreeView双击任意列展开

一.效果 二.原理 重点是如何通过其他列的QModelIndex(假设为index),获取第一列的QModelIndex(假设为firstColumnIndex)。代码如下所示: QModelIndex firstColumnIndex = model->index(index.row(), 0, index.parent()); 这里要注意index函数的第三个参数,第三个参…

切换国内yum源

切换国内yum源 一、备份现有YUM源二、下载新的YUM源配置文件三、先清理YUM缓存再生成四、测试新的YUM源五、重启系统服务(生效可以不重启)可选 首先&#xff0c;切换国内YUM源的具体步骤取决于您使用的Linux发行版和当前的YUM源配置。以下是一般步骤&#xff0c;适用于大多数基…

学会python——对目录的操作(python实例十)

目录 1、认识Python 2、环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3、遍历当前目录 3.1 代码构思 3.2 代码示例 3.3 运行结果 4、删除目录中的文件 4.1 代码构思 4.2 代码示例 4.3 运行结果 5、总计 1、认识Python Python 是一个高层次的结合了解释性…

CTF-蓝帽杯 2022 初赛Misc计算机取证题目详解

使用工具&#xff1a;Volatility、Passware Kit、Arsenal Image Mounter、DiskGenius 题目文件如下&#xff1a; 首先要知道这些文件是什么&#xff1a; dmp后缀指Dump文件&#xff0c;是windows系统中的错误转储文件。包含计算机程序运行时的内存信息的文件。通常操作系统或…

基于SpringBoot+Vue二手交易平台设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还…

经典面试题【作用域、闭包、变量提升】,带你深入理解掌握!

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是前端菜鸟的自我修养&#xff01;今天给大家分享经典面试题【作用域、闭包、变量提升】&#xff0c;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;原创不易&#xff0c;如果能帮助到带大家&#xff0…

Codeforces Round 953 (Div. 2) A~F

A.Alice and Books&#xff08;思维&#xff09; 题意&#xff1a; 爱丽丝有 n n n本书。第 1 1 1本书包含 a 1 a_1 a1​页&#xff0c;第 2 2 2本书包含 a 2 a_2 a2​页&#xff0c; … \ldots …第 n n n本书包含 a n a_n an​页。爱丽丝的操作如下&#xff1a; 她把所有的…

CTO的职责是什么?

看《架构思维》作者是这样讲的&#xff1a; CTO 到底是做什么的&#xff1f; 我当下的答案是&#xff1a;“CTO 就是一个从技术视角出发&#xff0c;为公司或者所在的部门做正确决策的 CEO。”怎么理解这句话呢&#xff1f;作为一个 CTO&#xff0c;其长期目标和决策优先级与…

【Linux】系统文件IO·文件描述符fd

前言 C语言文件接口 C 语言读写文件 1.C语言写入文件 2.C语言读取文件 stdin/stdout/stderr 系统文件IO 文件描述符fd&#xff1a; 文件描述符分配规则&#xff1a; 文件描述符fd&#xff1a; 前言 我们早在C语言中学习关于如何用代码来管理文件&#xff0c;比如文件的…

搜狐视频全自动工具

项目介绍&#xff1a; 首先我们要用自己的一个号&#xff0c;作为主号&#xff0c;主号上发作品&#xff0c;利用不实名的小号通过脚本自动去浏览视频 小号看视频的同时会出现搜狐官方强行插入的广告&#xff0c;从而获得收益 收益介绍&#xff1a; 小号看一个视频会出现3-…

使用SpringCache实现Redis缓存

目录 一 什么是Spring Cache 二 Spring Cache 各注解作用 ①EnableCaching ②Cacheable ③CachePut ④CacheEvict 三实现步骤 ①导入spring cache依赖和Redis依赖 ②配置Redis连接信息 ③在启动类上加上开启spring cache的注解 ④ 在对应的方法上加上需要的注解 一 什么…

虚拟机IP地址频繁变化的解决方法

勾八动态分配IP&#xff0c;让我在学习redis集群的时候&#xff0c;配置很多的IP地址&#xff0c;但是由于以下原因导致我IP频繁变动&#xff0c;报错让我烦恼&#xff01;&#xff01;&#xff01;&#xff01; 为什么虚拟机的IP地址会频繁变化&#xff1f; 虚拟机IP地址频繁…

【Python机器学习】利用t-SNE进行流形学习

虽然PCA通常是用于变换数据的首选方法&#xff0c;使你能够用散点图将其可视化&#xff0c;但这一方法的性质限制了其有效性。 有一类用于可视化的算法叫做流形学习算法&#xff0c;它允许进行更复杂的映射&#xff0c;通常也可以给出更好的可视化。其中特别有用的一个就是t-S…

MySQL学习笔记-进阶篇-锁

概述 概念 全局锁 表级锁 介绍 表锁 读锁 只允许加锁客户端读操作禁止写操作。允许其他客户端的读操作&#xff0c;阻塞其他客户端的写操作。 lock tables xxx read unlock tables&#xff1b; 写锁 允许加锁客户端的读写操作。既阻塞其他客户端的读&#xff0c;又阻塞其他客…