使用pg_stat_statement监控pgsql遇到的问题

本文介绍了pg_stat_statements插件的使用方法及原理,并详细解释了如何安装此插件、配置参数以及它如何通过哈希表存储SQL统计信息。同时探讨了在实际应用中遇到的一些常见问题及其解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       pg_stat_statements是PG中监控数据库活动的重要插件,通过它可以获得SQL的统计信息,例如该SQL被调用了多少次,返回了多少记录,在读写数据上花了多少时间,这个对于监控数据库很有帮助。默认情况下,这个插件可以统计5000个SQL,如果不够可以调整pg_stat_statements.max这个GUC。

       一般情况下,可以通过源码安装该插件:

       1, 先编译安装pgsql

       2,在pg源码的目录下执行 make install -C contrib/pg_stat_statements

       3,修改pg的配置文件postgres.conf,在其中加上一行

shared_preload_libraries = 'pg_stat_statements'

        4,启动数据库,执行下面的SQL添加该插件:

CREATE EXTENSION pg_stat_statements

     该语句会在当前的数据库中创建一个视图pg_stat_statements,这个视图中包含了很多有用的监控信息.简单的说来,这个插件会在PG数据库初始化的时候从共享内存中申请一块区域,这块区域主要是用作一个hash表,这个hash表将用来存储SQL的统计信息,默认是5000个不同的SQL。

     前面说到这个插件默认统计5000个SQL,那么如果新执行了一个SQL,该插件则会以SQL的查询计划为输入来计算hash码,(这个hash码就是pg_stat_statements视图中的queryid),然后去插件的哈希表中查找,如果发现该hash码和某个已经存在的SQL的queryid相同,则将统计结果累加到这个SQL的统计结果中;没有发现,则会添加到插件的hash表中,或是满了5000条了就通过类似LRU的算法替换掉某个SQL(这点细节需要看代码)。

      因此,这个插件在比对SQL时,智能程度还是很高的,只有语义上相同才会当作相同的SQL。例如下面三个SQL:

select * from t1 where a =1;
select * from t1 where a =2;
select * from t1 where a =3;

 会被这个插件当作一个SQL(严格上说是一类)。

       但是,有时候太智能也不太好:今天早上一个开发的同事来找我,说从pg_stat_statements中获得了很多重复的结果。登上他的测试环境,结果的确让人很奇怪:



 

      很明显,结果中出现了三种重复的结果:一种是执行pg_xlog_location_diff函数的SQL;一种是执行drop操作;最后一种就是执行insert 操作。

     第一眼看上去很吃惊,但是查看了文档http://www.postgresql.org/docs/9.4/static/pgstatstatements.html,再结合下SQL的处理流程,可以回答为什么前会有前两种重复的结果:

    1,对于执行select pg_xlog_location_diff() 这样的SQL,主要是因为执行SQL的用户不一样,所以插件认为是不同的SQL。并且,如果同一个用户连接不同的数据库去执行同一个SQL,插件也会认为是不同的SQL。从查询计划的角度来看,用户不同或是连接的数据库不同,即使其他内容相同,在数据库看来,也是不同的查询计划了。

    2,对于drop 操作的SQL重复出现,其实可以从文档里面就知道了,因为文档里面说的很明确了“Plannable queries (that is, SELECTINSERTUPDATE, and DELETE) are combined into a single pg_stat_statements entry whenever they have identical query structures according to an internal hash calculation.” 换言之,drop操作是没有查询计划的,因此没法判断一个drop操作之间是否相同,所以干脆当作都不同的

      最后,对于insert 操作的SQL重复出现,刚开始怎么也无法解释,后来问了问开发的同学怎么操作数据库的,就恍然大悟了:因为他每次连数据库都会建立一个心跳表,这是个临时表,然后执行那一堆insert 操作来判断数据库是否活着。

       一说临时表,就明白了七八分:因为他的临时表是session级别的,一个session连上来建立的临时表在断开session时,pg会自动删除临时表。虽然每次建立的临时表都是同一个表,一模一样,但是从数据库的角度来说,就是一个不同的表的了。所以每次session 第一次insert 这个表的SQL都会和前一次session执行的insert SQL是不同的,虽然他们字面上是一模一样的。当然,他们生成的查询计划的语义也是完全不一样的了。 而且考虑到pg_stat_statements的容量有限,最好还是建立一个非临时表。

       

<think>我们正在处理一个关于PL/pgSQL函数错误的调试问题。用户提供的错误信息是:PL/pgSQL function opr_stat.proc_table_spark_build_stat_d(date) error line 43 SQL statement。 根据错误信息,我们可以推断: 1. 函数名为:opr_stat.proc_table_spark_build_stat_d 2. 参数类型为:date 3. 错误发生在函数内的第43行 由于我们没有函数的具体代码,因此需要从常见错误角度进行分析。在PL/pgSQL中,第43行可能是一个SQL语句,该语句执行时发生了错误。 常见的可能原因包括: - 语法错误:SQL语句本身有语法问题。 - 数据类型不匹配:例如,函数参数传递到SQL语句中时,与SQL语句中使用的上下文不匹配。 - 无效的列名或表名:SQL语句中引用了不存在的列或表。 - 违反约束:如唯一性约束、非空约束等。 - 权限问题:函数的所有者可能没有执行该SQL语句的权限。 为了调试,我们可以采取以下步骤: ### 步骤1:查看函数定义 使用以下命令查看函数定义,特别是第43行附近的代码: ```sql SELECT pg_get_functiondef('opr_stat.proc_table_spark_build_stat_d(date)'::regprocedure); ``` 或者 ```sql \df+ opr_stat.proc_table_spark_build_stat_d ``` ### 步骤2:检查具体的SQL语句 找到第43行的SQL语句,并单独执行它,替换参数为实际值,以验证SQL语句的正确性。 ### 步骤3:检查参数传递 确保在函数内部使用参数时,数据类型和格式正确。例如,日期参数在SQL语句中可能需要被正确引用或格式化。 ### 步骤4:检查依赖对象 确保SQL语句中引用的表、视图等对象存在,并且函数所有者有访问权限。 ### 步骤5:使用日志 检查PostgreSQL的日志文件,通常日志中会有更详细的错误信息。 ### 示例:常见的日期参数使用问题 在PL/pgSQL中,日期参数在SQL语句中使用时,需要注意格式。例如,在动态SQL中,需要正确地将日期转换为字符串,或者使用参数化查询以避免SQL注入和格式问题。 假设第43行是一个动态SQL语句,可能如下: ```sql EXECUTE format('INSERT INTO some_table SELECT * FROM another_table WHERE date_col = %L', p_date); ``` 这里,%L会将日期参数转换为字符串并加上单引号。但要注意,如果日期格式与数据库设置的格式不匹配,可能会导致错误。 另一种方式是使用参数化查询: ```sql EXECUTE 'INSERT INTO some_table SELECT * FROM another_table WHERE date_col = $1' USING p_date; ``` 这样更安全,并且避免了格式问题。 ### 可能的问题 如果错误信息明确指出是第43行的SQL语句,那么很可能是该SQL语句执行时出错。例如,可能是INSERT、UPDATE或SELECT语句有误。 ### 建议 1. 获取函数定义,定位到第43行。 2. 单独运行该SQL语句,使用一个具体的日期值,观察是否报错。 3. 检查表结构是否发生变化,特别是如果该函数已经运行过一段时间,表结构可能被修改。 由于我们无法直接访问数据库,以上步骤需要用户自行操作。 §§相关问题§§ 1. 如何获取PostgreSQL中存储过程的源代码? 2. 在PL/pgSQL中如何调试动态SQL语句? 3. 如何查看PostgreSQL的错误日志以获取更详细的错误信息? 4. 在PL/pgSQL中,日期参数传递时需要注意哪些问题?</think>根据您提供的错误信息 `PL/pgSQL function opr_stat.proc_table_spark_build_stat_d(date) error line 43 SQL statement`,这是一个 PostgreSQL 存储过程执行错误。让我们逐步分析并解决这个问题: ### 问题诊断 1. **错误位置**: - 函数:`opr_stat.proc_table_spark_build_stat_d` - 参数类型:`date` - 错误行号:**第43行** - 错误类型:SQL语句执行失败 2. **常见原因**: - 参数类型不匹配(如日期格式错误) - SQL语法错误或无效对象引用 - 权限不足(函数所有者无操作目标表的权限) - 空值处理不当(如`NULL`传递给`NOT NULL`列) - 动态SQL拼接错误(特别是使用`EXECUTE`时) ### 调试步骤 #### 1. 检查函数定义 ```sql -- 查看函数源码 SELECT pg_get_functiondef('opr_stat.proc_table_spark_build_stat_d(date)'::regprocedure); -- 或使用 psql \df+ opr_stat.proc_table_spark_build_stat_d ``` **重点检查第43行附近的SQL语句**,常见问题包括: - 表/列名拼写错误 - 未转义的关键字(如`timestamp`作为列名需加引号) - 日期隐式转换失败 #### 2. 验证参数传递 ```sql -- 手动测试参数传递 DO $$ DECLARE test_date date := '2023-01-01'; -- 替换为实际参数 BEGIN -- 复制第43行SQL到这里测试 RAISE NOTICE 'SQL: %', format('SELECT * FROM target_table WHERE date_col = %L', test_date); END $$; ``` #### 3. 检查权限 ```sql -- 确认函数所有者有操作权限 SELECT proc.proname AS function_name, proc.proowner::regrole AS owner, has_table_privilege(proc.proowner, 'target_table', 'INSERT') AS insert_perm FROM pg_proc proc WHERE proname = 'proc_table_spark_build_stat_d'; ``` #### 4. 动态SQL调试(如果使用EXECUTE) 在第43行前添加诊断输出: ```sql RAISE NOTICE 'Executing SQL: %', dynamic_sql; -- 打印实际执行的SQL EXECUTE dynamic_sql; -- 原第43行 ``` ### 常见修复方案 #### 案例1:日期格式问题 ```diff -- 错误代码 EXECUTE format('INSERT INTO stats SELECT * FROM logs WHERE log_date = %s', p_date); -- 修复:显式转换日期类型 EXECUTE format('INSERT INTO stats SELECT * FROM logs WHERE log_date = %L::date', p_date); ``` #### 案例2:保留关键字冲突 ```diff -- 错误(timestamp是关键字) INSERT INTO events (timestamp, event) VALUES (p_date, 'test'); -- 修复:转义列名 INSERT INTO events ("timestamp", event) VALUES (p_date, 'test'); ``` #### 案例3:动态表名权限 ```sql -- 使用 SECURITY DEFINER 解决权限问题 CREATE FUNCTION opr_stat.proc_table_spark_build_stat_d(p_date date) RETURNS void SECURITY DEFINER -- 以函数所有者身份执行 AS $$ BEGIN -- 第43行代码 END; $$ LANGUAGE plpgsql; ``` ### 完整调试建议 1. 启用详细错误日志: ```sql ALTER DATABASE yourdb SET log_min_messages = NOTICE; ALTER DATABASE yourdb SET log_statement = 'all'; ``` 2. 捕获具体错误: ```sql BEGIN -- 原函数代码 EXCEPTION WHEN others THEN RAISE NOTICE '错误详情: %', SQLERRM; END; ``` 3. 检查依赖对象状态: ```sql -- 确认目标表存在且结构匹配 SELECT * FROM information_schema.tables WHERE table_schema = 'opr_stat' AND table_name = 'target_table'; ``` > **提示**:根据引用[2]的日志位置提示,检查 PostgreSQL 的 `log_directory`(默认 `/var/lib/pgsql/版本号/data/pg_log/`)获取详细错误堆栈[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值