与Oracle中一样,我们监视PostgreSQL 10的活动会话

图片

当我发现PostgreSQL 10中的pg_stat_activity视图具有wait_event_type和wait_event字段时,出于对体育运动的兴趣而编写了该工具,它们在本质上与v $ session中的wait_class和event非常相似。

目前,我正在积极使用akardapolov的 ASH-Viewer程序,我很好奇在Postgres下重写该产品有多么困难。 考虑到我不是专业的开发人员,这并不容易,但很有趣。 在我看来,在此过程中,我什至发现在Oracle原始程序和Standard Edition中都出现了几个重要的错误。

PASH-Viewer的原理:


无需扩展。 我们仅从内置的pg_stat_activity视图中获取数据。

每秒一次,请求进行活动会话:

pg_stat_activity请求文本
SELECT current_timestamp, datname, pid, usesysid, usename, application_name, backend_type, coalesce(client_hostname, client_addr::text, 'localhost') as client_hostname, wait_event_type, wait_event, query, query_start, 1000 * EXTRACT(EPOCH FROM (clock_timestamp()-query_start)) as duration from pg_stat_activity where state='active' and pid != pg_backend_pid(); 

每15秒一次,对最后15张图像的数据进行平均并显示在图表上。

我自己生成了在“ Top SQL”部分中对查询进行分组所需的SQL id,它与pg_stat_statements中的queryid没有关系。 我当时在考虑如何使用queryid,但是不幸的是,我没有找到一种方法来匹配这两个视图中的查询。 如果开发人员将queryid字段添加到pg_stat_activity,那就太好了。

SQL id = md5(标准化查询文本)中的前13个字符。

规范化查询文本是这样的查询,其中删除了换行符和多余的空格,并将文字替换为$ 1,$ 2等。。。这对我来说很难编写一个好的查询规范化函数。 我写的不好。 我引用了文本,但请不要看,否则我会感到ham愧。 最好发送一个好。

标准化SQL
 public static String NormalizeSQL(String sql) { sql = sql.replaceAll("\\n", " "); sql = sql.replaceAll("\\(", " ( "); sql = sql.replaceAll("\\)", " ) "); sql = sql.replaceAll(",", " , "); sql = sql.replaceAll("'", " ' "); sql = sql.replaceAll("=", " = "); sql = sql.replaceAll("<", " < "); sql = sql.replaceAll(">", " > "); sql = sql.replaceAll(";", ""); sql = sql.replaceAll("[ ]+", " "); sql = sql.replaceAll("> =", ">="); sql = sql.replaceAll("< =", "<="); sql = sql.toLowerCase().trim(); String[] array = sql.split(" ", -1); int var_number = 0; String normalized_sql = ""; Boolean quote_flag = false; for (int i = 0; i < array.length; i++) { if (array[i].equals("'")) { if (!quote_flag) { quote_flag = true; var_number++; normalized_sql += "$" + var_number + " "; } else { quote_flag = false; } } else if (quote_flag) { continue; } else if (array[i].matches("-?\\d+(\\.\\d+)?")) { var_number++; normalized_sql += "$" + var_number + " "; } else if (array[i].equals("order")) { for (int j = i; j < array.length; j++) { normalized_sql += array[j] + " "; } return normalized_sql.trim(); } else { normalized_sql += array[i] + " "; } } return normalized_sql.trim(); } 

很难完成查询执行计划。 对Oracle来说,您说:“给我一个sqlid = ...的计划”,他会回答您-“您有最新的还是昨天的,或者显示上个月的所有内容以及每个月的执行统计信息?” PostgreSQL会回答您-“什么是sqlid?”。

因此,对于SELECT / UPDATE / INSERT / DELETE形式的查询,我们将EXPLAIN命令发送到数据库并将结果保存在本地。 我们每小时进行的时间不超过1次。 在调试过程中,发现EXPLAIN挂在锁上的方式与请求本身挂起的方式相同,我们希望了解该计划。 因此,我必须添加setQueryTimeout(1)。

并且仅当请求在您连接到的同一数据库中执行时才有效(配置连接时指示)。 并且仅当您以超级用户(postgres)身份连接到数据库时,有些用户才会担心。 因此,您可以创建一个特殊的用户进行监视。 除了显示计划外,所有其他东西都可以使用。

 CREATE USER pgmonuser WITH password 'pgmonuser'; GRANT pg_monitor TO pgmonuser; 

从GitHub下载: https : //github.com/dbacvetkov/PASH-Viewer/releases

UPD:
在0.3版中,它增加了对PostgreSQL 9.6(只有两个等待类-Lock和LWLock,其他所有内容都类似于“ CPU”)和PostgreSQL 9.4-9.5(通常有CPU或Lock等待)的支持。
在版本0.3.1中,在“热门会话”中添加了“后端类型”字段,并摆脱了图表上的白条。
在0.3.2版中,改进了计划的工作,添加了一些有关请求的统计信息(AVG持续时间,呼叫计数)以及查看历史数据的功能:
如何创建pg_stat_activity-historical-table

谢谢你的问候:
亚历山大·卡达波洛夫(Alexander Kardapolov),代表ASH-Viewer。
Anton Glushakov进行咨询和测试。
Dmitry Rudopysov解释了如何编译和运行从github下载的项目。

更多幻灯片:


图片

图片

图片

Source: https://habr.com/ru/post/zh-CN413411/


All Articles