Mehrspaltiger Index und Leistung

31

Ich habe eine Tabelle mit einem mehrspaltigen Index, und ich habe Zweifel an der richtigen Sortierung der Indizes, um die maximale Leistung bei den Abfragen zu erzielen.

Das Szenario:

  • PostgreSQL 8.4, Tabelle mit ungefähr einer Million Zeilen

  • Werte in Spalte c1 können ungefähr 100 verschiedene Werte haben . Wir können davon ausgehen, dass die Werte gleichmäßig verteilt sind, sodass wir für jeden möglichen Wert ungefähr 10000 Zeilen haben.

  • Spalte c2 kann 1000 verschiedene Werte haben . Wir haben 1000 Zeilen für jeden möglichen Wert.

Bei der Suche nach Daten enthält die Bedingung immer Werte für diese beiden Spalten, sodass die Tabelle einen mehrspaltigen Index enthält, der c1 und c2 kombiniert. Ich habe darüber gelesen, wie wichtig es ist , die Spalten in einem mehrspaltigen Index richtig anzuordnen, wenn Sie bei Abfragen nur eine Spalte zum Filtern verwenden. Dies ist in unserem Szenario nicht der Fall.

Meine Frage ist diese:

Könnte ich angesichts der Tatsache, dass einer der Filter einen viel kleineren Datensatz auswählt, die Leistung verbessern, wenn der erste Index der selektivste ist (derjenige, der einen kleineren Datensatz zulässt)? Ich hatte diese Frage erst in Betracht gezogen, als ich die Grafiken aus dem Artikel sah, auf den verwiesen wurde:

Bildbeschreibung hier eingeben

Bild aus dem referenzierten Artikel über mehrspaltige Indizes .

Die Abfragen verwenden Werte aus den beiden Spalten zum Filtern. Ich habe keine Fragen mit nur einer Spalte zum Filtern. Alle von ihnen sind: WHERE c1=@ParameterA AND c2=@ParameterB. Es gibt auch Bedingungen wie diese:WHERE c1 = "abc" AND c2 LIKE "ab%"

jap1968
quelle

Antworten:

36

Antworten

Da Sie auf die Website verweisen use-the-index-luke.com, beachten Sie das Kapitel:

Verwenden Sie den Index, Luke ›Die Where-Klausel› Suche nach Bereichen › Größer, Kleiner und ZWISCHEN

Es gibt ein Beispiel, das perfekt zu Ihrer Situation passt (zweispaltiger Index, einer wird auf Gleichheit geprüft , der andere auf Reichweite ). Es erklärt (mit mehr dieser netten Indexgrafiken ), warum die Ratschläge von @ ypercube richtig sind und fasst es zusammen:

Rule of thumb: index for equality first  then for ranges.

Auch gut für nur eine Spalte?

Was für Abfragen in nur einer Spalte zu tun ist, scheint klar zu sein. Weitere Details und Benchmarks dazu unter dieser verwandten Frage:

Weniger selektive Spalte zuerst?

Abgesehen davon, was ist, wenn Sie nur Gleichheitsbedingungen für beide Spalten haben ?

Es spielt keine Rolle . Stellen Sie die Spalte an die erste Stelle, bei der die Wahrscheinlichkeit größer ist, dass sie ihre eigenen Bedingungen erhält, worauf es ankommt.

Betrachten Sie diese Demo oder reproduzieren Sie sie selbst. Ich erstelle eine einfache Tabelle aus zwei Spalten mit 100.000 Zeilen. Eine mit sehr wenigen , die andere mit vielen unterschiedlichen Werten:

CREATE TEMP TABLE t AS
SELECT (random() * 10000)::int AS lots
     , (random() * 4)::int     AS few
FROM generate_series (1, 100000);

DELETE FROM t WHERE random() > 0.9;  -- create some dead tuples, more "real-life"

ANALYZE t;

SELECT count(distinct lots)   -- 9999
     , count(distinct few)    --    5
FROM   t;

Abfrage:

SELECT *
FROM   t
WHERE  lots = 2345
AND    few = 2;

EXPLAIN ANALYZE Ausgabe (Best of 10, um Caching-Effekte auszuschließen):

Seq Scan on t (Kosten = 0.00..5840.84 Zeilen = 2 Breite = 8)
               (tatsächliche Zeit = 5.646..15.535 Zeilen = 2 Schleifen = 1)
  Filter: ((Lose = 2345) UND (wenige = 2))
  Puffer: Lokaler Treffer = 443
Gesamtlaufzeit: 15.557 ms

Index hinzufügen, erneut testen:

CREATE INDEX t_lf_idx ON t(lots, few);
Index Scan mit t_lf_idx auf t (Kosten = 0.00..3.76 Zeilen = 2 Breite = 8)
                                (tatsächliche Zeit = 0,008..0.011 Zeilen = 2 Schleifen = 1)
  Indexbedingung: ((Lose = 2345) UND (wenige = 2))
  Puffer: Lokaler Treffer = 4
Gesamtlaufzeit: 0.027 ms

Anderen Index hinzufügen, erneut testen:

DROP INDEX t_lf_idx;
CREATE INDEX t_fl_idx  ON t(few, lots);
Index Scan mit t_fl_idx auf t (Kosten = 0.00..3.74 Zeilen = 2 Breite = 8)
                                (tatsächliche Zeit = 0.007..0.011 Zeilen = 2 Schleifen = 1)
  Indexbedingung: ((wenige = 2) UND (Lose = 2345))
  Puffer: Lokaler Treffer = 4
Gesamtlaufzeit: 0.027 ms
Erwin Brandstetter
quelle
Gilt das auch für 3 (oder mehr) Spalten im Index?
Hayd
@hayd: Ich bin mir nicht sicher, worauf sich "das" bezieht. Sie könnten eine neue Frage stellen . Sie können immer auf diesen für den Kontext verweisen. (Und lassen Sie einen Kommentar hier, um zurück zu verlinken.)
Erwin Brandstetter
Mit "dies" meine ich "ist die Reihenfolge der
Indexdefinition
@hayd: Wichtigster Punkt: Ein Btree-Index eignet sich für Abfragen mit Gleichheitsbedingungen für führende Indexausdrücke . Ordnung unter diesen ist meistens irrelevant. Viele andere Details, die nicht in einen Kommentar passen ...
Erwin Brandstetter
Danke, ich werde versuchen, eine zusammenhängende Frage zu schreiben und darauf zu verlinken.
Hayd
11

Wenn, wie Sie sagen, die Abfragen, die diese beiden Spalten betreffen, alle Gleichheitsprüfungen beider Spalten sind, z.

WHERE c1=@ParameterA AND c2=@ParameterB

kümmere dich nicht darum. Ich bezweifle, dass es einen Unterschied geben wird, und wenn es einen gibt, wird er vernachlässigbar sein. Sie können natürlich jederzeit mit Ihren Daten und Ihren Servereinstellungen testen. Unterschiedliche Versionen eines DBMS können sich hinsichtlich der Optimierung leicht unterschiedlich verhalten.

Die Reihenfolge innerhalb des Indexes ist für andere Arten von Abfragen von Bedeutung, bei denen nur eine Spalte überprüft wird oder Ungleichheitsbedingungen oder Bedingungen für eine Spalte und die Gruppierung in der anderen usw.

Wenn ich einen der beiden Befehle wählen würde, würde ich die weniger selektive Spalte an die erste Stelle setzen. Betrachten Sie eine Tabelle mit Spalten yearund month. Es ist wahrscheinlicher, dass Sie eine WHERE year = 2000Bedingung oder eine WHERE year BETWEEN 2000 AND 2013oder eine benötigen WHERE (year, month) BETWEEN (1999, 6) AND (2000, 5).

Eine Abfrage des Typs WHERE month = 7 GROUP BY yearkann sicher gewünscht sein (Find people born on July), wäre aber seltener. Das hängt natürlich von den tatsächlich in Ihrer Tabelle gespeicherten Daten ab. Wählen Sie vorerst eine Bestellung aus, sagen Sie die (c1, c2)und Sie können später jederzeit einen weiteren Index hinzufügen (c2, c1).


Update nach dem Kommentar des OP:

Es gibt auch Bedingungen wie diese: WHERE c1 = 'abc' AND c2 LIKE 'ab%'

Bei dieser Art der Abfrage handelt es sich genau um eine Bereichsbedingung für die c2Spalte, für die ein (c1, c2)Index erforderlich wäre . Wenn Sie auch Anfragen vom umgekehrten Typ haben:

WHERE c2 = 'abc' AND c1 LIKE 'ab%'

dann wäre es gut, wenn du auch einen (c2, c1)index hättest .

ypercubeᵀᴹ
quelle