分区表中的慢查询

Posted

技术标签:

【中文标题】分区表中的慢查询【英文标题】:Slow Query in partitioned table 【发布时间】:2021-09-09 05:16:09 【问题描述】:

我们有 postgresql12 并且有一个大小为 108gb 的大表,包括索引。由于查询变慢了,我们尝试对表进行分区。但这没有帮助。

    EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS)
SELECT
    val_a AS x,
    val_b AS y,
    SUM(value) AS value
FROM
    test_table_tp
WHERE
    client = '767jjDHIPLkshj'
    AND identity_1 = '12edfdijijasd'
    AND identity_2 = '98jjaskhuUUHss'
    AND identity_3 = 1
    AND date_col BETWEEN '2021-04-01'::date AND ('2021-07-01'::date + Interval '1 day')::date
GROUP BY
    val_a,
    val_b;

这需要大约 3 分钟来执行。

->  HashAggregate  (cost=260438.41..260718.30 rows=27989 width=16) (actual time=214427.239..214427.614 rows=1298 loops=1)
               Output: test_table_tp.val_a, test_table_tp.val_b, sum(test_table_tp.value)
               Group Key: test_table_tp.val_a, test_table_tp.val_b
               Buffers: shared hit=216750 read=331936
               I/O Timings: read=211977.843
               ->  Append  (cost=0.56..258339.24 rows=279890 width=12) (actual time=3.057..213976.334 rows=722233 loops=1)
                     Buffers: shared hit=216750 read=331936
                     I/O Timings: read=211977.843
                     ->  Index Scan using idx_202104_108521 on public.test_table_tp_p2021_04_108521 test_table_tp  (cost=0.56..71154.06 rows=83535 width=12) (actual time=3.056..69033.315 rows=216908 loops=1)
                           Output: test_table_tp.val_a, test_table_tp.val_b, test_table_tp.value
                           Index Cond: (((test_table_tp.client)::text = '767jjDHIPLkshj'::text) AND ((test_table_tp.identity_1)::text = '12edfdijijasd'::text) AND ((test_table_tp.identity_2)::text = '98jjaskhuUUHss'::text) AND (test_table_tp.identity_3 = 1) AND (test_table_tp.date_col >= '2021-04-01'::date) AND (test_table_tp.date_col <= '2021-07-02'::date))
                           Buffers: shared hit=68990 read=96437
                           I/O Timings: read=68466.167
                     ->  Index Scan using idx_202105_108553 on public.test_table_tp_p2021_05_108553 test_table_tp_1  (cost=0.56..51361.84 rows=55441 width=12) (actual time=8.641..55685.999 rows=160618 loops=1)
                           Output: test_table_tp_1.val_a, test_table_tp_1.val_b, test_table_tp_1.value
                           Index Cond: (((test_table_tp_1.client)::text = '767jjDHIPLkshj'::text) AND ((test_table_tp_1.identity_1)::text = '12edfdijijasd'::text) AND ((test_table_tp_1.identity_2)::text = '98jjaskhuUUHss'::text) AND (test_table_tp_1.identity_3 = 1) AND (test_table_tp_1.date_col >= '2021-04-01'::date) AND (test_table_tp_1.date_col <= '2021-07-02'::date))
                           Buffers: shared hit=48314 read=69911
                           I/O Timings: read=55277.406
                     ->  Index Scan using idx_202106_108585 on public.test_table_tp_p2021_06_108585 test_table_tp_2  (cost=0.56..63581.82 rows=66779 width=12) (actual time=2.870..48249.339 rows=188842 loops=1)
                           Output: test_table_tp_2.val_a, test_table_tp_2.val_b, test_table_tp_2.value
                           Index Cond: (((test_table_tp_2.client)::text = '767jjDHIPLkshj'::text) AND ((test_table_tp_2.identity_1)::text = '12edfdijijasd'::text) AND ((test_table_tp_2.identity_2)::text = '98jjaskhuUUHss'::text) AND (test_table_tp_2.identity_3 = 1) AND (test_table_tp_2.date_col >= '2021-04-01'::date) AND (test_table_tp_2.date_col <= '2021-07-02'::date))
                           Buffers: shared hit=54983 read=90249
                           I/O Timings: read=47732.316
                     ->  Index Scan using idx_202107_108617 on public.test_table_tp_p2021_07_108617 test_table_tp_3  (cost=0.56..70842.08 rows=74135 width=12) (actual time=2.849..40902.561 rows=155865 loops=1)
                           Output: test_table_tp_3.val_a, test_table_tp_3.val_b, test_table_tp_3.value
                           Index Cond: (((test_table_tp_3.client)::text = '767jjDHIPLkshj'::text) AND ((test_table_tp_3.identity_1)::text = '12edfdijijasd'::text) AND ((test_table_tp_3.identity_2)::text = '98jjaskhuUUHss'::text) AND (test_table_tp_3.identity_3 = 1) AND (test_table_tp_3.date_col >= '2021-04-01'::date) AND (test_table_tp_3.date_col <= '2021-07-02'::date))
                           Buffers: shared hit=44463 read=75339
                           I/O Timings: read=40501.954
             Planning Time: 18.081 ms
             Execution Time: 214427.963 ms
 Planning Time: 0.083 ms
 Execution Time: 214461.427 ms
(41 rows)

Time: 214462.391 ms (03:34.462)

这已经是一个包含每月汇总数据的汇总表。进一步的聚合也是不可能的,因为可以应用许多过滤器。标识列在表中具有唯一键。

可以做些什么来进一步优化它吗?任何帮助是极大的赞赏。提前谢谢你。

【问题讨论】:

这几乎完全是 I/O 绑定的,所以更快和更多的磁盘会有所帮助。或者(更多)更多 RAM,以便可以缓存数据 @a_horse_with_no_name - 我们的磁盘有 16000 IOPS,最大吞吐量为 1000 MB/s,我们的机器有 128GB RAM。检查使用情况,这些都没有达到最大限制。 知道为什么这里没有并行查询吗? 1 IO 可以有多大?如果 8kb 算作 8 个 IO,那么您将非常接近指定的 IOPS。 @jjanes - 是的,我们现在正计划增加 IOPS,看看它是否会提高速度。关于并行查询,我们认为是查询计划者决定不使用并行查询 【参考方案1】:

这个查询会因为分区而变得变慢,虽然不是很多。

除了获得更快的磁盘或更多 RAM 之外,加快此查询的唯一方法是

    val_aval_bvalue 包含在索引中,将VACUUM 包含在表中,以便您获得快速的仅索引扫描

    CREATE INDEX ON test_table_tp (
       identity_1,
       identity_2,
       identity_3,
       date_col,
       val_a,
       val_b,
       value
    );
    

    CLUSTER使用索引的表,这样行存储在更少的块中

【讨论】:

我们有一个包含所有列的唯一索引,以支持可以应用的过滤器。关于Cluster table,谢谢,试试看是否有帮助。 建议包括更多列。 谢谢,是的,我们已经在我们独特的索引中找到了它。但没有帮助 您是否没有获得仅索引扫描? @successmalla 什么是唯一索引定义?仅存在所有列是不够的,它们还必须按正确的顺序排列。首先是相等测试的,然后是范围测试的,然后是其他的。

以上是关于分区表中的慢查询的主要内容,如果未能解决你的问题,请参考以下文章

phpMyAdmin中的慢查询但Mysql慢查询日志文件中没有[关闭]

laravel 中的慢查询

查询中的慢排序

Postgres 中的慢查询优化

优化 WordPress 插件“更好的 WordPress 最近评论”中的慢查询

死磕 Redis----- 如何排查 Redis 中的慢查询