为什么 select 结果在 postgreSql 的分区 table 中需要很长时间?
Why select result takes long time in partitioned table in postgreSql?
我在 postgresql 中有一个每日分区 table。它使用 cdr_date 进行分区。当我 select 一个简单的查询时,它需要很长时间我不知道为什么!
这是一个简单的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
这是我的根table
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);
这是我的 child table
-- 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);
因为你的分区很大,你基本上select分区中的大部分数据。
过滤不等于检查,所以在确定使用哪个分区后,它仍然扫描索引。
我可以提出 3 种可以协同工作的解决方案:
- 不要在如此高分辨率的范围内进行分区。考虑添加另一个字段,它只是
DATE
组件,并改为使用相等运算符进行检查。这也将确保您的分区不会像本例那样重叠。在这种情况下,这不会有太大帮助,除非您真的想 select 来自单个分区的所有数据。
Cluster cdr_date 索引上的 table,这将大大加快此类查询的速度。
CLUSTER cdr_2018_05_24 USING ind_cdr_2018_05_24
考虑按小时对分区进行分区,以防您经常 select 较小的时间范围。 700 万行对于这样的查询来说已经很多了。
在根 table 的索引扫描中找到 0 行不可能花费 5 秒。我会说你的根table(或索引,无论如何)非常臃肿。如果是这样,也许您的其他人也是如此。您是否对这些 table 进行了充分的吸尘,甚至根本没有吸尘?在 pg_stat_user_tables
中查看它们最后一次被手动或自动清理的时间。
我在 postgresql 中有一个每日分区 table。它使用 cdr_date 进行分区。当我 select 一个简单的查询时,它需要很长时间我不知道为什么!
这是一个简单的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
这是我的根table
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);
这是我的 child table
-- 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);
因为你的分区很大,你基本上select分区中的大部分数据。
过滤不等于检查,所以在确定使用哪个分区后,它仍然扫描索引。
我可以提出 3 种可以协同工作的解决方案:
- 不要在如此高分辨率的范围内进行分区。考虑添加另一个字段,它只是
DATE
组件,并改为使用相等运算符进行检查。这也将确保您的分区不会像本例那样重叠。在这种情况下,这不会有太大帮助,除非您真的想 select 来自单个分区的所有数据。 Cluster cdr_date 索引上的 table,这将大大加快此类查询的速度。
CLUSTER cdr_2018_05_24 USING ind_cdr_2018_05_24
考虑按小时对分区进行分区,以防您经常 select 较小的时间范围。 700 万行对于这样的查询来说已经很多了。
在根 table 的索引扫描中找到 0 行不可能花费 5 秒。我会说你的根table(或索引,无论如何)非常臃肿。如果是这样,也许您的其他人也是如此。您是否对这些 table 进行了充分的吸尘,甚至根本没有吸尘?在 pg_stat_user_tables
中查看它们最后一次被手动或自动清理的时间。