我们的一个PostgreSQL查询开始变慢(~15秒),因此我们考虑迁移到Graph数据库。早期测试显示速度明显更快,所以很棒。
这就是问题 - 我们仍然需要在Postgres中存储数据备份,以满足非分析需求。 Graph数据库仅用于分析,我们希望它仍然是辅助数据存储。由于我们的业务逻辑在此迁移期间发生了相当大的变化,因此两个现有表变为4 - 而Postgres中当前的“备份”选择需要1到6分钟才能运行。
我已经尝试了几种方法来优化它,最好的似乎是把它变成两个查询。如果有人可以在这里提出明显的错误,我很乐意听到一个建议。我已经尝试在查询规划器中切换左/右/内连接,但差别不大。连接顺序确实会影响差异;我想我只是没弄错。
我会详细介绍。
目标:检索发送给特定人员的最后10个附件
数据库结构:
CREATE TABLE message (
id SERIAL PRIMARY KEY NOT NULL ,
body_raw TEXT
);
CREATE TABLE attachments (
id SERIAL PRIMARY KEY NOT NULL ,
body_raw TEXT
);
CREATE TABLE message_2_attachments (
message_id INT NOT NULL REFERENCES message(id) ,
attachment_id INT NOT NULL REFERENCES attachments(id)
);
CREATE TABLE mailings (
id SERIAL PRIMARY KEY NOT NULL ,
event_timestamp TIMESTAMP not null ,
recipient_id INT NOT NULL ,
message_id INT NOT NULL REFERENCES message(id)
);
旁注:从邮件中抽象邮件的原因是邮件通常有多个收件人/ /单个邮件可以发送给多个收件人
此查询在相对较小的数据集上大约需要5分钟(查询计划程序时间是每个项目上方的注释):
-- 159374.75
EXPLAIN ANALYZE SELECT attachments.*
FROM attachments
JOIN message_2_attachments ON attachments.id = message_2_attachments.attachment_id
JOIN message ON message_2_attachments.message_id = message.id
JOIN mailings ON mailings.message_id = message.id
WHERE mailings.recipient_id = 1
ORDER BY mailings.event_timestamp desc limit 10 ;
将其拆分为2个查询只需要1/8的时间:
-- 19123.22
EXPLAIN ANALYZE SELECT message_2_attachments.attachment_id
FROM mailings
JOIN message ON mailings.message_id = message.id
JOIN message_2_attachments ON message.id = message_2_attachments.message_id
JOIN attachments ON message_2_attachments.attachment_id = attachments.id
WHERE mailings.recipient_id = 1
ORDER BY mailings.event_timestamp desc limit 10 ;
-- 1.089
EXPLAIN ANALYZE SELECT * FROM attachments WHERE id IN ( results of above query )
我已经尝试过多次重写查询 - 不同的连接顺序,不同类型的连接等等。我似乎无法在单个查询中将其设置为几乎同样有效,因为它可以在2。
UPDATED Github有更好的格式,所以解释的完整输出在这里 - https://gist.github.com/jvanasco/bc1dd38ca06e52c9a090
答案 0 :(得分:2)
在此处插入解释的输出:http://explain.depesz.com/s/hqPT
如您所见,:
Hash Join (cost=96588.85..158413.71 rows=44473 width=3201) (actual time=22590.630..30761.213 rows=44292 loops=1)
Hash Cond: (message_2_attachment.attachment_id = attachment.id)
花了很多时间。我尝试将索引添加到外键中:
CREATE INDEX idx_message_2_attachments_attachment_id ON "message_2_attachments" USING btree (attachment_id);
CREATE INDEX idx_message_2_attachments_message_id ON "message_2_attachments" USING btree (message_id);`
CREATE INDEX idx_mailings_message_id ON "mailings" USING btree (message_id);
答案 1 :(得分:0)
联结表缺少主键。此外,建议在此PK上添加反向索引:
CREATE TABLE message_2_attachments (
message_id INT NOT NULL REFERENCES message(id) ,
attachment_id INT NOT NULL REFERENCES attachments(id)
, PRIMARY KEY (message_id,attachment_id) -- <<== here
);
CREATE UNIQUE INDEX ON message_2_attachments(attachment_id,message_id); -- <<== here
对于邮件表,情况并不是那么清楚。 看起来像 {event_timestamp,recipient_id,message_id}的某种组合可以充当候选键。 id
字段仅作为代理。