tl;dr Du måste lägga till ett index på item_id
. Den "svarta magin" med Postgres-indexering täcks av 11. Index
.
Du har ett sammansatt index på (topic_id, item_id)
och kolumnordningen är viktig. Postgres kan använda detta för att indexera frågor på topic_id
, frågor på båda topic_id
och item_id
, men inte (eller mindre effektivt) item_id
ensam.
Från 11.3. Index för flera kolumner ...
-- 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 = ?
Detta beror på att ett sammansatt index som (topic_id, item_id)
lagrar ämnes-ID först, sedan ett objekt-ID som också har det ämnes-ID. För att kunna slå upp ett artikel-ID effektivt i detta index måste Postgres först begränsa sökningen med ett ämnes-ID.
Postgres kan vända ett index om det tycker att det är värt ansträngningen. Om det finns ett litet antal möjliga ämnes-ID:n och ett stort antal möjliga index-ID:n kommer den att söka efter index-ID:n i varje ämnes-ID.
Låt oss till exempel säga att du har 10 möjliga ämnes-ID:n och 1000 möjliga artikel-ID:n och ditt index (topic_id, index_id)
. Det är som att ha 10 tydligt märkta ämnes-ID-hinkar var och en med 1000 tydligt märkta objekt-ID-hinkar inuti. För att komma till artikel-ID-blocken måste den titta in i varje ämnes-ID-hink. För att använda detta index på where item_id = 23
Postgres måste söka i var och en av de 10 ämnes-ID-segmenten efter alla hinkar med artikel-ID 23.
Men om du har 1000 möjliga ämnes-ID:n och 10 möjliga artikel-ID:n, måste Postgres söka efter 1000 ämnes-ID:n. Med största sannolikhet kommer den att göra en fullbordsskanning istället. I det här fallet skulle du vilja vända ditt index och göra det till (item_id, topic_id)
.
Detta beror mycket på att ha bra tabellstatistik, vilket innebär att se till att autovakuum fungerar korrekt.
Så du kan komma undan med ett enda index för två kolumner, om en kolumn har mycket mindre variation än en annan.
Postgres kan också använda flera index om det tror att det kommer att få frågan att köras snabbare
. Till exempel, om du hade ett index på topic_id
och ett index på item_id
, det kan använd båda indexen och kombinera resultaten. Till exempel where topic_id = 23 or item_id = 42
skulle kunna använda topic_id-indexet för att söka efter ämnes-ID 23 och item_id-indexet för att söka efter artikel-ID 42, och sedan kombinera resultaten.
Detta är i allmänhet långsammare än att ha en sammansatt (topic_id, item_id)
index. Det kan också vara långsammare än att använda ett enda index, så bli inte förvånad om Postgres bestämmer sig för att inte använda flera index.
I allmänhet, för b-tree-index, när du har två kolumner har du tre möjliga kombinationer.
- a + b
- a
- b
Och du behöver två index.
- (a, b) -- a och a + b
- (b) -- b
(a, b)
täcker både sökningar efter a och a + b. (b)
omfattar sökning efter b
.
När du har tre kolumner har du sju möjliga kombinationer.
- a + b + c
- a + b
- a + c
- a
- b + c
- b
- c
Men du behöver bara tre index.
- (a, b, c) -- a, a + b, a + b + c
- (b, c) -- b, b + c
- (c, a) -- c, c + a
Däremot vill du nog faktiskt undvika att ha ett index på tre kolumner. Det är ofta långsammare . Vad du egentligen vill är detta.
- (a, b)
- (b, c)
- (c, a)
Att läsa från ett index är långsammare än att läsa från tabellen. Du vill att dina index ska minska antalet rader som måste läsas, men du vill inte att Postgres ska behöva göra mer indexskanning än nödvändigt.