Karena saya adalah pengembang muda dan tidak benar-benar terampil dalam menggunakan basis data (PostgreSQL 9.3) saya mengalami beberapa masalah dengan sebuah proyek, di mana saya benar-benar membutuhkan bantuan.
Proyek saya adalah mengumpulkan data dari perangkat (hingga 1000 perangkat atau lebih), di mana setiap perangkat mengirim satu blok data setiap detik, yang menghasilkan sekitar 3 juta baris per jam.
Saat ini saya punya satu meja besar tempat saya menyimpan data yang masuk dari setiap perangkat:
CREATE TABLE data_block(
id bigserial
timestamp timestamp
mac bigint
)
Karena ada beberapa jenis data yang dapat dimasukkan oleh suatu blok data (atau tidak bisa), ada tabel lain yang merujuk data_block
tabel tersebut.
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);
...
Ada kemungkinan bahwa dalam satu data_block ada 3x dataA, 1x dataB, tetapi tidak ada dataC.
Data akan disimpan selama beberapa minggu, jadi saya akan memiliki ~ 5 miliar baris dalam tabel ini. Saat ini, saya memiliki ~ 600 juta baris di meja dan pertanyaan saya membutuhkan waktu yang sangat lama. Jadi saya memutuskan untuk membuat indeks lebih timestamp
dan mac
, karena pernyataan pilih saya selalu query dari waktu ke waktu dan sering juga dari waktu ke waktu + mac.
CREATE INDEX index_ts_mac ON data_block (timestamp DESC, mac);
... tapi pertanyaan saya masih butuh waktu lama. Misalnya, saya menanyakan data untuk satu hari dan satu mac:
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
Saya melakukan vakum penuh sebelum menjalankan query. Apakah ada cara yang elegan untuk memecahkan masalah dengan tabel besar untuk melakukan kueri <10detik?
Saya membaca tentang mempartisi, tetapi ini tidak akan berfungsi dengan dataA saya, dataB, referensi dataC ke data_block_id kan? Jika itu akan berhasil, haruskah saya membuat partisi dari waktu ke waktu atau di atas mac?
Saya mengubah indeks saya ke arah lain. Pertama MAC, lalu timestamp, dan itu mendapatkan banyak kinerja.
CREATE INDEX index_mac_ts ON data_block (mac, timestamp DESC);
Tapi tetap saja, kueri membutuhkan> 30detik. Terutama ketika saya melakukan LEFT JOIN
dengan tabel data saya. Berikut adalah EXPLAIN ANALYZE
permintaan dengan indeks baru:
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
Sayangnya perangkat keras saya sangat terbatas. Saya menggunakan Intel i3-2100 @ 3.10Ghz, RAM 4GB. Pengaturan saya saat ini adalah sebagai berikut:
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
Saya mengerjakan aplikasi yang memiliki miliaran bacaan dari meteran listrik dan menjalankan sebagian besar kueri dalam waktu kurang dari 10 detik.
Lingkungan kami berbeda. Microsoft SQL Server pada mesin kelas server (4 core, memori 24 GB). Adakah peluang untuk meningkatkan ke server?
Satu masalah besar adalah bahwa menelan pembacaan satu per satu memiliki dampak kinerja yang besar pada database. Menulis data diperlukan kunci dan kueri akan menunggu. Bisakah Anda memasukkan secara berkelompok?
Dengan skema Anda, Anda akan memiliki 4 tabel yang sangat besar. Penting bahwa semua gabungan Anda menggunakan indeks di kedua tabel. Pemindaian tabel akan berlangsung selamanya. Apakah layak untuk menggabungkannya ke 1 tabel dengan bidang yang dapat dibatalkan?
sumber
Anda mencapai batas skalabilitas yang melekat pada Postgres (atau RDBMS lainnya).
Ingat bahwa indeks RDBMS adalah B-Tree. B-Tree adalah O (log n) untuk kasus rata-rata dan terburuk. Ini membuatnya menjadi pilihan yang bagus, aman, dapat diprediksi untuk nilai-nilai wajar dari N. Itu rusak ketika N menjadi terlalu besar.
Database NoSQL adalah (sebagian besar) tabel hash. Tabel hash adalah O (1) dalam kasus rata-rata dan O (n) dalam kasus terburuk. Dengan asumsi Anda dapat menghindari kasus terburuk, ia berkinerja sangat baik untuk nilai N. yang sangat besar
Selain itu, tabel hash mudah diparalelkan dan b-tree tidak. Ini membuat tabel hash lebih cocok untuk arsitektur komputasi terdistribusi.
Ketika Anda mulai mendapatkan miliaran tabel baris, saatnya untuk mempertimbangkan beralih dari RDBMS ke NoSQL. Cassandra mungkin akan menjadi pilihan yang baik untuk kasus penggunaan Anda.
sumber