Milyarlarca-satır-tablo // dizininde yavaş sorgular kullanıldı


10

Genç bir geliştirici olduğum ve veritabanlarını (PostgreSQL 9.3) kullanma konusunda yetenekli olmadığım için, gerçekten yardıma ihtiyacım olan bir projeyle ilgili bazı sorunlarla karşılaştım.

Projem, her cihazın saniyede bir veri bloğu gönderdiği cihazlardan (1000 veya daha fazla cihaza kadar) veri toplamakla ilgilidir, bu da saatte yaklaşık 3 milyon satır yapar.

Şu anda her cihazın gelen verilerini sakladığım büyük bir tablo var:

CREATE TABLE data_block(
    id bigserial
    timestamp timestamp
    mac bigint
)

Bir veri bloğunun içerebileceği (veya ekleyemediği) çeşitli veri türleri olduğundan data_blocktabloya başvuran başka tablolar da vardır .

CREATE TABLE dataA(
    data_block_id bigserial
    data

    CONSTRAINT fkey FOREIGN KEY (data_block_id) REFERENCES data_block(id);
);
CREATE TABLE dataB(...);
CREATE TABLE dataC(...);
CREATE INDEX index_dataA_block_id ON dataA (data_block_id DESC);
...

Bir data_block'ta 3x dataA, 1x dataB olması, ancak dataC olmaması mümkündür.

Veriler birkaç hafta saklanacak, bu yüzden bu tabloda ~ 5 milyar satır olacak. Şu anda, tabloda ~ 600 milyon satır var ve sorgularım çok uzun sürüyor. Bu yüzden bir dizin oluşturmaya karar verdim timestampve macçünkü select deyimlerim her zaman zaman içinde ve sıklıkla + mac üzerinde sorgulanıyor.

CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);

... ancak sorgularım hala uzun sürüyor. Örneğin, bir gün ve bir mac için verileri sorguladım:

SELECT * FROM data_block 
WHERE timestamp>'2014-09-15' 
AND timestamp<'2014-09-17' 
AND mac=123456789
Index Scan using index_ts_mac on data_block  (cost=0.57..957307.24 rows=315409 width=32) (actual time=39.849..334534.972 rows=285857 loops=1)
  Index Cond: ((timestamp > '2014-09-14 00:00:00'::timestamp without time zone) AND (timestamp < '2014-09-16 00:00:00'::timestamp without time zone) AND (mac = 123456789))
Total runtime: 334642.078 ms

Sorgu çalıştırmadan önce tam bir vakum yaptım. <10sn bir sorgu yapmak için büyük tablolarda böyle bir sorunu çözmek için zarif bir yolu var mı?

Bölümleme hakkında okudum, ama bu benim dataA, dataB, data_block_id için dataC referansları ile çalışmaz değil mi? Bir şekilde çalışırsa, zaman içinde veya mac üzerinde bölümler yapmalı mıyım?

İndeksimi diğer yöne değiştirdim. Önce MAC, sonra zaman damgası, hem de çok fazla performans kazanıyor.

CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);

Ancak yine de, sorgular 30 saniyeden fazla sürer. Özellikle LEFT JOINveri tablolarımla bir yaptığımda. İşte EXPLAIN ANALYZEyeni dizine sahip bir sorgu:

EXPLAIN ANALYZE SELECT * FROM data_block WHERE mac = 123456789 AND timestamp < '2014-10-05 00:00:00' AND timestamp > '2014-10-04 00:00:00'
Bitmap Heap Scan on data_block  (cost=1514.57..89137.07 rows=58667 width=28) (actual time=2420.842..32353.678 rows=51342 loops=1)
  Recheck Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
  ->  Bitmap Index Scan on index_mac_ts  (cost=0.00..1499.90 rows=58667 width=0) (actual time=2399.291..2399.291 rows=51342 loops=1)
        Index Cond: ((mac = 123456789) AND (timestamp < '2014-10-05 00:00:00'::timestamp without time zone) AND (timestamp > '2014-10-04 00:00:00'::timestamp without time zone))
Total runtime: 32360.620 ms 

Maalesef donanımım kesinlikle sınırlı. Intel i3-2100 @ 3.10Ghz, 4GB RAM kullanıyorum. Mevcut ayarlarım aşağıdaki gibidir:

default_statistics_target = 100
maintenance_work_mem = 512MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 4GB
work_mem = 512MB
wal_buffers = 16MB
checkpoint_segments = 32
shared_buffers = 2GB
max_connections = 20
random_page_cost = 2

Yanıtlar:


1

Bu benim MS SQL önyargı yansıtabilir, ancak tablo tarafından kümeleme deneyin timestamp. Belirli bir zaman aralığı için sık sık veri alıyorsanız, veriler fiziksel olarak bitişik olarak saklanacağı için bu yardımcı olacaktır. Sistem başlangıç ​​noktasını arayabilir, aralığın sonuna kadar tarayabilir ve yapılabilir. Belirli bir saati sorguluyorsanız, bu sadece 3.600.000 kayıttır.

Sorgunuz (...?) Belirli bir makine içinse, Postgres'in bu 3.6 M kayıtlarının% 99.9'unu filtrelemesi gerekir. Bu binde bir filtre, tipik bir tarih aralığı ayırıcısından daha seçiciyse, macdizininizin ilk bileşeni olarak daha seçici alanı kullanmalısınız . Yine de kümelenmeye değer olabilir.

Bu yine de yapmazsa, dizine eklediğiniz aynı alana timestampveya bölüme ayırırım mac.

Veri türlerini vermediniz. Verilere uygun mu? Örneğin, tarihleri ​​metin olarak depolamak tablonuzu gereksiz yere şişirir.


2
Postgres'in kümelenmiş dizinleri yoktur ( bir tabloyu bir dizin boyunca kümeleyebilir - ancak bunun manuel olarak yapılması gerekir ve "kalmaz")
a_horse_with_no_name

tavsiye için teşekkür ederim. şimdi her zamankinden daha hızlı çalışıyor, ancak yine de sorgu başına 30 saniyeden çok düşük bir performansta. ben de kümeleme yaptım, ama @a_horse_with_no_name dedi ki: postgres bu tek atış. veri tiplerim doğru bence. onları soruya
ekledim

Kümelenmiş tablolar olmadan, aralık sorguları için bir sonraki önerim bölümleme olacaktır.
Tüm

-2

Elektrik sayaçlarından milyarlarca ölçüm yapan bir uygulama üzerinde çalıştım ve 10 saniyenin altında birçok sorguyu yürüttüm.

Çevremiz farklıydı. Sunucu sınıfı bir makinede Microsoft SQL Server (4 çekirdek, 24 GB bellek). Bir sunucuya yükseltme şansınız var mı?

Büyük bir sorun, okumaların birer birer alınmasının veritabanı üzerinde büyük bir performans etkisinin olmasıdır. Gerekli verileri yazmak kilitler ve sorgular beklerdi. Toplu olarak kesici uçlar yapabilir misiniz?

Şemanızla 4 çok büyük masanız olacak. Tüm birleştirmelerinizin her iki tabloda da dizin kullanması önemlidir. Bir tablo taraması sonsuza kadar sürecektir. Onları boş alanları olan 1 tablo ile birleştirmek mümkün müdür?


toplu olarak ekler: toplu ekler yapabilirdim ama şu anda bir sorgu çalışırken hiçbir ekler hiç yapılmadığı bir test veritabanı üzerinde çalışıyorum im. ama teşekkür ederim daha sonra düşüneceğim :) endeksler: her tablo üzerinde dizinler var. veri tablolarında id'deki bir dizin, data_block tablosundaki (mac, zaman damgası). sorun sol im birleştirme için dataA ararken im de orada ama orada yok. indeksle bile veri tablolarını arar. null olabilecek alanlar: bir data_block bir türden birden fazla veri içerebileceğinden mümkün değildir. 1xdata_block -> 4xdataA eg
manman

DB aracınız size bir sorgu analizörü veriyor mu? Kimliğine göre data_block dizinine ihtiyacınız olabilir.
KC-NH

deneyeceğim, ama bunun neden yardımcı olabileceğini anlamıyorum !?
manman

-2

Postgres'in (veya başka herhangi bir RDBMS'nin) doğal ölçeklenebilirlik sınırlarına ulaşıyorsunuz.

Bir RDBMS dizininin bir B-Ağacı olduğunu unutmayın. B-Ağacı hem ortalama hem de en kötü durum için O (log n) şeklindedir. Bu onu makul N değerleri için güzel, güvenli ve öngörülebilir bir seçim yapar. N çok büyüdüğünde bozulur.

NoSQL veritabanları karma tablolardır (çoğunlukla). Bir karma tablosu ortalama durumda O (1) ve en kötü durumda O (n) 'dir. En kötü durumdan kaçınabileceğinizi varsayarsak, çok büyük N değerleri için gerçekten iyi performans gösterir.

Ek olarak, bir karma tablosunun paralelleştirilmesi kolaydır ve b-ağacı değildir. Bu, karma tabloları dağıtılmış bir hesaplama mimarisi için daha uygun hale getirir.

Milyar sıra tablolara ulaşmaya başladığınızda, RDBMS'den NoSQL'e geçmeyi düşünmenin zamanı geldi. Cassandra muhtemelen kullanım durumunuz için iyi bir seçim olacaktır.


2
Birçok RDBMS, B-ağacı dizinlerinden (karma, bitmap ve diğerleri) çok daha fazla seçeneğe sahiptir. Bazı DBMS satırları ve bazıları da sütunları depolar. Ve O (logn) milyarlarca satır için bile kötü değil. Ve 4GB bellek makinesi kullanırken herhangi bir sınıra ulaşamıyor olabilirler.
ypercubeᵀᴹ
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.