PostgreSQL 查询的性能监控。 第 1 部分 - 报告

工程师 - 翻译自拉丁语 - 受到启发。
工程师可以做任何事情。 (c) R.迪塞尔。
题词。
PostgreSQL 查询的性能监控。 第 1 部分 - 报告
或者一个关于为什么数据库管理员需要记住他过去的编程经历的故事。

前言

所有名称均已更改。 比赛是随机的。 该材料仅代表作者的个人意见。

免责声明: 在计划的系列文章中,将不会对所使用的表格和脚本进行详细和准确的描述。 材料不能立即“按原样”使用。
首先,由于材料量大,
其次,因为对真实客户生产基地的敏锐度。
因此,文章中只会给出最一般形式的想法和描述。
也许将来这个系统会发展到在 GitHub 上发布的水平,也可能不会。 时间会证明。

故事的开始——你还记得这一切是如何开始的吗“。
结果发生了什么,用最笼统的话来说——”综合作为提高 PostgreSQL 性能的方法之一»

为什么我需要这一切?

嗯,首先,不要忘记自己,记住退休后的辉煌岁月。
其次,将所写内容系统化。 对于我自己来说,有时我开始感到困惑并忘记单独的部分。

好吧,最重要的是 - 突然间,它可以对某人派上用场,帮助您避免重新发明轮子,也不会收集耙子。 换句话说,提高你的业力(而不是哈布罗夫斯基)。 因为这个世界上最有价值的东西就是想法。 最重要的是找到一个想法。 而将想法变为现实已经是一个纯粹的技术问题了。

那么让我们慢慢开始吧...

问题的表述。

有:

PostgreSQL(10.5),混合负载(OLTP+DSS),中到轻负载,托管在AWS云中。
没有数据库监控,基础设施监控以最低配置作为标准 AWS 工具呈现。

要求:

监视数据库的性能和状态,查找并获取初始信息以优化繁重的数据库查询。

解决方案简介或分析

首先,我们尝试从对工程师的好处和麻烦进行比较分析的角度来分析解决问题的选项,并让那些应该在人员名单上的人来处理好处和损失的管理。

选项 1 - “按需工作”

我们让一切保持原样。 如果客户对数据库或应用程序的运行状况、性能不满意,他将通过电子邮件或在票箱中创建事件来通知 DBA 工程师。
工程师收到通知后,会了解问题,提出解决方案,或者将问题搁置起来,希望一切都能自行解决,反正一切都会很快被遗忘。
姜饼和甜甜圈、瘀伤和肿块姜饼和甜甜圈:
1. 没有什么额外的事情可做
2. 总有机会出去弄脏。
3. 有很多时间可以自己度过。
瘀伤和肿块:
1. 迟早,顾客会思考这个世界存在的本质和普遍正义,并再次问自己这个问题 - 为什么我要付钱给他们? 结果总是一样的——唯一的问题是顾客什么时候感到无聊并挥手告别。 而且喂食器是空的。 这是可悲的。
2.工程师的发展为零。
3、排工、装车困难

选项2——“敲着手鼓跳舞,穿上鞋子”

第 1 款-为什么我们需要一个监控系统,我们会收到所有请求。 我们对数据字典和动态视图发起一系列各种查询,打开各种计数器,将所有内容放入表中,定期分析列表和表格。 结果,我们得到了漂亮或不太漂亮的图表、表格、报告。 最重要的是——那会更多,更多。
第 2 款-生成活动-运行所有这一切的分析。
第 3 款-我们正在准备一份文件,我们简单地称之为“我们如何装备数据库”。
第 4 款- 客户看到所有这些精彩的图表和数字,充满了幼稚的天真自信 - 现在一切都会为我们服务,很快。 而且,可以轻松无痛地放弃他们的财务资源。 管理层也确信我们的工程师正在努力工作。 最大负载。
第 5 款- 定期重复步骤 1。
姜饼和甜甜圈、瘀伤和肿块姜饼和甜甜圈:
1. 经理和工程师的生活简单、可预测且充满活动。 一切都很热闹,每个人都很忙碌。
2. 客户的生活也不错——他总是确信你需要耐心一点,一切都会好起来的。 情况并没有好转,好吧,好吧——这个世界不公平,下辈子——你会幸运的。
瘀伤和肿块:
1. 迟早会有一个更聪明的类似服务提供商会做同样的事情,但价格会便宜一些。 如果结果是一样的,为什么要付出更多呢? 这又会导致喂食器的消失。
2. 很无聊。 任何有意义的小活动都是多么无聊啊。
3. 与之前的版本一样 - 没有开发。 但对于工程师来说,缺点是,与第一个选项不同,这里您需要不断生成 IDB。 这需要时间。 这笔钱可以用来造福您所爱的人。 因为你都照顾不了自己,所以大家都关心你。

方案3——不需要发明自行车,你需要购买并骑它。

其他公司的工程师故意吃披萨配啤酒(哦,90年代圣彼得堡的辉煌时代)。 让我们使用已经制作、调试和运行的监控系统,一般来说,它们会带来好处(嗯,至少对它们的创建者来说)。
姜饼和甜甜圈、瘀伤和肿块姜饼和甜甜圈:
1. 无需浪费时间去发明已经发明的东西。 采取并使用。
2、监控系统不是傻子写的,当然有用。
3. 工作监控系统通常会提供有用的过滤信息。
瘀伤和肿块:
1.这种情况下的工程师并不是工程师,而只是别人产品的用户,或者说是用户。
2. 必须让客户确信需要购买他一般不想了解的东西,也不应该购买,而且一般来说当年的预算已经批准,不会改变。 然后你需要分配一个单独的资源,为特定的系统配置它。 那些。 首先你需要付款、付款、再付款。 而且顾客很吝啬。 这是此生的常态。

车尔尼雪夫斯基该怎么办? 你的问题非常中肯。 (和)

在这种特殊情况和当前情况下,你可以做一些不同的事情 - 让我们制作自己的监控系统。
PostgreSQL 查询的性能监控。 第 1 部分 - 报告
好吧,不是一个系统,当然,从这个词的完整意义上来说,这太响亮和傲慢了,但至少以某种方式让你自己更容易并收集更多信息来解决性能事件。 为了不让自己陷入这样的境地——“去那里,我不知道在哪里,找到那个,我不知道是什么。”

此选项的优点和缺点是什么:

优点:
1. 很有趣。 好吧,至少比不断的“收缩数据文件、更改表空间等”更有趣。
2. 这些是新技能和新发展。 将来迟早会带来当之无愧的姜饼和甜甜圈。
缺点:
1.必须工作。 工作了很多。
2. 您必须定期解释所有活动的意义和观点。
3. 必须牺牲一些东西,因为工程师唯一可用的资源——时间——受到宇宙的限制。
4. 最糟糕和最不愉快的事情 ——结果可能会出现“不是老鼠,不是青蛙,而是不知名的小动物”之类的垃圾。

不冒险的人不会喝香槟。
那么,有趣的事情就开始了。

总体思路 - 示意图

PostgreSQL 查询的性能监控。 第 1 部分 - 报告
(插图取自文章 «综合作为提高 PostgreSQL 性能的方法之一“)

说明:

  • 目标数据库使用标准 PostgreSQL 扩展“pg_stat_statements”安装。
  • 在监控数据库中,我们创建一组服务表,用于存储初始阶段的pg_stat_statements历史记录,并用于配置未来的指标和监控
  • 在监控主机上,我们创建一组bash脚本,包括用于在工单系统中生成事件的脚本。

服务台

首先,一个示意性简化的 ERD,最后发生了什么:
PostgreSQL 查询的性能监控。 第 1 部分 - 报告
表格的简要说明端点 - 主机,实例的连接点
数据库 - 数据库选项
pg_stat_历史记录 - 历史表,用于存储目标数据库的pg_stat_statements视图的临时快照
度量词汇表 - 绩效指标词典
度量配置 - 单独指标的配置
- 正在监控的请求的特定指标
指标警报历史记录 - 性能警告的历史记录
日志查询 - 服务表,用于存储从 AWS 下载的 PostgreSQL 日志文件中解析的记录
底线 - 用作基础的时间段的参数
检查站 - 配置用于检查数据库状态的指标
检查点警报历史记录 - 数据库状态检查指标的警告历史记录
pg_stat_db_queries — 活动请求的服务表
活动日志 — 活动日志服务表
trap_oid - 陷阱配置服务表

第 1 阶段 - 收集性能统计数据并获取报告

表用于存储统计信息。 pg_stat_历史记录
pg_stat_history表结构

                                          表“public.pg_stat_history”列| 类型 | 修饰符--------------------+--------------------- --+---- -------------------------------- ID | 整数 | 默认 nextval('pg_stat_history_id_seq'::regclass) snapshot_timestamp | not null 没有时区的时间戳| 数据库 ID | 整数 | dbid | 类 | 用户 ID | 类 | 查询ID | 大整数 | 查询 | 文字| 来电 | 大整数 | 总时间 | 双精度 | 最短时间 | 双精度 | 最大时间 | 双精度 | 平均时间 | 双精度 | stddev_时间 | 双精度 | 行 | 大整数 | 共享_blks_hit | 大整数 | 共享_blks_read | 大整数 | 共享_blks_dirtied | 大整数 | 共享块写入| 大整数 | 本地_blks_hit | 大整数 | 本地_blks_read | 大整数 | 本地_blks_dirtied | 大整数 | 本地_blks_书面 | 大整数 | temp_blks_read | 大整数 | 临时BLKS_书面| 大整数 | blk_read_time | 块读取时间双精度 | blk_write_time | 块写入时间双精度 | 基线_id | 整数 | 索引:“pg_stat_history_pkey”主键,btree(id)“database_idx”btree(database_id)“queryid_idx”btree(queryid)“snapshot_timestamp_idx”btree(snapshot_timestamp)外键约束:“database_id_fk”外键(database_id)引用数据库(id) ) 删除级联

可以看到,该表只是一个累积视图数据 pg_stat_语句 在目标数据库中。

这个表的使用非常简单。

pg_stat_历史记录 将表示每小时查询执行的累积统计信息。 每小时开始,填表后,统计 pg_stat_语句 重置为 pg_stat_statements_reset().
注: 对持续时间超过 1 秒的请求进行统计。
填充 pg_stat_history 表

--pg_stat_history.sql
CREATE OR REPLACE FUNCTION pg_stat_history( ) RETURNS boolean AS $$
DECLARE
  endpoint_rec record ;
  database_rec record ;
  pg_stat_snapshot record ;
  current_snapshot_timestamp timestamp without time zone;
BEGIN
  current_snapshot_timestamp = date_trunc('minute',now());  
  
  FOR endpoint_rec IN SELECT * FROM endpoint 
  LOOP
    FOR database_rec IN SELECT * FROM database WHERE endpoint_id = endpoint_rec.id 
	  LOOP
	    
		RAISE NOTICE 'NEW SHAPSHOT IS CREATING';
		
		--Connect to the target DB	  
	    EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||endpoint_rec.host||' dbname='||database_rec.name||' user=USER password=PASSWORD '')';
 
        RAISE NOTICE 'host % and dbname % ',endpoint_rec.host,database_rec.name;
		RAISE NOTICE 'Creating snapshot of pg_stat_statements for database %',database_rec.name;
		
		SELECT 
	      *
		INTO 
		  pg_stat_snapshot
	    FROM dblink('LINK1',
	      'SELECT 
	       dbid , SUM(calls),SUM(total_time),SUM(rows) ,SUM(shared_blks_hit) ,SUM(shared_blks_read) ,SUM(shared_blks_dirtied) ,SUM(shared_blks_written) , 
           SUM(local_blks_hit) , SUM(local_blks_read) , SUM(local_blks_dirtied) , SUM(local_blks_written) , SUM(temp_blks_read) , SUM(temp_blks_written) , SUM(blk_read_time) , SUM(blk_write_time)
	       FROM pg_stat_statements WHERE dbid=(SELECT oid from pg_database where datname=current_database() ) 
		   GROUP BY dbid
  	      '
	               )
	      AS t
	       ( dbid oid , calls bigint , 
  	         total_time double precision , 
	         rows bigint , shared_blks_hit bigint , shared_blks_read bigint ,shared_blks_dirtied bigint ,shared_blks_written	 bigint ,
             local_blks_hit	 bigint ,local_blks_read bigint , local_blks_dirtied bigint ,local_blks_written bigint ,
             temp_blks_read	 bigint ,temp_blks_written bigint ,
             blk_read_time double precision , blk_write_time double precision	  
	       );
		 
		INSERT INTO pg_stat_history
          ( 
		    snapshot_timestamp  ,database_id  ,
			dbid , calls  ,total_time ,
            rows ,shared_blks_hit  ,shared_blks_read  ,shared_blks_dirtied  ,shared_blks_written ,local_blks_hit , 	 	
            local_blks_read,local_blks_dirtied,local_blks_written,temp_blks_read,temp_blks_written, 	
            blk_read_time, blk_write_time 
		  )		  
	    VALUES
	      (
	       current_snapshot_timestamp ,
		   database_rec.id ,
	       pg_stat_snapshot.dbid ,pg_stat_snapshot.calls,
	       pg_stat_snapshot.total_time,
	       pg_stat_snapshot.rows ,pg_stat_snapshot.shared_blks_hit ,pg_stat_snapshot.shared_blks_read ,pg_stat_snapshot.shared_blks_dirtied ,pg_stat_snapshot.shared_blks_written , 
           pg_stat_snapshot.local_blks_hit , pg_stat_snapshot.local_blks_read , pg_stat_snapshot.local_blks_dirtied , pg_stat_snapshot.local_blks_written , 
	       pg_stat_snapshot.temp_blks_read , pg_stat_snapshot.temp_blks_written , pg_stat_snapshot.blk_read_time , pg_stat_snapshot.blk_write_time 	   
	      );		   
		  
        RAISE NOTICE 'Creating snapshot of pg_stat_statements for queries with min_time more than 1000ms';
	
        FOR pg_stat_snapshot IN
          --All queries with max_time greater than 1000 ms
	      SELECT 
	        *
	      FROM dblink('LINK1',
	        'SELECT 
	         dbid , userid ,queryid,query,calls,total_time,min_time ,max_time,mean_time, stddev_time ,rows ,shared_blks_hit ,
			 shared_blks_read ,shared_blks_dirtied ,shared_blks_written , 
             local_blks_hit , local_blks_read , local_blks_dirtied , 
			 local_blks_written , temp_blks_read , temp_blks_written , blk_read_time , 
			 blk_write_time
	         FROM pg_stat_statements 
			 WHERE dbid=(SELECT oid from pg_database where datname=current_database() AND min_time >= 1000 ) 
  	        '

	                  )
	        AS t
	         ( dbid oid , userid oid , queryid bigint ,query text , calls bigint , 
  	           total_time double precision ,min_time double precision	 ,max_time double precision	 , mean_time double precision	 ,  stddev_time double precision	 , 
	           rows bigint , shared_blks_hit bigint , shared_blks_read bigint ,shared_blks_dirtied bigint ,shared_blks_written	 bigint ,
               local_blks_hit	 bigint ,local_blks_read bigint , local_blks_dirtied bigint ,local_blks_written bigint ,
               temp_blks_read	 bigint ,temp_blks_written bigint ,
               blk_read_time double precision , blk_write_time double precision	  
	         )
	    LOOP
		  INSERT INTO pg_stat_history
          ( 
		    snapshot_timestamp  ,database_id  ,
			dbid ,userid  , queryid  , query  , calls  ,total_time ,min_time ,max_time ,mean_time ,stddev_time ,
            rows ,shared_blks_hit  ,shared_blks_read  ,shared_blks_dirtied  ,shared_blks_written ,local_blks_hit , 	 	
            local_blks_read,local_blks_dirtied,local_blks_written,temp_blks_read,temp_blks_written, 	
            blk_read_time, blk_write_time 
		  )		  
	      VALUES
	      (
	       current_snapshot_timestamp ,
		   database_rec.id ,
	       pg_stat_snapshot.dbid ,pg_stat_snapshot.userid ,pg_stat_snapshot.queryid,pg_stat_snapshot.query,pg_stat_snapshot.calls,
	       pg_stat_snapshot.total_time,pg_stat_snapshot.min_time ,pg_stat_snapshot.max_time,pg_stat_snapshot.mean_time, pg_stat_snapshot.stddev_time ,
	       pg_stat_snapshot.rows ,pg_stat_snapshot.shared_blks_hit ,pg_stat_snapshot.shared_blks_read ,pg_stat_snapshot.shared_blks_dirtied ,pg_stat_snapshot.shared_blks_written , 
           pg_stat_snapshot.local_blks_hit , pg_stat_snapshot.local_blks_read , pg_stat_snapshot.local_blks_dirtied , pg_stat_snapshot.local_blks_written , 
	       pg_stat_snapshot.temp_blks_read , pg_stat_snapshot.temp_blks_written , pg_stat_snapshot.blk_read_time , pg_stat_snapshot.blk_write_time 	   
	      );
		  
        END LOOP;

        PERFORM dblink_disconnect('LINK1');  
				
	  END LOOP ;--FOR database_rec IN SELECT * FROM database WHERE endpoint_id = endpoint_rec.id 
    
  END LOOP;

RETURN TRUE;  
END
$$ LANGUAGE plpgsql;

结果,经过一定时间后表中 pg_stat_历史记录 我们将有一组表内容的快照 pg_stat_语句 目标数据库。

实际报道

使用简单的查询,您可以获得非常有用且有趣的报告。

给定时间段内的汇总数据

请求

SELECT 
  database_id , 
  SUM(calls) AS calls ,SUM(total_time)  AS total_time ,
  SUM(rows) AS rows , SUM(shared_blks_hit)  AS shared_blks_hit,
  SUM(shared_blks_read) AS shared_blks_read ,
  SUM(shared_blks_dirtied) AS shared_blks_dirtied,
  SUM(shared_blks_written) AS shared_blks_written , 
  SUM(local_blks_hit) AS local_blks_hit , 
  SUM(local_blks_read) AS local_blks_read , 
  SUM(local_blks_dirtied) AS local_blks_dirtied , 
  SUM(local_blks_written)  AS local_blks_written,
  SUM(temp_blks_read) AS temp_blks_read, 
  SUM(temp_blks_written) temp_blks_written , 
  SUM(blk_read_time) AS blk_read_time , 
  SUM(blk_write_time) AS blk_write_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NULL AND
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY database_id ;

数据库时间

to_char(间隔 '1 毫秒' * pg_total_stat_history_rec.total_time, 'HH24:MI:SS.MS')

输入/输出时间

to_char(间隔 '1 毫秒' * ( pg_total_stat_history_rec.blk_read_time + pg_total_stat_history_rec.blk_write_time ), 'HH24:MI:SS.MS')

按总时间排序的 TOP10 SQL

请求

SELECT 
  queryid , 
  SUM(calls) AS calls ,
  SUM(total_time)  AS total_time  	
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT 
GROUP BY queryid 
ORDER BY 3 DESC 
LIMIT 10
-------------------------------------------------- ------------------------------------------------ | 总执行时间排名前 10 的 SQL | #| 查询ID| 来电| 来电 %| 总时间(毫秒)| dbtime % +----+------------+------------+------------+-------- ------------------+---------- | 1| 821760255| 2| .00001|00:03:23.141(203141.681 毫秒)| 5.42 | 2 4152624390| 2| 00001| .00|03:13.929:193929.215(5.17 毫秒)| 3 | 1484454471 4| 00001| 00| .02|09.129:129129.057:3.44(4 毫秒)| 655729273 | 1 00000| 00| 02| .01.869|121869.981:3.25:5(2460318461 毫秒)| 1 | 00000 00| 01| 33.113| .93113.835|2.48:6:2194493487(4 毫秒)| 00001 | 00 00| 17.377| 17377.868| .46|7:1053044345:1(00000 毫秒)| .00 | 00| 06.156| 6156.352| .16|8:3644780286:1( 00000 毫秒)| .00 | 00| 01.063| 1063.830| .03|XNUMX:XNUMX:XNUMX(XNUMX 毫秒)| .XNUMX

按总 I/O 时间排列的 TOP10 SQL

请求

SELECT 
  queryid , 
  SUM(calls) AS calls ,
  SUM(blk_read_time + blk_write_time)  AS io_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY  queryid 
ORDER BY 3 DESC 
LIMIT 10
-------------------------------------------------- -------------------------------------- | 总 I/O 时间排名前 10 的 SQL | #| 查询ID| 来电| 来电 %| I/O 时间 (ms)|db I/O 时间 % +----+-----------+-----------+-------- ---+--------------------------------+------------ --| 1| 4152624390| 2| .00001|00:08:31.616(511616.592 毫秒)| 31.06 月 2 日 | 821760255| 2| 00001| .00|08:27.099:507099.036(30.78 毫秒)| 3 | 655729273| 1| 00000| .00|05:02.209:302209.137( 18.35 毫秒)| 4 | 2460318461 1| 00000| 00| .04|05.981:245981.117:14.93(5 毫秒)| 1484454471 | 4 00001| 00| 00| .39.144|39144.221:2.38:6( 2194493487 毫秒)| 4 | 00001 00| 00| 18.182| .18182.816|1.10:7:1053044345( 1 毫秒)| 00000 | 00 00| 16.611| 16611.722| .1.01|8:3644780286:1( 00000 毫秒)| 00 | 00 00.436| 436.205| 03| .XNUMX|XNUMX:XNUMX:XNUMX( XNUMX 毫秒)| .XNUMX

按最长执行时间排序的 TOP10 SQL

请求

SELECT 
  id AS snapshotid , 
  queryid , 
  snapshot_timestamp ,  
  max_time 
FROM 
  pg_stat_history 
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
ORDER BY 4 DESC 
LIMIT 10

-------------------------------------------------- ------------------------------------------------ | 最大执行时间排名前 10 的 SQL | #| 快照| 快照ID| 查询ID| 最大时间(毫秒)+----+------------------+------------+--------- --+---------------------------------------- | 1| 05.04.2019/01/03 4169:655729273| 00| 02| 01.869:121869.981:2(04.04.2019 毫秒)| 17| 00/4153/821760255 00:01| 41.570| 101570.841| 3:04.04.2019:16(00 毫秒)| 4146| 821760255/00/01 41.570:101570.841| 4| 04.04.2019| 16:00:4144(4152624390 毫秒)| 00| 01/36.964/96964.607 5:04.04.2019| 17| 00| 4151:4152624390:00(01 毫秒)| 36.964| 96964.607/6/05.04.2019 10:00| 4188| 1484454471| 00:01:33.452(93452.150 毫秒)| 7| 04.04.2019/17/00 4150:2460318461 | 00| 01| 33.113:93113.835:8(04.04.2019 毫秒)| 15| 00/4140/1484454471 00:00| 11.892| 11892.302| 9:04.04.2019:16(00 毫秒)| 4145| 1484454471/00/00 11.892:11892.302| 10| 04.04.2019| 17:00:4152(1484454471 毫秒)| 00| 00/11.892/11892.302 XNUMX:XNUMX| XNUMX| XNUMX| XNUMX:XNUMX:XNUMX(XNUMX 毫秒)| XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX:XNUMX:XNUMX(XNUMX 毫秒)

共享缓冲区读/写的 TOP10 SQL

请求

SELECT 
  id AS snapshotid , 
  queryid ,
  snapshot_timestamp , 
  shared_blks_read , 
  shared_blks_written 
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
  ( shared_blks_read > 0 OR shared_blks_written > 0 )
ORDER BY 4 DESC  , 5 DESC 
LIMIT 10
-------------------------------------------------- ------------------------------------------------ | 共享缓冲区读/写排名前十的 SQL | #| 快照| 快照ID| 查询ID| 共享块读取| 共享块写入 +----+--------------------+------------+------------ -+--------------------------------+--------------------- | 10| 1/04.04.2019/17 00:4153| 821760255| 797308| 0| 2 | 04.04.2019| 16/00/4146 821760255:797308| 0| 3| 05.04.2019| 01 | 03| 4169/655729273/797158 0:4| 04.04.2019| 16| 00| 4144 | 4152624390| 756514/0/5 04.04.2019:17| 00| 4151| 4152624390| 756514 | 0| 6/04.04.2019/17 00:4150| 2460318461| 734117| 0| 7 | 04.04.2019| 17/00/4155 3644780286:52973| 0| 8| 05.04.2019| 01 | 03| 4168/1053044345/52818 0:9| 04.04.2019| 15| 00| 4141 | 2194493487| 52813/0/10 04.04.2019:16| 00| 4147| 2194493487| 52813 | 0| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX| XNUMX | XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX| XNUMX -------------------------------------------------- -------------------------------------------------

按最大执行时间排列的查询分布直方图

请求

SELECT  
  MIN(max_time) AS hist_min  , 
  MAX(max_time) AS hist_max , 
  (( MAX(max_time) - MIN(min_time) ) / hist_columns ) as hist_width
FROM 
  pg_stat_history 
WHERE 
  queryid IS NOT NULL AND
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT ;

SELECT 
  SUM(calls) AS calls
FROM 
  pg_stat_history 
WHERE 
  queryid IS NOT NULL AND
  database_id =DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND 
  ( max_time >= hist_current_min AND  max_time < hist_current_max ) ;
|------------------------------------------------ ---------------------------------------------------- | 最大时间直方图 | 总通话次数:33851920 | 最短时间:00:00:01.063 | 最长时间:00:02:01.869 ---------------------------------- -------- ---------------------------- | 最短持续时间| 最长持续时间| 致电+--------------------------------+------------- ------------------+---------- | 00:00:01.063(1063.830 毫秒)| 00:00:13.144(13144.445 毫秒)| 9 | 00:00:13.144(13144.445 毫秒)| 00:00:25.225(25225.060 毫秒)| 0 | 00:00:25.225(25225.060 毫秒)| 00:00:37.305(37305.675 毫秒)| 0 | 00:00:37.305(37305.675 毫秒)| 00:00:49.386(49386.290 毫秒)| 0 | 00:00:49.386(49386.290 毫秒)| 00:01:01.466(61466.906 毫秒)| 0 | 00:01:01.466(61466.906 毫秒)| 00:01:13.547(73547.521 毫秒)| 0 | 00:01:13.547(73547.521 毫秒)| 00:01:25.628(85628.136 毫秒)| 0 | 00:01:25.628(85628.136 毫秒)| 00:01:37.708(97708.751 毫秒)| 4 | 00:01:37.708(97708.751 毫秒)| 00:01:49.789(109789.366 毫秒)| 2 | 00:01:49.789(109789.366 毫秒)| 00:02:01.869(121869.981 毫秒)| 0

按每秒查询次数排序的 TOP10 快照

请求

--pg_qps.sql
--Calculate Query Per Second 
CREATE OR REPLACE FUNCTION pg_qps( pg_stat_history_id integer ) RETURNS double precision AS $$
DECLARE
 pg_stat_history_rec record ;
 prev_pg_stat_history_id integer ;
 prev_pg_stat_history_rec record;
 total_seconds double precision ;
 result double precision;
BEGIN 
  result = 0 ;
  
  SELECT *
  INTO pg_stat_history_rec
  FROM 
    pg_stat_history
  WHERE id = pg_stat_history_id ;

  IF pg_stat_history_rec.snapshot_timestamp IS NULL 
  THEN
    RAISE EXCEPTION 'ERROR - Not found pg_stat_history for id = %',pg_stat_history_id;
  END IF ;  
  
 --RAISE NOTICE 'pg_stat_history_id = % , snapshot_timestamp = %', pg_stat_history_id , 
 pg_stat_history_rec.snapshot_timestamp ;
  
  SELECT 
    MAX(id)   
  INTO
    prev_pg_stat_history_id
  FROM
    pg_stat_history
  WHERE 
    database_id = pg_stat_history_rec.database_id AND
	queryid IS NULL AND
	id < pg_stat_history_rec.id ;

  IF prev_pg_stat_history_id IS NULL 
  THEN
    RAISE NOTICE 'Not found previous pg_stat_history shapshot for id = %',pg_stat_history_id;
	RETURN NULL ;
  END IF;
  
  SELECT *
  INTO prev_pg_stat_history_rec
  FROM 
    pg_stat_history
  WHERE id = prev_pg_stat_history_id ;
  
  --RAISE NOTICE 'prev_pg_stat_history_id = % , prev_snapshot_timestamp = %', prev_pg_stat_history_id , prev_pg_stat_history_rec.snapshot_timestamp ;    

  total_seconds = extract(epoch from ( pg_stat_history_rec.snapshot_timestamp - prev_pg_stat_history_rec.snapshot_timestamp ));
  
  --RAISE NOTICE 'total_seconds = % ', total_seconds ;    
  
  --RAISE NOTICE 'calls = % ', pg_stat_history_rec.calls ;      
  
  IF total_seconds > 0 
  THEN
    result = pg_stat_history_rec.calls / total_seconds ;
  ELSE
   result = 0 ; 
  END IF;
   
 RETURN result ;
END
$$ LANGUAGE plpgsql;


SELECT 
  id , 
  snapshot_timestamp ,
  calls , 	
  total_time , 
  ( select pg_qps( id )) AS QPS ,
  blk_read_time ,
  blk_write_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
  ( select pg_qps( id )) IS NOT NULL 
ORDER BY 5 DESC 
LIMIT 10
|------------------------------------------------ ---------------------------------------------------- | 按 QueryPerSeconds 数字排序的 TOP10 快照 -------------------------------------- ------ -------------------------------------------------------- ------ ------------------------------------------- | #| 快照| 快照ID| 来电| 总数据库时间| 每秒查询量 | I/O 时间 | I/O 时间% +-----+--------------------+------------+-------- ---+--------------------------------+---------- ----------------------------------+------------------------ | 1| 04.04.2019/20/04 4161:5758631| 00| 06| 30.513:390513.926:1573.396(00 毫秒)| 00| 01.470:1470.110:376(2 毫秒)| .04.04.2019| 17| 00/4149/3529197 00:11| 48.830| 708830.618| 980.332:00:12(47.834 毫秒)| 767834.052| 108.324:3:04.04.2019(16 毫秒)| 00 | 4143 3525360| 00/10/13.492 613492.351:979.267| 00| 08| 41.396:521396.555:84.988(4 毫秒)| 04.04.2019| 21:03:4163(2781536 毫秒)| 00 | 03| 06.470/186470.979/785.745 00:00| 00.249| 249.865| 134:5:04.04.2019(19 毫秒)| 03| 4159:2890362:00(03 毫秒)| .16.784| 196784.755| 776.979/00/00 01.441:1441.386| 732| 6| 04.04.2019:14:00(4137 毫秒)| 2397326| 00:04:43.033(283033.854 毫秒)| .665.924| 00| 00/00.024/24.505 009:7| 04.04.2019| 15| 00:4139:2394416(00 毫秒)| 04| 51.435:291435.010:665.116(00 毫秒)| .00| 12.025| 12025.895/4.126/8 04.04.2019:13| 00| 4135| 2373043:00:04(26.791 毫秒)| 266791.988| 659.179:00:00(00.064 毫秒)| 64.261 | 024 9| 05.04.2019/01/03 4167:4387191| 00| 06| 51.380:411380.293:609.332(00 毫秒)| 05| 18.847:318847.407:77.507(10 毫秒)| .04.04.2019| 18| 01/4157/1145596 00:01| 19.217| 79217.372| 313.004:00:00(01.319 毫秒)| 1319.676| 1.666:XNUMX:XNUMX(XNUMX 毫秒)| XNUMX | XNUMX XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX:XNUMX:XNUMX(XNUMX 毫秒)| XNUMX| XNUMX:XNUMX:XNUMX(XNUMX 毫秒)| XNUMX

具有 QueryPerSeconds 和 I/O 时间的每小时执行历史记录

请求

SELECT 
  id , 
  snapshot_timestamp ,
  calls , 	
  total_time , 
  ( select pg_qps( id )) AS QPS ,
  blk_read_time ,
  blk_write_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
ORDER BY 2
|-----------------------------------------------------------------------------------------------
| HOURLY EXECUTION HISTORY  WITH QueryPerSeconds and I/O Time
-----------------------------------------------------------------------------------------------------------------------------------------------
| QUERY PER SECOND HISTORY
|    #|          snapshot| snapshotID|      calls|                      total dbtime|        QPS|                          I/O time| I/O time %
+-----+------------------+-----------+-----------+----------------------------------+-----------+----------------------------------+-----------
|    1|  04.04.2019 11:00|       4131|       3747|  00:00:00.835(       835.374 ms.)|      1.041|  00:00:00.000(          .000 ms.)|       .000
|    2|  04.04.2019 12:00|       4133|    1002722|  00:01:52.419(    112419.376 ms.)|    278.534|  00:00:00.149(       149.105 ms.)|       .133
|    3|  04.04.2019 13:00|       4135|    2373043|  00:04:26.791(    266791.988 ms.)|    659.179|  00:00:00.064(        64.261 ms.)|       .024
|    4|  04.04.2019 14:00|       4137|    2397326|  00:04:43.033(    283033.854 ms.)|    665.924|  00:00:00.024(        24.505 ms.)|       .009
|    5|  04.04.2019 15:00|       4139|    2394416|  00:04:51.435(    291435.010 ms.)|    665.116|  00:00:12.025(     12025.895 ms.)|      4.126
|    6|  04.04.2019 16:00|       4143|    3525360|  00:10:13.492(    613492.351 ms.)|    979.267|  00:08:41.396(    521396.555 ms.)|     84.988
|    7|  04.04.2019 17:00|       4149|    3529197|  00:11:48.830(    708830.618 ms.)|    980.332|  00:12:47.834(    767834.052 ms.)|    108.324
|    8|  04.04.2019 18:01|       4157|    1145596|  00:01:19.217(     79217.372 ms.)|    313.004|  00:00:01.319(      1319.676 ms.)|      1.666
|    9|  04.04.2019 19:03|       4159|    2890362|  00:03:16.784(    196784.755 ms.)|    776.979|  00:00:01.441(      1441.386 ms.)|       .732
|   10|  04.04.2019 20:04|       4161|    5758631|  00:06:30.513(    390513.926 ms.)|   1573.396|  00:00:01.470(      1470.110 ms.)|       .376
|   11|  04.04.2019 21:03|       4163|    2781536|  00:03:06.470(    186470.979 ms.)|    785.745|  00:00:00.249(       249.865 ms.)|       .134
|   12|  04.04.2019 23:03|       4165|    1443155|  00:01:34.467(     94467.539 ms.)|    200.438|  00:00:00.015(        15.287 ms.)|       .016
|   13|  05.04.2019 01:03|       4167|    4387191|  00:06:51.380(    411380.293 ms.)|    609.332|  00:05:18.847(    318847.407 ms.)|     77.507
|   14|  05.04.2019 02:03|       4171|     189852|  00:00:10.989(     10989.899 ms.)|     52.737|  00:00:00.539(       539.110 ms.)|      4.906
|   15|  05.04.2019 03:01|       4173|       3627|  00:00:00.103(       103.000 ms.)|      1.042|  00:00:00.004(         4.131 ms.)|      4.010
|   16|  05.04.2019 04:00|       4175|       3627|  00:00:00.085(        85.235 ms.)|      1.025|  00:00:00.003(         3.811 ms.)|      4.471
|   17|  05.04.2019 05:00|       4177|       3747|  00:00:00.849(       849.454 ms.)|      1.041|  00:00:00.006(         6.124 ms.)|       .721
|   18|  05.04.2019 06:00|       4179|       3747|  00:00:00.849(       849.561 ms.)|      1.041|  00:00:00.000(          .051 ms.)|       .006
|   19|  05.04.2019 07:00|       4181|       3747|  00:00:00.839(       839.416 ms.)|      1.041|  00:00:00.000(          .062 ms.)|       .007
|   20|  05.04.2019 08:00|       4183|       3747|  00:00:00.846(       846.382 ms.)|      1.041|  00:00:00.000(          .007 ms.)|       .001
|   21|  05.04.2019 09:00|       4185|       3747|  00:00:00.855(       855.426 ms.)|      1.041|  00:00:00.000(          .065 ms.)|       .008
|   22|  05.04.2019 10:00|       4187|       3797|  00:01:40.150(    100150.165 ms.)|      1.055|  00:00:21.845(     21845.217 ms.)|     21.812

所有 SQL 选择的文本

请求

SELECT 
  queryid , 
  query 
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY queryid , query

正如您所看到的,通过相当简单的方法,您可以获得有关工作负载和数据库状态的大量有用信息。

Primechenie:如果您修复查询中的queryid,那么我们将获得单独请求的历史记录(为了节省空间,省略了单独请求的报告)。

因此,可以获取并收集有关查询性能的统计数据。
第一阶段“统计数据收集”已经完成。

您可以继续第二阶段——“配置性能指标”。
PostgreSQL 查询的性能监控。 第 1 部分 - 报告

但这是一个完全不同的故事。

待续...

来源: habr.com

添加评论