Sistem kami menulis banyak data (semacam sistem Big Data). Kinerja menulis cukup baik untuk kebutuhan kita, tetapi kinerja membaca benar-benar terlalu lambat.
Struktur primary key (constraint) serupa untuk semua tabel kami:
timestamp(Timestamp) ; index(smallint) ; key(integer).
Sebuah tabel dapat memiliki jutaan baris, bahkan miliaran baris, dan permintaan baca biasanya untuk periode tertentu (cap waktu / indeks) dan tag. Adalah umum untuk memiliki kueri yang mengembalikan sekitar 200 ribu baris. Saat ini, kami dapat membaca sekitar 15k baris per detik tetapi kami harus 10 kali lebih cepat. Apakah ini mungkin dan jika iya, bagaimana?
Catatan: PostgreSQL dikemas dengan perangkat lunak kami, jadi perangkat kerasnya berbeda dari satu klien ke klien lainnya.
Ini adalah VM yang digunakan untuk pengujian. Tuan rumah VM adalah Windows Server 2008 R2 x64 dengan 24.0 GB RAM.
Server Spec (VMWare Mesin Virtual)
Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)
postgresql.conf
optimisasi
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)
Definisi Tabel
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
);
Pertanyaan
Query membutuhkan sekitar 30 detik untuk dieksekusi di pgAdmin3, tetapi kami ingin memiliki hasil yang sama di bawah 5 detik jika memungkinkan.
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;
Jelaskan 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"
Jelaskan 2
Dalam tes terakhir saya, butuh 7 menit untuk memilih data saya! Lihat di bawah:
"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"
sumber
Jadi, dari rencana saya melihat satu hal: indeks Anda membengkak (kemudian bersama dengan tabel yang mendasari) atau tidak benar-benar baik untuk pertanyaan semacam ini (saya mencoba untuk mengatasinya dalam komentar terakhir saya di atas).
Satu baris indeks berisi 14 byte data (dan beberapa untuk header). Sekarang, menghitung dari angka-angka yang diberikan dalam rencana: Anda mendapat 500.000 baris dari 190147 halaman - itu berarti, rata-rata, kurang dari 3 baris berguna per halaman, yaitu, sekitar 37 byte per halaman 8 kb. Ini adalah rasio yang sangat buruk, bukan? Karena kolom pertama dari indeks adalah
Timestamp
bidang dan digunakan dalam kueri sebagai rentang, perencana dapat - dan memang - memilih indeks untuk menemukan baris yang cocok. Tetapi tidak adaTimestampIndex
disebutkan dalamWHERE
kondisi, jadi penyaringan padaKeyTag
tidak terlalu efektif karena nilai-nilai itu seharusnya muncul secara acak di halaman indeks.Jadi, satu kemungkinan adalah mengubah definisi indeks menjadi
(atau, mengingat beban sistem Anda, buat indeks ini sebagai yang baru:
Kemungkinan lain bahwa sebagian besar halaman indeks ditempati oleh baris mati, yang dapat dihilangkan dengan menyedot debu. Anda membuat tabel dengan pengaturan
autovacuum_enabled=true
- tetapi apakah Anda pernah memulai autovacuuming? Atau jalankanVACUUM
secara manual?sumber