Ich habe eine PostgreSQL 9.3-Tabelle mit einigen Zahlen und einigen zusätzlichen Daten:
CREATE TABLE mytable (
myid BIGINT,
somedata BYTEA
)
Diese Tabelle enthält derzeit ca. 10 Millionen Datensätze und benötigt 1 GB Speicherplatz. myid
sind nicht aufeinanderfolgend.
Ich möchte berechnen, wie viele Zeilen sich in jedem Block mit 100000 aufeinander folgenden Zahlen befinden:
SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
Dies gibt ungefähr 3500 Zeilen zurück.
Ich habe festgestellt, dass das Vorhandensein eines bestimmten Index diese Abfrage erheblich beschleunigt, obwohl der Abfrageplan dies überhaupt nicht erwähnt. Der Abfrageplan ohne Index:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Sort (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
Output: ((myid / 100000))
Sort Key: ((mytable.myid / 100000))
Sort Method: external merge Disk: 157440kB
-> Seq Scan on public.mytable (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 8914.780 ms
(9 rows)
Der Index:
db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;
Der neue Abfrageplan:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Seq Scan on public.mytable (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 3190.975 ms
(5 rows)
Die Abfragepläne und die Laufzeiten unterscheiden sich also erheblich (fast dreimal), erwähnen jedoch weder den Index. Dieses Verhalten ist auf meinem Entwicklungscomputer perfekt reproduzierbar: Ich habe mehrere Zyklen durchlaufen, in denen der Index gelöscht, die Abfrage mehrmals getestet, der Index neu erstellt und die Abfrage erneut mehrmals getestet wurde. Was passiert hier?
HashAggregate
Methode verwendet (und es ist keine Sortierung erforderlich), damit Sie eine bessere Leistung erzielen. Warum der Index im Plan nicht erwähnt wird, weiß ich nicht.explain (analyze true, verbose true) ...
?Antworten:
VACUUM ANALYZE
macht den Unterschied in Ihrem Beispiel. Außerdem, wie von @jjanes angegeben , die zusätzlichen Statistiken für den Funktionsindex. Pro Dokumentation:Das Erstellen des Index führt jedoch nicht dazu, dass Postgres Statistiken sammelt. Versuchen:
Gibt nichts zurück, bis Sie Ihren ersten ausführen
ANALYZE
(oderVACUUM ANALYZE
oder der Autovacuum-Daemon startet).Jetzt sehen Sie hinzugefügte Statistiken.
Da die gesamte Tabelle ohnehin gelesen werden muss, wird Postgres einen sequentiellen Scan verwenden, es sei denn, es wird erwartet, dass die Berechnung für
myid/100000
den Wechsel teuer genug ist, was nicht der Fall ist.Ihre einzige andere Chance wäre ein Nur-Index-Scan, wenn der Index viel kleiner als die Tabelle ist - und die Voraussetzungen für einen Nur-Index-Scan erfüllt sind. Details im Postgres Wiki und im Handbuch .
Solange dieser Funktionsindex nicht verwendet wird, ist der Nutzen der Sicherheiten durch zusätzliche Statistiken moderat. Wenn die Tabelle schreibgeschützt wäre, wären die Kosten niedrig - aber andererseits würden wir wahrscheinlich sofort einen Nur-Index-Scan sehen.
Vielleicht können Sie auch bessere Abfragepläne erzielen, indem Sie ein höheres Statistikziel für festlegen
mytable.myid
. Das würde nur geringe Kosten verursachen. Mehr:quelle
myid/100000 BETWEEN somevalue AND othervalue
Bedingung, sodass der Index ohnehin im Abfrageplan verwendet wird. Ich habe diese Frage gerade gestellt, weil ich nicht verstanden habe, warum der Index im Fall der gesamten Tabelle nützlich ist.WHERE myid BETWEEN somevalue*100000 AND othervalue*100000
(man denke Rundungseffekte auf Ihre Typen abhängig), und Sie haben wahrscheinlich bereits einen einfachen Index aufmyid
, so dass Sie ohne zusätzliche Spezialindex tun können. Könnte effizienter sein.Wenn Sie einen Ausdrucksindex erstellen, sammelt PostgreSQL Statistiken zu diesem Ausdruck. Mit diesen Statistiken verfügt es nun über eine genaue Schätzung der Anzahl der aggregierten Zeilen, die von der Abfrage zurückgegeben werden, was dazu führt, dass eine bessere Planauswahl getroffen wird.
Insbesondere in diesem Fall wurde die Hash-Tabelle ohne diese zusätzlichen Statistiken für zu groß gehalten, um in work_mem zu passen, sodass diese Methode nicht ausgewählt wurde.
quelle
work_mem
. Wenn Sie es so erhöht haben, dass die Sortierung in den Speicher passt, würde es immer noch denselben Plan verwenden. Lassen Sie mich merken hier , dass die Zeitdifferenz ( die meisten davon) von der externen Festplatte sortieren kommt.