我有一个 SQL 查询,我试图通过添加索引或更改查询本身来加快速度。
但是,执行计划说我的 HashAggregate 和 Sort 操作很慢。
这是可视化的执行计划:https://explain.dalibo.com/plan/09c7d4a2dgcc29ec
这是我的桌子 DDL:
CREATE TABLE notification
(
id bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
body jsonb NOT NULL,
"type" text NOT NULL,
userid int NOT NULL,
isread boolean NOT NULL DEFAULT FALSE,
platform text NOT NULL,
"state" text NOT NULL,
createdat timestamp WITH TIME ZONE NOT NULL DEFAULT NOW(),
updatedat timestamp WITH TIME ZONE NOT NULL DEFAULT NOW(),
CONSTRAINT notification_pkey PRIMARY KEY (id, platform),
CONSTRAINT fk_notification_userid FOREIGN KEY (userid) REFERENCES "user" (id)
) PARTITION BY LIST (platform);
CREATE TABLE notification_a PARTITION OF notification FOR VALUES IN ('a');
CREATE TABLE notification_b PARTITION OF notification FOR VALUES IN ('b');
CREATE TABLE notification_default PARTITION OF notification DEFAULT;
这是我的 sql 查询:
--EXPLAIN (ANALYZE, COSTS, BUFFERS, VERBOSE, FORMAT TEXT)
WITH grouped AS (
SELECT CASE
WHEN type = 'newContent' AND COUNT(*) FILTER (WHERE type = 'newContent') OVER () >= 3
THEN
JSONB_AGG(
JSONB_BUILD_OBJECT(
'id', id,
'body', body,
'createdAt', createdat,
'type', type
)
) FILTER (WHERE type = 'newContent') OVER ()
ELSE
JSONB_BUILD_OBJECT(
'id', id,
'body', body,
'createdAt', createdat,
'type', type
)
END "notification"
FROM notification_a
WHERE userid = 23053
AND NOT isread
AND state = 'sent'
ORDER BY createdat DESC
)
SELECT CASE
WHEN JSONB_TYPEOF(notification) = 'array' AND notification #>> '{0,type}' = 'newContent' THEN
JSONB_BUILD_OBJECT(
'body', JSONB_BUILD_OBJECT(
'contentCount', JSONB_ARRAY_LENGTH(notification),
'department', notification #>> '{0,body,department}'
),
'createdAt', NOW(),
'type', 'newContentCompact',
'group', notification
)
ELSE notification
END,
COUNT(*) OVER () total_count
FROM grouped
GROUP BY notification
ORDER BY (notification ->> 'createdAt')::timestamptz DESC
OFFSET 0 LIMIT 100;
首先,我按
platform
键对表进行分区,因为平台a
的通知数量比平台b
多得多。
然后,我尝试了不同的索引配置,并停在了下面给出最佳性能增益的配置上:
CREATE INDEX IF NOT EXISTS notification_search_index ON notification (userid, isread, state, createdat DESC) WHERE (state = 'sent' and not isread);
然而,尽管在索引中有它,但我无法摆脱对
notification_a.createdat
键的排序操作。
该表目前约有 1000 万行,未来将有数亿行。
我意识到可能是查询本身不是很“可优化”,但我找不到更好的方法来实现我需要的:
platform
type
有超过2个通知(并且如果这种类型是newContent
),它们应该在最终结果中组合在一起(因此使用JSONB_AGG
)。该组唯一真正需要的值是type
,body ->> 'department'
和组的大小。例如,如果我有行
id: 1, type: 'a'
id: 2, type: 'a'
id: 3, type: 'a'
id: 4, type: 'b'
id: 5, type: 'c'
id: 6, type: 'd'
如果做一个
SELECT ... OFFSET 0 LIMIT 3
我应该收到3行:
count: 3, type: 'a'
id: 4, type: 'b'
id: 5, type: 'c'
你能帮我优化我的查询,并可能找到一种更好(性能更高)的方法来实现我的需要吗?结果
jsonb
并不那么重要,我可以在应用程序级别构建 json,这是我发现让它进行分组的唯一方法。
运行
ALTER ROLE current_user SET work_mem TO '128MB';
给~x2加速。