我有一个记录有关正在运行的进程的信息的系统。每个正在运行的进程都包含一系列可能并行运行也可能不并行运行的步骤。系统将有关进程及其步骤的信息记录到两个单独的表中:
CREATE TABLE pid (
pid integer,
start_time timestamp,
end_time timestamp,
elapsed bigint,
aborted integer,
label char(30)
);
CREATE TABLE pid_step (
pid integer,
step integer,
start_time timestamp,
end_time timestamp,
elapsed bigint,
mem bigint,
...
);
该pid_step
表包含关于每个步骤的大量资源使用统计信息,我在此处将其简化为仅mem
记录为该步骤分配的内存字节数的列。我想按进程标签对内存分配进行采样,也许每隔 5 秒,所以我可以绘制它。我需要类似于以下的结果:
tick label mem
----------------------- ------ -----------
2014-11-04 05:37:40.0 foo 328728576
2014-11-04 05:37:40.0 bar 248436
2014-11-04 05:37:40.0 baz 1056144
2014-11-04 05:37:45.0 foo 1158807552
2014-11-04 05:37:45.0 bar 632822
2014-11-04 05:37:45.0 baz 854398
由于日志只给我每个进程和步骤的开始和结束时间戳,而不是每隔 5 秒的资源使用示例,我需要找到最有效的方法来确定每 5 秒间隔运行哪些进程步骤(打勾)然后聚合他们分配的内存。我已经进行了 3 次单独的尝试,它们都产生了相同的结果,但性能水平不同。为简洁起见,我将把每个查询及其解释计划放在一个要点中(https://gist.github.com/anonymous/3b57f70015b0d234a2de),但我会为每个查询解释我的方法:
这是我的第一次尝试,它绝对是最直观和最容易维护的。它交叉连接不同的进程标签,
generate_series
为每个标签生成 5 秒的刻度,然后在pid
和pid_step
表上进行左连接。左连接创建“零填充”效果,并确保我们不会丢弃任何没有关联数据的报价。不幸的是,这种方法表现最差(请参阅下面的基准链接),我相信这是由于使用了哈希连接,其中between t2.start_time and t2.end_time
谓词被处理为连接过滤器而不是连接条件。这是我的第二次尝试,它的性能更好,但不太直观和可维护。“零填充”方法与查询 1 中的方法相同。但是,在执行 and 的左连接之前
pid
,pid_step
我根据最大进程经过时间和进程步骤开始和结束时间预先计算具有关联数据的刻度. 这允许排序合并连接,其中刻度和标签谓词都可以表示为连接条件,并且不使用连接过滤器。这是我的最后一次尝试,它以与查询 2 大致相同的直观性和可维护性表现最佳。这里的优化是我使用了最大流程步骤经过时间,它保证小于最大流程经过时间,因此创建了一个CTE t3 开始时的较小嵌套循环。
理想情况下,我希望 SQL 与查询 1 一样简单且可维护,但性能与查询 3 一样好。我可以通过索引或稍微重写查询 1 来提高性能吗?
基准测试结果:http: //i.imgur.com/yZxdQlM.png