I have an app called 'Links' where 1) users congregate in groups and add others, 2) post content for each other in the said groups. Groups are defined by a links_group
table in my postgresql 9.6.5 DB, whereas the replies they post in these are defined by a links_reply
table. Overall the DB's performance is great.
However one SELECT
query on the links_reply
table is consistently showing up in slow_log. It's taking longer than 500ms, and is ~10X slower than what I'm experiencing in most other postgresql operations.
I used the Django ORM to generate the query. Here's the ORM call: replies = Reply.objects.select_related('writer__userprofile').filter(which_group=group).order_by('-submitted_on')[:25]
. Essentially, this is selecting the latest 25 replies for a given group object. It's also selecting associated user
and userprofile
objects as well.
Here's an example of the corresponding SQL from my slow log: LOG: duration: 8476.309 ms statement:
SELECT
"links_reply"."id", "links_reply"."text",
"links_reply"."which_group_id", "links_reply"."writer_id",
"links_reply"."submitted_on", "links_reply"."image",
"links_reply"."device", "links_reply"."category",
"auth_user"."id", "auth_user"."username",
"links_userprofile"."id", "links_userprofile"."user_id",
"links_userprofile"."score", "links_userprofile"."avatar"
FROM
"links_reply"
INNER JOIN "auth_user"
ON ("links_reply"."writer_id" = "auth_user"."id")
LEFT OUTER JOIN "links_userprofile"
ON ("auth_user"."id" = "links_userprofile"."user_id")
WHERE "links_reply"."which_group_id" = 124479
ORDER BY "links_reply"."submitted_on" DESC
LIMIT 25
Look at the the explain analyze results here: https://explain.depesz.com/s/G4X The index scan (backward) seems to be eating up all the time.
Here's the output of \d links_reply
:
Table "public.links_reply"
Column | Type | Modifiers
----------------+--------------------------+----------------------------------------------------------
id | integer | not null default nextval('links_reply_id_seq'::regclass)
text | text | not null
which_group_id | integer | not null
writer_id | integer | not null
submitted_on | timestamp with time zone | not null
image | character varying(100) |
category | character varying(15) | not null
device | character varying(10) | default '1'::character varying
Indexes:
"links_reply_pkey" PRIMARY KEY, btree (id)
"category_index" btree (category)
"links_reply_submitted_on" btree (submitted_on)
"links_reply_which_group_id" btree (which_group_id)
"links_reply_writer_id" btree (writer_id)
"text_index" btree (text)
Foreign-key constraints:
"links_reply_which_group_id_fkey" FOREIGN KEY (which_group_id) REFERENCES links_group(id) DEFERRABLE INITIALLY DEFERRED
"links_reply_writer_id_fkey" FOREIGN KEY (writer_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
Referenced by:
TABLE "links_groupseen" CONSTRAINT "links_groupseen_which_reply_id_fkey" FOREIGN KEY (which_reply_id) REFERENCES links_reply(id) DEFERRABLE INITIALLY DEFERRED
TABLE "links_report" CONSTRAINT "links_report_which_reply_id_fkey" FOREIGN KEY (which_reply_id) REFERENCES links_reply(id) DEFERRABLE INITIALLY DEFERRED
It's a big table (~25M rows). The hardware it's operating on has 16 cores and 60 GB memory. It shares this machine with a python app. But I've been monitoring the server's performance and I don't see bottlenecks there.
Is there any way I can improve this query's performance? Please advise on all options (if any) I have here.
Note that this query performed exceptionally well till last week. What's changed since then? I carried out a pg_dump
and then pg_restore
of the DB (on a separate VM), and I upgraded from Postgresql 9.3.10 to 9.6.5. I also used a connection pooler called pgbouncer
before, which I haven't configured on the new VM I've migrated to yet. That's it.
Lastly, I've also noticed (from user experience) that for all group objects created till last week, the query still performs fast. But all new objects being created now are producing slow log. Could this be some kind of an indexing issue, specifically with the links_reply_submitted_on
index?
Update: The prescribed optimizations really turned things around. Have a look:
ORDER BY
is a major change.id
does NOT change the situation in terms of performance at all. I made this change yesterday, it's precisely as bad (if not worse). I can revert it in the question though, if you want me to. Should I? Also removed extraneous fields (and made that actual change my my query as well - ofcourse, to no avail).