为什么选择结果需要在postgreSql中的分区表中花费很长时间?

时间:2018-05-26 14:29:56

标签: database postgresql indexing partitioning database-partitioning

我在postgresql中有一个每日分区表。它使用cdr_date进行分区。当我选择一个简单的查询时,我需要很长时间才知道原因!

这是一个简单的sql

EXPLAIN (ANALYZE , BUFFERS )
select * FROM cdr
WHERE cdr_date >= '2018-05-24 11:59:00.937000 +00:00'
  AND cdr_date <= '2018-05-25 23:59:59.937000 +00:00'

结果

Append  (cost=0.56..1036393.46 rows=14908437 width=295) (actual time=5019.283..335535.305 rows=15191628 loops=1)
  Buffers: shared hit=252735 read=1443977 written=125'
  ->  Index Scan using ind_cdr_cdr_date on cdr  (cost=0.56..8.58 rows=1 width=286) (actual time=5019.190..5019.190 rows=0 loops=1)'
        Index Cond: ((cdr_date >= ''2018-05-24 11:59:00.937+00''::timestamp with time zone) AND (cdr_date <= ''2018-05-25 23:59:59.937+00''::timestamp with time zone))
        Buffers: shared hit=178464 read=708130 written=125
  ->  Index Scan using ind_cdr_2018_05_24 on cdr_2018_05_24  (cost=0.43..567998.02 rows=7158579 width=295) (actual time=0.091..311773.252 rows=7846816 loops=1)
        Index Cond: ((cdr_date >= ''2018-05-24 11:59:00.937+00''::timestamp with time zone) AND (cdr_date <= ''2018-05-25 23:59:59.937+00''::timestamp with time zone))
        Buffers: shared hit=74264 read=383715
  ->  Seq Scan on cdr_2018_05_25  (cost=0.00..468386.85 rows=7749857 width=295) (actual time=5.192..16189.737 rows=7344812 loops=1)
        Filter: ((cdr_date >= ''2018-05-24 11:59:00.937+00''::timestamp with time zone) AND (cdr_date <= ''2018-05-25 23:59:59.937+00''::timestamp with time zone))
        Buffers: shared hit=7 read=352132
Planning time: 3.394 ms
Execution time: 336984.703 ms

这是我的根表

CREATE TABLE cdr
(
  id                      BIGSERIAL                NOT NULL
    CONSTRAINT cdr_pkey
    PRIMARY KEY,
  username                VARCHAR(256)             NOT NULL,
  user_id                 BIGINT,
  cdr_date                TIMESTAMP WITH TIME ZONE NOT NULL,
  created_at              TIMESTAMP WITH TIME ZONE NOT NULL,
  last_reset_time         TIMESTAMP WITH TIME ZONE,
  prev_cdr_date           TIMESTAMP WITH TIME ZONE NOT NULL
);

CREATE INDEX ind_cdr_user_id
  ON cdr (user_id);

CREATE INDEX ind_cdr_cdr_date
  ON cdr (cdr_date);

这是我的一个子表

-- auto-generated definition
CREATE TABLE cdr_2018_05_25
(
  CONSTRAINT cdr_2018_05_25_cdr_date_check
  CHECK ((cdr_date >= '2018-05-25 00:00:00+00' :: TIMESTAMP WITH TIME ZONE) AND
         (cdr_date <= '2018-05-26 00:23:29.064408+00' :: TIMESTAMP WITH TIME ZONE))
)
  INHERITS (cdr);

CREATE INDEX ind_cdr_2018_05_25_user_id
  ON cdr_2018_05_25 (user_id);

CREATE INDEX ind_cdr_2018_05_25
  ON cdr_2018_05_25 (cdr_date);

2 个答案:

答案 0 :(得分:0)

因为您的分区很大,而且您基本上选择了分区中的大部分数据。

过滤器不等于检查,因此在确定要使用哪个分区后,它仍会扫描索引。

我可以提出3个可以协同工作的解决方案:

  1. 不要在具有如此高分辨率的范围上进行分区。考虑添加另一个字段,它只是DATE组件,并使用相等运算符进行检查。这也将确保您的分区不会像这种情况那样重叠。除非您真的想从单个分区中选择所有数据,否则在这种情况下,这不会有多大帮助。
  2. Cluster cdr_date索引上的表格,这将大大加快此类查询的速度。

    CLUSTER cdr_2018_05_24 USING ind_cdr_2018_05_24
    
  3. 考虑按小时划分分区,以防您经常选择较小的时间范围。对于这样的查询,700万行非常多。

答案 1 :(得分:0)

在根表的索引扫描中找不到0行是不可能的。我会说你的根表(或索引,无论如何)是大量膨胀。如果是这样的话,也许你的其他人也是如此。你是否充分吸尘这些桌子,甚至根本不吸尘?查看pg_stat_user_tables最后一次抽真空,手动或自动。