Index (endast) Scan --> Bitmap Index Scan --> Sekventiell skanning
För några rader lönar det sig att köra en indexskanning. Om tillräckligt många datasidor är synliga för alla (=tillräckligt vakuum och inte för mycket samtidig skrivbelastning) och indexet kan tillhandahålla alla kolumnvärden som behövs, används en snabbare endast indexskanning. Med fler rader som förväntas returneras (högre procentandel av tabellen och beroende på datadistribution, värdefrekvenser och radbredd) blir det mer sannolikt att hitta flera rader på en datasida. Då lönar det sig att byta till en bitmappsindexskanning. (Eller för att kombinera flera distinkta index.) När en stor andel av datasidorna ändå måste besökas, är det billigare att köra en sekventiell skanning, filtrera överskottsrader och hoppa över omkostnader för index helt och hållet.
Indexanvändning blir (mycket) billigare och mer sannolikt när det inte är (mycket) dyrare att komma åt datasidor i slumpmässig ordning än att komma åt dem i sekventiell ordning. Det är fallet när du använder SSD istället för att snurra diskar, eller ännu mer så desto mer cachelagras i RAM - och respektive konfigurationsparametrar random_page_cost
och effective_cache_size
är inställda därefter.
I ditt fall byter Postgres till en sekventiell skanning och förväntar sig att hitta rows=263962
, det är redan 3 % av hela tabellen. (Medan endast rows=47935
faktiskt hittas, se nedan.)
Mer i detta relaterade svar:
- Effektiv PostgreSQL-fråga på tidsstämpel med index- eller bitmappsindexskanning?
Se upp för att tvinga fram frågeplaner
Du kan inte tvinga fram en viss planeringsmetod direkt i Postgres, men du kan göra annan metoder verkar extremt dyra för felsökningsändamål. Se Planer Method Configuration i manualen.
SET enable_seqscan = off
(som föreslås i ett annat svar) gör det med sekventiella skanningar. Men det är endast avsett för felsökningsändamål i din session. Gör inte använd detta som en allmän inställning i produktionen om du inte vet exakt vad du gör. Det kan tvinga fram löjliga frågeplaner. Manualen:
Dessa konfigurationsparametrar ger en grov metod för att påverka frågeplanerna som valts av frågeoptimeraren. Om standardplanen som valts av optimeraren för en viss fråga inte är optimal, entillfällig lösningen är att använda en av dessa konfigurationsparametrar för att tvinga optimeraren att välja en annan plan. Bättre sätt att förbättra kvaliteten på planerna som väljs av optimeraren inkluderar att justera planerarens kostnadskonstanter (se avsnitt 19.7.2), att köra
ANALYZE
manuellt, öka värdet pådefault_statistics_target
konfigurationsparameter, och öka mängden statistik som samlas in för specifika kolumner medALTER TABLE SET STATISTICS
.
Det är redan de flesta råd du behöver.
- Få PostgreSQL från att ibland välja en dålig frågeplan
I det här specifika fallet förväntar sig Postgres 5-6 gånger fler träffar på email_activities.email_recipient_id
än som faktiskt hittas:
uppskattad
rows=227007
kontraactual ... rows=40789
uppskattadrows=263962
kontraactual ... rows=47935
Om du kör den här frågan ofta kommer det att löna sig att ha ANALYZE
titta på ett större urval för mer exakt statistik på den specifika kolumnen. Ditt bord är stort (~ 10 miljoner rader), så gör det:
ALTER TABLE email_activities ALTER COLUMN email_recipient_id
SET STATISTICS 3000; -- max 10000, default 100
Sedan ANALYZE email_activities;
Åtgärd för sista utväg
I mycket sällsynt fall kan du tvinga fram ett index med SET LOCAL enable_seqscan = off
i en separat transaktion eller i en funktion med egen miljö. Gilla:
CREATE OR REPLACE FUNCTION f_count_dist_recipients(_email_campaign_id int, _limit int)
RETURNS bigint AS
$func$
SELECT COUNT(DISTINCT a.email_recipient_id)
FROM email_activities a
WHERE a.email_recipient_id IN (
SELECT id
FROM email_recipients
WHERE email_campaign_id = $1
LIMIT $2) -- or consider query below
$func$ LANGUAGE sql VOLATILE COST 100000 SET enable_seqscan = off;
Inställningen gäller endast för funktionens lokala omfattning.
Varning: Detta är bara ett bevis på konceptet. Även detta mycket mindre radikala manuella ingrepp kan bita dig i det långa loppet. Kardinaliteter, värdefrekvenser, ditt schema, globala Postgres-inställningar, allt förändras över tiden. Du kommer att uppgradera till en ny Postgres-version. Frågeplanen du tvingar fram nu kan bli en mycket dålig idé senare.
Och vanligtvis är detta bara en lösning för ett problem med din installation. Bättre att hitta och fixa det.
Alternativ fråga
Viktig information saknas i frågan, men denna likvärdiga fråga är förmodligen snabbare och mer sannolikt att använda ett index på (email_recipient_id
) - alltmer för en större LIMIT
.
SELECT COUNT(*) AS ct
FROM (
SELECT id
FROM email_recipients
WHERE email_campaign_id = 1607
LIMIT 43000
) r
WHERE EXISTS (
SELECT FROM email_activities
WHERE email_recipient_id = r.id);