Index (pouze) Scan --> Bitmap Index Scan --> Sekvenční skenování
Pro několik řádků se vyplatí spustit indexový sken. Pokud je pro všechny viditelný dostatek datových stránek (=dostatečně vysáté a ne příliš mnoho souběžného zápisu) a index může poskytnout všechny potřebné hodnoty sloupců, pak se použije rychlejší skenování pouze indexu. Očekává-li se, že bude vráceno více řádků (vyšší procento tabulky a v závislosti na distribuci dat, četnosti hodnot a šířce řádku), je pravděpodobnější, že na jedné datové stránce najdete několik řádků. Pak se vyplatí přejít na skenování indexu bitmapy. (Nebo zkombinovat více různých indexů.) Jakmile je nutné velké procento datových stránek stejně navštívit, je levnější spustit sekvenční skenování, filtrovat přebytečné řádky a úplně přeskočit režii indexů.
Používání indexu je (mnohem) levnější a pravděpodobnější, když přístup k datovým stránkám v náhodném pořadí není (mnohem) dražší než přístup k nim v sekvenčním pořadí. To je případ, kdy místo rotujících disků používáte SSD, nebo ještě více, čím více je uloženo v paměti RAM – a příslušné konfigurační parametry random_page_cost
a effective_cache_size
jsou nastaveny podle toho.
Ve vašem případě se Postgres přepne na sekvenční skenování a očekává, že najde rows=263962
, to jsou již 3 % z celé tabulky. (Zatímco pouze rows=47935
jsou skutečně nalezeny, viz níže.)
Více v této související odpovědi:
- Efektivní PostgreSQL dotaz na časové razítko pomocí indexového nebo bitmapového indexového skenování?
Dejte si pozor na vynucování plánů dotazů
Určitou metodu plánovače nemůžete vynutit přímo v Postgresu, ale můžete vytvořit jinou metody se zdají extrémně drahé pro účely ladění. Viz Konfigurace metody plánovače v příručce.
SET enable_seqscan = off
(jak je navrženo v jiné odpovědi) to dělá sekvenčnímu skenování. Ale to je určeno pouze pro účely ladění ve vaší relaci. nedělejte to použijte toto jako obecné nastavení ve výrobě, pokud přesně nevíte, co děláte. Může si vynutit směšné plány dotazů. Manuál:
Tyto konfigurační parametry poskytují hrubou metodu ovlivňování plánů dotazů zvolených optimalizátorem dotazů. Pokud výchozí plán zvolený optimalizátorem pro konkrétní dotaz není optimální, dočasné řešením je použít jeden z těchto konfiguračních parametrů k přinucení optimalizátoru zvolit jiný plán. Lepší způsoby, jak zlepšit kvalitu plánů zvolených optimalizátorem, zahrnují úpravu nákladových konstant plánovače (viz oddíl 19.7.2), spuštění
ANALYZE
ručně, zvýšením hodnotydefault_statistics_target
konfigurační parametr a zvýšení množství statistik shromážděných pro konkrétní sloupce pomocíALTER TABLE SET STATISTICS
.
To už je většina rad, které potřebujete.
- Zabraňte PostgreSQL, aby někdy zvolil špatný plán dotazů
V tomto konkrétním případě Postgres očekává 5-6krát více přístupů na email_activities.email_recipient_id
než jsou ve skutečnosti nalezeny:
odhadovaný
rows=227007
vs.actual ... rows=40789
odhadovanýrows=263962
vs.actual ... rows=47935
Pokud budete tento dotaz spouštět často, vyplatí se mít ANALYZE
podívejte se na větší vzorek pro přesnější statistiky pro konkrétní sloupec. Váš stůl je velký (~ 10 milionů řádků), takže:
ALTER TABLE email_activities ALTER COLUMN email_recipient_id
SET STATISTICS 3000; -- max 10000, default 100
Poté ANALYZE email_activities;
Opatření poslední instance
velmi vzácné v případech se můžete uchýlit k vynucení indexu pomocí SET LOCAL enable_seqscan = off
v samostatné transakci nebo ve funkci s vlastním prostředím. Jako:
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;
Nastavení se vztahuje pouze na místní rozsah funkce.
Upozornění: Toto je jen důkaz konceptu. I tento mnohem méně radikální manuální zásah vás může z dlouhodobého hlediska kousnout. Mohutnosti, hodnotové frekvence, vaše schéma, globální nastavení Postgresu, vše se v čase mění. Chystáte se upgradovat na novou verzi Postgres. Plán dotazů, který si vynutíte nyní, se později může stát velmi špatným nápadem.
A obvykle se jedná pouze o řešení problému s nastavením. Je lepší to najít a opravit.
Alternativní dotaz
V otázce chybí základní informace, ale tento ekvivalentní dotaz je pravděpodobně rychlejší a pravděpodobněji použije index na (email_recipient_id
) – čím dál tím více pro větší 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);