Permintaan lambat pada miliaran-baris-tabel // indeks digunakan

10

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_blocktabel 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 timestampdan 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 JOINdengan tabel data saya. Berikut adalah EXPLAIN ANALYZEpermintaan 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
manman
sumber

Jawaban:

1

Ini mungkin mencerminkan bias MS SQL saya, tapi saya akan mencoba mengelompokkan tabel timestamp. Jika Anda sering menarik data untuk rentang waktu tertentu, ini akan membantu karena data akan disimpan secara bersamaan. Sistem dapat mencari ke titik awal, memindai ke ujung rentang, dan dilakukan. Jika Anda meminta waktu tertentu, itu hanya 3.600.000 catatan.

Jika kueri Anda (yang ...?) Adalah untuk mesin tertentu, Postgres perlu menyaring 99,9% dari catatan 3,6 M itu. Jika filter satu-dalam-seribu ini lebih selektif daripada fitler rentang tanggal yang khas, Anda harus menggunakan bidang yang lebih selektif macsebagai komponen pertama dari indeks Anda. Mungkin masih layak untuk dikelompokkan.

Jika itu masih tidak berhasil, saya akan mempartisi dengan bidang yang sama dengan Anda mengindeks, baik timestampatau mac.

Anda tidak memberikan tipe data. Apakah sesuai dengan data? Menyimpan tanggal sebagai teks tidak perlu akan menggembungkan meja Anda, misalnya.

Jon dari Semua Perdagangan
sumber
2
Postgres tidak memiliki indeks berkerumun (meskipun dapat mengelompokkan tabel di sepanjang indeks - tetapi itu perlu dilakukan secara manual dan tidak akan "tetap")
a_horse_with_no_name
terima kasih untuk nasehatnya. sekarang ini berjalan lebih cepat dari sebelumnya, tetapi masih pada kinerja yang sangat rendah> 30detik per permintaan. Saya juga melakukan pengelompokan, tetapi seperti @a_horse_with_no_name mengatakan: di postgres ini adalah satu-shot. tipe data saya benar saya pikir. Saya menambahkan mereka dalam pertanyaan
manman
Tanpa tabel berkerumun, rekomendasi berikutnya untuk kueri rentang akan dipartisi.
Jon dari Semua Perdagangan
-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?

KC-NH
sumber
sisipan dalam batch: saya bisa melakukan sisipan massal tetapi saat ini saya sedang mengerjakan database uji, di mana tidak ada sisipan dibuat sama sekali saat kueri sedang berjalan. tapi terima kasih saya akan memikirkannya nanti :) indeks: saya punya indeks di setiap tabel. pada tabel data indeks pada id, pada tabel data_block pada (mac, timestamp). masalahnya juga ada ketika saya sedang mencari data per bergabung-kiri tetapi tidak ada. bahkan dengan indeks itu mencari tabel data. bidang nullable: tidak mungkin karena data_block dapat memiliki lebih dari satu data dari satu jenis. 1xdata_block -> 4xdataA eg
manman
Apakah alat DB Anda memberi Anda penganalisis permintaan? Anda mungkin perlu indeks pada data_block berdasarkan id.
KC-NH
Saya akan mencoba, tetapi saya tidak mengerti mengapa ini bisa membantu !?
manman
-2

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.

Profesor Photon
sumber
2
Banyak RDBMS memiliki lebih banyak pilihan daripada indeks B-tree (hash, bitmap, dan lainnya). Beberapa DBMS menyimpan baris dan beberapa menyimpan kolom. Dan O (logn) tidak buruk, bahkan untuk miliaran baris. Dan mereka tidak mungkin mencapai batas ketika mereka menggunakan mesin memori 4GB.
ypercubeᵀᴹ