PostgreSQL'i okuma performansı için yapılandırma


39

Sistemimiz çok fazla veri yazar (bir tür Büyük Veri sistemi). Yazma performansı ihtiyaçlarımız için yeterince iyi ancak okuma performansı gerçekten çok yavaş.

Birincil anahtar (kısıtlama) yapısı tüm tablolarımız için aynıdır:

timestamp(Timestamp) ; index(smallint) ; key(integer).

Bir tabloda milyonlarca satır, hatta milyarlarca satır bulunabilir ve okuma isteği genellikle belirli bir süre (zaman damgası / dizin) ve etiketi içindir. 200k satır döndüren bir sorguya sahip olmak yaygındır. Şu anda, saniyede yaklaşık 15k satır okuyabiliyoruz ancak 10 kat daha hızlı olmamız gerekiyor. Bu mümkün mü ve eğer mümkünse nasıl?

Not: PostgreSQL bizim yazılımımızla birlikte gelir, böylece donanım bir müşteriden diğerine farklılık gösterir.

Test için kullanılan bir VM'dir. VM'nin sunucusu, 24.0 GB RAM'e sahip Windows Server 2008 R2 x64'tür.

Sunucu Özelliği (Sanal Makine VMWare)

Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)

postgresql.conf optimizasyon

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)

Tablo Tanımı

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
);

Sorgu

Sorgu pgAdmin3'te yürütmek için yaklaşık 30 saniye sürer, ancak aynı sonucu mümkünse 5 saniyenin altında almak istiyoruz.

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;

Açıkla 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"

Açıkla 2

Son testimde verilerimi seçmek 7 dakika sürdü! Aşağıya bakınız:

"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"

Yanıtlar:


52

Veri hizalama ve depolama boyutu

Aslında, tuple başına düşen tepe noktası, başlık başlığı için 24 bayt artı, öğe işaretçisi için 4 bayttır.
Bu cevapta hesaplamada daha fazla detay:

SO ile ilgili bu cevapta veri hizalama ve doldurmanın temelleri:

Birincil anahtar için üç sütunumuz var:

PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag")

"Timestamp"      timestamp (8 bytes)
"TimestampIndex" smallint  (2 bytes)
"KeyTag"         integer   (4 bytes)

Sonuçlar:

 Sayfa başlığında 4 baytlık öğe işaretçisi (8 baytın katlarına kadar sayılmaz)
---
Bağlantı başlığı için 23 bayt
 Veri hizalama için 1 baytlık dolgu (veya NULL bitmap)
 8 bayt "Zaman damgası"
 2 bayt "TimestampIndex"
 Veri hizalama için 2 baytlık dolgu
 4 bayt "KeyTag" 
 0 baytın en yakın katına 0 dolgu
-----
Demet başına 44 bayt

Bu ilgili cevapta nesne boyutunu ölçmek hakkında daha fazlası:

Çok sütunlu bir dizindeki sütunların sırası

Anlamak için bu iki soru ve cevabı okuyun:

Dizininize sahip olduğunuz yol (birincil anahtar), bir sıralama adımı olmadan, özellikle de çekici olan satırları alabilirsiniz LIMIT. Ancak satırları almak oldukça pahalı görünüyor.

Genel olarak, çok sütunlu bir dizinde, "eşitlik" sütunlarının önce ve "aralık" sütunlarının en son gitmesi gerekir:

Bu nedenle, ters sütun sırası ile ek bir dizin deneyin :

CREATE INDEX analogransition_mult_idx1
   ON "AnalogTransition" ("KeyTag", "TimestampIndex", "Timestamp");

Veri dağıtımına bağlıdır. Ancak bununla millions of row, even billion of rowsdaha hızlı olabilir.

Veri hizalama ve doldurma nedeniyle küme boyutu 8 bayttır. Bunu düz dizin olarak kullanıyorsanız, üçüncü sütunu bırakmayı deneyebilirsiniz "Timestamp". Biraz daha hızlı olabilir veya olmayabilir (çünkü sıralamada yardımcı olabilir).

Her iki dizini de tutmak isteyebilirsiniz. Bazı faktörlere bağlı olarak, orijinal dizininiz tercih edilebilir - özellikle küçük LIMIT.

otovakum ve tablo istatistikleri

Tablo istatistiklerinin güncel olması gerekiyor. Otovakumun çalıştığından eminim .

Tablonuz çok büyük ve doğru sorgu planı için istatistikler önemli göründüğü için, alakalı sütunlar için istatistik hedefini büyük ölçüde artıracağım :

ALTER TABLE "AnalogTransition" ALTER "Timestamp" SET STATISTICS 1000;

... hatta milyarlarca satır ile daha da yüksek. Maksimum 10000, varsayılan 100'dür.

Bunu, tüm WHEREya da ORDER BYmaddelerde yer alan tüm sütunlar için yapın . O zaman koş ANALYZE.

Masa düzeni

Bu sırada, veri hizalama ve doldurma hakkında öğrendiklerinizi uygularsanız, bu optimize edilmiş tablo düzeni biraz disk alanı kazanmalı ve performansı biraz desteklemelidir (pk & fk dikkate alınmadan):

CREATE TABLE "AnalogTransition"(
  "Timestamp" timestamp with time zone NOT NULL,
  "KeyTag" integer NOT NULL,
  "TimestampIndex" smallint NOT NULL,
  "TimestampQuality" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  "QualityFlags" smallint,
  "Quality" boolean,
  "Value" numeric
);

CLUSTER / pg_repack

Belirli bir dizini kullanan sorgular için okuma performansını optimize etmek için (orijinali veya önerilen alternatifim olabilir), tabloyu dizinin fiziksel sırasına göre yeniden yazabilirsiniz. CLUSTERBunu yapar, ancak oldukça invazivdir ve operasyon süresince özel bir kilit gerektirir. pg_repackAynı şeyi masaya özel bir kilit olmadan yapabilen daha sofistike bir alternatiftir.
Bu, büyük tablolarda büyük ölçüde yardımcı olabilir, çünkü tablonun daha az bloğu okunmalıdır.

Veri deposu

Genel olarak, 2GB fiziksel RAM, milyarlarca satırla hızlı bir şekilde başa çıkmak için yeterli değildir. Daha fazla RAM uzun bir yol kat edebilir - uyarlanmış ayar eşliğinde: başlangıçta daha büyük effective_cache_size.


2
Sadece KeyTag'e basit bir indeks ekledim ve şimdi oldukça hızlı görünüyor. Veri hizalama konusundaki önerilerinizi de uygulayacağım. Çok teşekkürler!
JPelletier

9

Yani, gördüğüm planlardan tek bir şey var: ya indeksi ya şişirilmiş (o zaman alttaki tablo ile birlikte) ya da sadece bu tür bir sorgu için gerçekten iyi değil (yukarıdaki son yorumumda bunu ele almaya çalıştım).

Dizinin bir satırı 14 bayt veri içerir (bazıları başlık için). Şimdi, planda verilen rakamlardan hesaplayarak: 190147 sayfadan 500.000 satır aldınız - bu, ortalama olarak, sayfa başına 3 faydalı satırdan daha az, yani 8 kb sayfa başına yaklaşık 37 bayt anlamına gelir. Bu çok kötü bir oran, değil mi? Dizinin ilk sütunu Timestampalan olduğundan ve sorguda bir aralık olarak kullanıldığından, planlayıcı eşleşen satırları bulmak için dizini seçebilir - ve yapar -. Ancak şartlarda TimestampIndexbahsedilmemiştir WHERE, bu nedenle filtreleme, KeyTagsöz konusu değerlerin dizin sayfalarında rastgele göründüğü için çok etkili değildir.

Yani, bir olasılık indeks tanımını değiştiriyor.

CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp", "KeyTag", "TimestampIndex")

(veya sisteminizin yükü göz önüne alındığında, bu dizini yeni bir tane olarak oluşturun:

CREATE INDEX CONCURRENTLY "idx_AnalogTransition" 
    ON "AnalogTransition" ("Timestamp", "KeyTag", "TimestampIndex");
  • bu kesin bir süre alacaktır ancak bu arada hala çalışabilirsiniz.)

Dizin sayfalarının büyük bir bölümünün, vakumla kaldırılabilen ölü sıralar tarafından işgal edilmesi olasılığı. Masayı ayarlayarak yarattın autovacuum_enabled=true- ama otomatik yaşamaya hiç başladın mı? Ya da VACUUMelle mi koşuyorsun ?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.