sql >> Database >  >> RDS >> PostgreSQL

Postgres gebruikt geen index wanneer indexscan een veel betere optie is

Index (Alleen) Scan --> Bitmap Index Scan --> Sequentiële Scan

Voor een paar rijen loont het om een ​​indexscan uit te voeren. Als er voldoende gegevenspagina's voor iedereen zichtbaar zijn (=genoeg gestofzuigd, en niet te veel gelijktijdige schrijfbelasting) en de index alle benodigde kolomwaarden kan leveren, wordt een snellere alleen-indexscan gebruikt. Met meer rijen die naar verwachting worden geretourneerd (hoger percentage van de tabel en afhankelijk van gegevensdistributie, waardefrequenties en rijbreedte), wordt het waarschijnlijker dat meerdere rijen op één gegevenspagina worden gevonden. Dan loont het om over te schakelen naar een bitmap-indexscans. (Of om meerdere afzonderlijke indexen te combineren.) Zodra een groot percentage gegevenspagina's toch moet worden bezocht, is het goedkoper om een ​​sequentiële scan uit te voeren, overtollige rijen te filteren en de overhead voor indexen helemaal over te slaan.

Indexgebruik wordt (veel) goedkoper en waarschijnlijker wanneer toegang tot datapagina's in willekeurige volgorde niet (veel) duurder is dan toegang in sequentiële volgorde. Dat is het geval bij het gebruik van SSD in plaats van draaiende schijven, of zelfs meer, hoe meer er in het RAM-geheugen wordt opgeslagen - en de respectievelijke configuratieparameters random_page_cost en effective_cache_size zijn dienovereenkomstig ingesteld.

In jouw geval schakelt Postgres over op een sequentiële scan, in de verwachting rows=263962 te vinden , dat is al 3% van de hele tafel. (Terwijl alleen rows=47935 daadwerkelijk worden gevonden, zie hieronder.)

Meer in dit gerelateerde antwoord:

  • Efficiënte PostgreSQL-query op tijdstempel met index- of bitmap-indexscan?

Pas op voor het forceren van queryplannen

U kunt een bepaalde planner-methode niet rechtstreeks in Postgres forceren, maar u kunt wel other . maken methoden lijken extreem duur voor foutopsporingsdoeleinden. Zie Configuratie plannermethode in de handleiding.

SET enable_seqscan = off (zoals gesuggereerd in een ander antwoord) doet dat met opeenvolgende scans. Maar dat is alleen bedoeld voor foutopsporingsdoeleinden in uw sessie. Doe niet gebruik dit als een algemene instelling in de productie, tenzij u precies weet wat u doet. Het kan belachelijke queryplannen afdwingen. De handleiding:

Deze configuratieparameters bieden een ruwe methode om de door de query-optimizer gekozen queryplannen te beïnvloeden. Als het standaardplan dat door de optimizer is gekozen voor een bepaalde zoekopdracht niet optimaal is, wordt eentijdelijke oplossing is om een ​​van deze configuratieparameters te gebruiken om de optimizer te dwingen een ander plan te kiezen. Betere manieren om de kwaliteit van de plannen die door de optimizer zijn gekozen te verbeteren, zijn onder meer het aanpassen van de kostenconstanten van de planner (zie paragraaf 19.7.2), het uitvoeren van ANALYZE handmatig, de waarde van dedefault_statistics_target . verhogen configuratieparameter, en het verhogen van de hoeveelheid verzamelde statistieken voor specifieke kolommen met behulp van ALTER TABLE SET STATISTICS .

Dat is al het meeste advies dat je nodig hebt.

  • Voorkom dat PostgreSQL soms een slecht queryplan kiest

In dit specifieke geval verwacht Postgres 5-6 keer meer hits op email_activities.email_recipient_id dan daadwerkelijk worden gevonden:

geschatte rows=227007 vs. actual ... rows=40789
geschatte rows=263962 vs. actual ... rows=47935

Als u deze zoekopdracht vaak uitvoert, loont het om ANALYZE . te hebben kijk naar een grotere steekproef voor nauwkeurigere statistieken over de specifieke kolom. Je tafel is groot (~ 10M rijen), dus zorg ervoor dat:

ALTER TABLE email_activities ALTER COLUMN email_recipient_id
SET STATISTICS 3000;  -- max 10000, default 100

Dan ANALYZE email_activities;

Maatregel als laatste redmiddel

In zeer zeldzaam gevallen kunt u een index forceren met SET LOCAL enable_seqscan = off in een aparte transactie of in een functie met een eigen omgeving. Vind ik leuk:

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;

De instelling is alleen van toepassing op het lokale bereik van de functie.

Waarschuwing: Dit is slechts een proof of concept. Zelfs deze veel minder radicale handmatige interventie kan je op de lange termijn bijten. Kardinaliteiten, waardefrequenties, je schema, globale Postgres-instellingen, alles verandert in de loop van de tijd. U gaat upgraden naar een nieuwe Postgres-versie. Het queryplan dat je nu forceert, kan later een heel slecht idee worden.

En meestal is dit slechts een tijdelijke oplossing voor een probleem met uw installatie. Beter zoeken en repareren.

Alternatieve zoekopdracht

Essentiële informatie ontbreekt in de vraag, maar deze equivalente zoekopdracht is waarschijnlijk sneller en gebruikt eerder een index op (email_recipient_id ) - steeds meer voor een grotere 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);


  1. Replicatieproblemen van niet-GTID naar GTID verwerken MariaDB-databaseclusters

  2. 12c IDENTITEIT kolommen

  3. Hoe waarden van XML-knooppunten opvragen?

  4. PDO::PARAM voor type decimaal?