Em um aplicativo chamado 'Links', os usuários postam links de conteúdo interessante que descobriram recentemente (e outros votam nas referidas postagens).
Esses links postados são salvos em uma links_link
tabela no meu banco de dados postgresql 9.6.5.
SELECT
Uma consulta de aparência inócua na links_link
tabela está aparecendo consistentemente no slow_log
. Está demorando mais de 500 ms e é ~ 10 vezes mais lento do que estou experimentando na maioria das outras operações do postgresql.
Aqui está um exemplo do SQL correspondente do meu log lento:
LOG: duração: 8648,676 ms declaração:
SELECT "links_link"."id",
"links_link"."description",
"links_link"."submitted_on",
"links_link"."reply_count",
"links_link"."net_votes"
FROM "links_link"
WHERE "links_link"."submitter_id" = 811645
ORDER BY "links_link"."id" DESC
LIMIT 1
Veja os explain analyze
resultados: https://explain.depesz.com/s/Xp5v
A consulta acaba filtrando 14.331.127 linhas de acordo com isso!
O que eu tentei:
Minha intuição é que o Postgres baseia esse plano de consulta em estatísticas enganosas. Assim, eu corri VACUUM ANALYZE
na referida mesa. No entanto, isso não mudou nada.
Sendo uma espécie de DBA acidental, estou procurando uma orientação rápida de especialistas sobre o assunto. Desde já agradeço e peço desculpas pela pergunta noob (se for).
Editar:
Aqui está toda a saída para \d links_link
:
Table "public.links_link"
Column | Type | Modifiers
----------------------+--------------------------+---------------------------------------------------------
id | integer | not null default nextval('links_link_id_seq'::regclass)
description | text | not null
submitter_id | integer | not null
submitted_on | timestamp with time zone | not null
rank_score | double precision | not null
url | character varying(250) | not null
cagtegory | character varying(25) | not null
image_file | character varying(100) |
reply_count | integer | default 0
device | character varying(10) | default '1'::character varying
latest_reply_id | integer |
which_photostream_id | integer |
is_visible | boolean | default true
net_votes | integer | default 0
Indexes:
"links_link_pkey" PRIMARY KEY, btree (id)
"links_link_latest_reply_id_idx" btree (latest_reply_id)
"links_link_submitter_id" btree (submitter_id)
Foreign-key constraints:
"link_whichphotostreamid_fkey" FOREIGN KEY (which_photostream_id) REFERENCES links_photostream(id) ON UPDATE CASCADE ON DELETE CASCADE
"links_link_submitter_id_fkey" FOREIGN KEY (submitter_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED
"publicreplyposter_link_fkey" FOREIGN KEY (latest_reply_id) REFERENCES links_publicreply(id) ON UPDATE CASCADE ON DELETE CASCADE
Referenced by:
TABLE "links_publicreply" CONSTRAINT "links_publicreply_answer_to_id_fkey" FOREIGN KEY (answer_to_id) REFERENCES links_link(id) DEFERRABLE INITIALLY DEFERRED
TABLE "links_report" CONSTRAINT "links_report_which_link_id_fkey" FOREIGN KEY (which_link_id) REFERENCES links_link(id) DEFERRABLE INITIALLY DEFERRED
TABLE "links_vote" CONSTRAINT "links_vote_link_id_fkey" FOREIGN KEY (link_id) REFERENCES links_link(id) DEFERRABLE INITIALLY DEFERRED
TABLE "links_photoobjectsubscription" CONSTRAINT "which_link_id_photoobjectsubscription" FOREIGN KEY (which_link_id) REFERENCES links_link(id) ON DELETE CASCADE
Eu poderia imaginar um índice sobre
links_link (submitter_id, id DESC)
ser de grande ajuda aqui.Ele pode ser percorrido para encontrar rapidamente a subárvore para
submitter_id = 811645
. Então o primeiro nó pode ser escolhido instantaneamente para obter o primeiro registro ordenado porid DESC
.