【发布时间】:2025-12-29 04:15:11
【问题描述】:
我正在运行的查询随着记录的添加而逐渐变慢。 记录是通过自动化过程(bash 调用 psql)不断添加的。 我想纠正这个瓶颈;但是,我不知道我最好的选择是什么。
Hour Count Duration Avg duration
00 9,990 10m3s 60ms <---ignore this hour
02 1 60ms 60ms <---ignore this hour
03 4,638 1m54s 24ms <---queries begin with table empty
04 30,991 55m49s 108ms <---first full hour of queries running
05 13,497 58m3s 258ms
06 9,904 58m32s 354ms
07 10,542 58m25s 332ms
08 8,599 58m42s 409ms
09 7,360 58m52s 479ms
10 6,661 58m57s 531ms
11 6,133 59m2s 577ms
12 5,601 59m6s 633ms
13 5,327 59m9s 666ms
14 4,964 59m12s 715ms
15 4,759 59m14s 746ms
16 4,531 59m17s 785ms
17 4,330 59m18s 821ms
18 939 13m16s 848ms
表结构如下:
CREATE TABLE "Parent" (
"ParentID" SERIAL PRIMARY KEY,
"Details1" VARCHAR
);
表"Parent"与表"Foo"是一对多的关系:
CREATE TABLE "Foo" (
"FooID" SERIAL PRIMARY KEY,
"ParentID" int4 NOT NULL REFERENCES "Parent" ("ParentID"),
"Details1" VARCHAR
);
表"Foo"与表"Bar"是一对多的关系:
CREATE TABLE "Bar" (
"FooID" int8 NOT NULL REFERENCES "Foo" ("FooID"),
"Timerange" tstzrange NOT NULL,
"Detail1" VARCHAR,
"Detail2" VARCHAR,
CONSTRAINT "Bar_pkey" PRIMARY KEY ("FooID", "Timerange")
);
CREATE INDEX "Bar_FooID_Timerange_idx" ON "Bar" USING gist("FooID", "Timerange");
此外,表"Bar" 可能不包含相同"FooID" 或"ParentID" 的重叠"Timespan" 值。 我创建了一个触发器,它在任何INSERT、@987654338 之后触发@ 或 DELETE 可防止范围重叠。
触发器包含一个部分,看起来与此相似:
WITH
"cte" AS (
SELECT
"Foo"."FooID",
"Foo"."ParentID",
"Foo"."Details1",
"Bar"."Timespan"
FROM
"Foo"
JOIN "Bar" ON "Foo"."FooID" = "Bar"."FooID"
WHERE
"Foo"."FooID" = 1234
)
SELECT
"Foo"."FooID",
"Foo"."ParentID",
"Foo"."Details1",
"Bar"."Timespan"
FROM
"cte"
JOIN "Foo" ON
"cte"."ParentID" = "Foo"."ParentID"
AND "cte"."FooID" <> "Foo"."FooID"
JOIN "Bar" ON
"Foo"."FooID" = "Bar"."FooID"
AND "cte"."Timespan" && "Bar"."Timespan";
来自EXPLAIN ANALYSE的结果:
Nested Loop (cost=7258.08..15540.26 rows=1 width=130) (actual time=8.052..147.792 rows=1 loops=1)
Join Filter: ((cte."FooID" <> "Foo"."FooID") AND (cte."ParentID" = "Foo"."ParentID"))
Rows Removed by Join Filter: 76
CTE cte
-> Nested Loop (cost=0.68..7257.25 rows=1000 width=160) (actual time=1.727..1.735 rows=1 loops=1)
-> Function Scan on "fn_Bar" (cost=0.25..10.25 rows=1000 width=104) (actual time=1.699..1.701 rows=1 loops=1)
-> Index Scan using "Foo_pkey" on "Foo" "Foo_1" (cost=0.42..7.24 rows=1 width=64) (actual time=0.023..0.025 rows=1 loops=1)
Index Cond: ("FooID" = "fn_Bar"."FooID")
-> Nested Loop (cost=0.41..8256.00 rows=50 width=86) (actual time=1.828..147.188 rows=77 loops=1)
-> CTE Scan on cte (cost=0.00..20.00 rows=1000 width=108) (actual time=1.730..1.740 rows=1 loops=1)
**** -> Index Scan using "Bar_FooID_Timerange_idx" on "Bar" (cost=0.41..8.23 rows=1 width=74) (actual time=0.093..145.314 rows=77 loops=1)
Index Cond: ((cte."Timespan" && "Timespan"))
-> Index Scan using "Foo_pkey" on "Foo" (cost=0.42..0.53 rows=1 width=64) (actual time=0.004..0.005 rows=1 loops=77)
Index Cond: ("FooID" = "Bar"."FooID")
Planning time: 1.490 ms
Execution time: 147.869 ms
(****强调我的)
这似乎表明 99% 的工作都在从 "cte" 到 "Bar" 的 JOIN 中(通过 "Foo")......但它已经在使用适当的索引......还是太慢了。
所以我跑了:
SELECT
pg_size_pretty(pg_relation_size('"Bar"')) AS "Table",
pg_size_pretty(pg_relation_size('"Bar_FooID_Timerange_idx"')) AS "Index";
结果:
Table | Index
-------------|-------------
283 MB | 90 MB
这种大小的索引(相对于表)在读取性能方面是否提供了很多?我正在考虑一个 sudo 分区,其中索引被几个部分索引替换......也许这些部分将减少维护(和读取)并且性能会提高。我从未见过这样做,只是一个想法。如果这是一个选项,我想不出任何好的方法来限制细分,因为这将是 TSTZRANGE 值。
我还认为将"ParentID" 添加到"Bar" 会加快速度,但我不想反规范化。
我还有哪些选择?
Erwin Brandstetter 建议的更改的影响
在性能高峰期(18:00 时),该过程一直在添加每秒 14.5 条记录...高于每秒 1.15 条记录。
这是由于:
- 将
"ParentID"添加到表"Bar" - 将外键约束添加到
"Foo" ("ParentID", "FooID") - 添加
EXCLUDE USING gist ("ParentID" WITH =, "Timerange" WITH &&) DEFERRABLE INITIALLY DEFERRED(btree_gist 模块已安装)
【问题讨论】:
-
使用 CTE 有什么特别的原因吗?在 PostgreSQL 中,CTE 是一个优化器栅栏,可以防止一些优化。我会尝试没有。
-
@DavidAldridge - CTE 稍后在查询中是必需的,每个
EXPLAIN ANALYSE只表示相关部分;但是,就您而言,结果是相同的......瓶颈在从 CTE(或重写的子查询)到表"Bar"通过"Foo"的 JOIN 中。 -
由于所有这些假表和索引名称,很难理解解释计划中发生的事情。
-
"ParentID" int4 NOT NULL REFERENCES "Parents" ("ParentID"),此处显示了 parent_id 的支持索引:create index on "Foo" ("parentID"); -
没有。只有 FK 的“目标”需要至少有一个 UNIQUE 约束。 [但你仍然需要摆脱 cte,恕我直言]
标签: sql postgresql database-design postgresql-9.4 postgresql-performance