Unser System schreibt viele Daten (eine Art Big-Data-System). Die Schreibleistung ist gut genug für unsere Anforderungen, aber die Leseleistung ist wirklich zu langsam.
Die Primärschlüsselstruktur (Constraint) ist für alle unsere Tabellen ähnlich:
timestamp(Timestamp) ; index(smallint) ; key(integer).
Eine Tabelle kann Millionen von Zeilen enthalten, sogar Milliarden von Zeilen, und eine Leseanforderung gilt normalerweise für einen bestimmten Zeitraum (Zeitstempel / Index) und ein bestimmtes Tag. Es ist üblich, eine Abfrage zu haben, die ungefähr 200.000 Zeilen zurückgibt. Gegenwärtig können wir ungefähr 15.000 Zeilen pro Sekunde lesen, aber wir müssen 10-mal schneller sein. Ist das möglich und wenn ja, wie?
Hinweis: PostgreSQL ist im Lieferumfang unserer Software enthalten, sodass sich die Hardware von Client zu Client unterscheidet.
Es ist eine VM, die zum Testen verwendet wird. Der Host der VM ist Windows Server 2008 R2 x64 mit 24,0 GB RAM.
Serverspezifikation (virtuelle Maschine VMWare)
Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)
postgresql.conf
Optimierungen
shared_buffers = 512MB (default: 32MB)
effective_cache_size = 1024MB (default: 128MB)
checkpoint_segment = 32 (default: 3)
checkpoint_completion_target = 0.9 (default: 0.5)
default_statistics_target = 1000 (default: 100)
work_mem = 100MB (default: 1MB)
maintainance_work_mem = 256MB (default: 16MB)
Tabellendefinition
CREATE TABLE "AnalogTransition"
(
"KeyTag" integer NOT NULL,
"Timestamp" timestamp with time zone NOT NULL,
"TimestampQuality" smallint,
"TimestampIndex" smallint NOT NULL,
"Value" numeric,
"Quality" boolean,
"QualityFlags" smallint,
"UpdateTimestamp" timestamp without time zone, -- (UTC)
CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag" ),
CONSTRAINT "FK_AnalogTransition_Tag" FOREIGN KEY ("KeyTag")
REFERENCES "Tag" ("Key") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE,
autovacuum_enabled=true
);
Abfrage
Die Ausführung der Abfrage in pgAdmin3 dauert ungefähr 30 Sekunden, wir möchten jedoch, wenn möglich, dasselbe Ergebnis in weniger als 5 Sekunden erzielen.
SELECT
"AnalogTransition"."KeyTag",
"AnalogTransition"."Timestamp" AT TIME ZONE 'UTC',
"AnalogTransition"."TimestampQuality",
"AnalogTransition"."TimestampIndex",
"AnalogTransition"."Value",
"AnalogTransition"."Quality",
"AnalogTransition"."QualityFlags",
"AnalogTransition"."UpdateTimestamp"
FROM "AnalogTransition"
WHERE "AnalogTransition"."Timestamp" >= '2013-05-16 00:00:00.000' AND "AnalogTransition"."Timestamp" <= '2013-05-17 00:00:00.00' AND ("AnalogTransition"."KeyTag" = 56 OR "AnalogTransition"."KeyTag" = 57 OR "AnalogTransition"."KeyTag" = 58 OR "AnalogTransition"."KeyTag" = 59 OR "AnalogTransition"."KeyTag" = 60)
ORDER BY "AnalogTransition"."Timestamp" DESC, "AnalogTransition"."TimestampIndex" DESC
LIMIT 500000;
Erklären Sie 1
"Limit (cost=0.00..125668.31 rows=500000 width=33) (actual time=2.193..3241.319 rows=500000 loops=1)"
" Buffers: shared hit=190147"
" -> Index Scan Backward using "PK_AnalogTransition" on "AnalogTransition" (cost=0.00..389244.53 rows=1548698 width=33) (actual time=2.187..1893.283 rows=500000 loops=1)"
" Index Cond: (("Timestamp" >= '2013-05-16 01:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-16 15:00:00-04'::timestamp with time zone))"
" Filter: (("KeyTag" = 56) OR ("KeyTag" = 57) OR ("KeyTag" = 58) OR ("KeyTag" = 59) OR ("KeyTag" = 60))"
" Buffers: shared hit=190147"
"Total runtime: 3863.028 ms"
Erklären Sie 2
In meinem letzten Test hat die Auswahl meiner Daten 7 Minuten gedauert! Siehe unten:
"Limit (cost=0.00..313554.08 rows=250001 width=35) (actual time=0.040..410721.033 rows=250001 loops=1)"
" -> Index Scan using "PK_AnalogTransition" on "AnalogTransition" (cost=0.00..971400.46 rows=774511 width=35) (actual time=0.037..410088.960 rows=250001 loops=1)"
" Index Cond: (("Timestamp" >= '2013-05-22 20:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-24 20:00:00-04'::timestamp with time zone) AND ("KeyTag" = 16))"
"Total runtime: 411044.175 ms"
quelle
Aus den Plänen geht hervor, dass Ihr Index entweder aufgebläht ist (dann zusammen mit der zugrunde liegenden Tabelle) oder einfach nicht für diese Art von Abfrage geeignet ist (ich habe versucht, dies in meinem letzten Kommentar oben zu beheben).
Eine Zeile des Index enthält 14 Datenbytes (und einige für den Header). Berechnen Sie nun anhand der im Plan angegebenen Zahlen: Sie haben 500.000 Zeilen von 190147 Seiten erhalten - das bedeutet im Durchschnitt weniger als 3 nützliche Zeilen pro Seite, dh etwa 37 Byte pro 8-kb-Seite. Das ist ein sehr schlechtes Verhältnis, nicht wahr? Da die erste Spalte des Index das
Timestamp
Feld ist und in der Abfrage als Bereich verwendet wird, kann und kann der Planer den Index auswählen, um passende Zeilen zu finden.TimestampIndex
DieWHERE
Bedingungen werden jedoch nicht erwähnt , sodass das Filtern nachKeyTag
nicht sehr effektiv ist, da diese Werte angeblich zufällig auf den Indexseiten angezeigt werden.Eine Möglichkeit besteht darin, die Indexdefinition in zu ändern
(oder erstellen Sie diesen Index unter Berücksichtigung der Auslastung Ihres Systems als neuen:
Die andere Möglichkeit ist, dass ein großer Teil der Indexseiten von toten Zeilen belegt ist, die durch Staubsaugen entfernt werden könnten. Sie haben die Tabelle mit Einstellung erstellt
autovacuum_enabled=true
- aber haben Sie jemals mit dem automatischen Staubsaugen begonnen? OderVACUUM
manuell ausführen ?quelle