tl;dr Du skal tilføje et indeks på item_id
. Den "sorte magi" ved Postgres-indeksering er dækket i 11. Indekser
.
Du har et sammensat indeks på (topic_id, item_id)
og kolonnerækkefølgen er vigtig. Postgres kan bruge dette til at indeksere forespørgsler på topic_id
, forespørgsler på begge topic_id
og item_id
, men ikke (eller mindre effektivt) item_id
alene.
Fra 11.3. Indekser med flere kolonner ...
-- indexed
select *
from topics_items
where topic_id = ?
-- also indexed
select *
from topics_items
where topic_id = ?
and item_id = ?
-- probably not indexed
select *
from topics_items
where item_id = ?
Dette skyldes, at et sammensat indeks som (topic_id, item_id)
gemmer først emne-id'et, derefter en vare-id, som også har dette emne-id. For at kunne slå et vare-id op effektivt i dette indeks, skal Postgres først indsnævre søgningen med et emne-id.
Postgres kan vende et indeks, hvis det mener, det er besværet værd. Hvis der er et lille antal mulige emne-id'er og et stort antal mulige indeks-id'er, vil den søge efter indeks-id'et i hvert emne-id.
Lad os f.eks. sige, at du har 10 mulige emne-id'er og 1000 mulige element-id'er og dit indeks (topic_id, index_id)
. Det svarer til at have 10 tydeligt mærkede emne-id-bøtter hver med 1000 tydeligt mærkede emne-id-bøtter indeni. For at komme til vare-id-spandene, skal den kigge inde i hver emne-id-spand. For at bruge dette indeks på where item_id = 23
Postgres skal søge i hver af de 10 emne-id-bøtter for alle buckets med emne-ID 23.
Men hvis du har 1000 mulige emne-id'er og 10 mulige emne-id'er, ville Postgres skulle søge efter 1000 emne-id'er. Mest sandsynligt vil den lave en fuld tabelscanning i stedet for. I dette tilfælde vil du gerne vende dit indeks og gøre det til (item_id, topic_id)
.
Dette afhænger i høj grad af at have en god tabelstatistik, hvilket betyder at sikre, at autovakuum fungerer korrekt.
Så du kan slippe afsted med et enkelt indeks for to kolonner, hvis en kolonne har langt mindre variabilitet end en anden.
Postgres kan også bruge flere indekser, hvis det tror, det vil få forespørgslen til at køre hurtigere
. For eksempel, hvis du havde et indeks på topic_id
og et indeks på item_id
, det kan brug begge indekser og kombiner resultaterne. For eksempel where topic_id = 23 or item_id = 42
kunne bruge topic_id-indekset til at søge efter emne-id 23, og item_id-indekset til at søge efter element-ID 42, og derefter kombinere resultaterne.
Dette er generelt langsommere end at have en sammensat (topic_id, item_id)
indeks. Det kan også være langsommere end at bruge et enkelt indeks, så bliv ikke overrasket, hvis Postgres beslutter sig for ikke at bruge flere indekser.
Generelt for b-tree indekser, når du har to kolonner, har du tre mulige kombinationer.
- a + b
- a
- b
Og du har brug for to indekser.
- (a, b) -- a og a + b
- (b) -- b
(a, b)
dækker både søgninger efter a og a + b. (b)
dækker søgning efter b
.
Når du har tre kolonner, har du syv mulige kombinationer.
- a + b + c
- a + b
- a + c
- a
- b + c
- b
- c
Men du behøver kun tre indekser.
- (a, b, c) -- a, a + b, a + b + c
- (b, c) -- b, b + c
- (c, a) -- c, c + a
Dog vil du nok faktisk gerne undgå at have et indeks på tre kolonner. Det er ofte langsommere . Det, du faktisk ønsker, er dette.
- (a, b)
- (b, c)
- (c, a)
At læse fra et indeks er langsommere end at læse fra tabellen. Du ønsker, at dine indekser skal reducere antallet af rækker, der skal læses, men du ønsker ikke, at Postgres skal foretage mere indeksscanning end nødvendigt.