Dalam skenario bisnis seperti keuangan, logistik, dan Internet of Things (IoT), sistem menghasilkan sejumlah besar data deret waktu, seperti catatan transaksi, data lintasan, dan log pemantauan. Melakukan analitik secara real-time terhadap data berskala terabyte ini sering kali menimbulkan tantangan kinerja. PolarDB for PostgreSQL menyediakan solusi hemat biaya untuk menyimpan data deret waktu dalam jumlah besar dengan fitur-fitur seperti tabel partisi dan penyimpanan bertingkat untuk data panas dan dingin. Berdasarkan fondasi ini, fitur In-Memory Column Index (IMCI) memungkinkan Anda melakukan analitik real-time berkinerja tinggi pada data deret waktu dalam jumlah besar tanpa pra-pemrosesan data yang kompleks, sehingga membantu Anda mengoptimalkan nilai data secara efektif.
Tinjauan solusi
Alur operasional
Penulisan data: Aplikasi bisnis menulis data deret waktu, seperti catatan transaksi, ke kluster PolarDB for PostgreSQL.
Indeks penyimpanan kolom: Buat indeks penyimpanan kolom pada tabel dasar. PolarDB for PostgreSQL secara otomatis memelihara data penyimpanan kolom dalam tabel tersebut. Penyimpanan kolom mengorganisasi data berdasarkan kolom, yang memberikan rasio kompresi lebih tinggi dan mengurangi konsumsi I/O selama kueri agregasi karena hanya kolom yang relevan yang perlu dibaca.
Akselerasi kueri: Kueri analitik, seperti agregasi candlestick, diarahkan oleh pengoptimal atau
Hintuntuk menggunakan indeks penyimpanan kolom. Mesin kueri kemudian menggunakan penyimpanan kolom dan pemrosesan paralel untuk memindai serta mengagregasi data dan mengembalikan hasilnya.
Keunggulan solusi
Mudah digunakan: Tidak diperlukan modifikasi bisnis atau proses ekstrak, transformasi, dan muat (ETL) yang kompleks. Anda cukup membuat indeks penyimpanan kolom untuk tabel dasar guna mempercepat kueri analitik secara transparan.
Fitur lengkap: Mendukung secara native tabel partisi dan menyediakan berbagai fungsi analisis deret waktu bawaan, seperti
time_bucket,first, danlast, untuk menyederhanakan pengembangan SQL Anda.
Hasil kinerja
Volume data: 100 juta entri data dalam rentang 2 hari, dengan sekitar 50 juta entri per hari.
Kueri agregasi candlestick: mencakup lima metrik dalam periode tertentu: nilai tertinggi, nilai terendah, harga pembukaan, harga penutupan, dan total volume transaksi.
Tingkat paralelisme indeks penyimpanan kolom: 8.
Waktu yang dibutuhkan adalah sebagai berikut (dalam detik):
Skenario
Agregasi candlestick tingkat detik
Agregasi candlestick tingkat menit
Agregasi K-line per jam
Agregasi candlestick tingkat hari
Agregasi seluruh data (100 juta entri)
3,41
0,95
0,93
0,91
Agregasi data 1 hari (sekitar 50 juta entri)
1,88
0,82
0,81
0,76
Agregasi data 12 jam (sekitar 25 juta entri)
0,89
0,55
0,53
N/A
Agregasi data 1 jam (sekitar 6 juta entri)
0,41
0,39
0,37
N/A
Langkah-langkah implementasi
Langkah 1: Siapkan lingkungan
Pastikan versi dan konfigurasi kluster Anda memenuhi persyaratan berikut:
Versi kluster:
PostgreSQL 16 (versi mesin minor 2.0.16.8.3.0 atau lebih baru)
PostgreSQL 14 (versi mesin minor 2.0.14.10.20.0 atau lebih baru)
CatatanAnda dapat melihat nomor versi mesin minor di konsol atau dengan menjalankan pernyataan
SHOW polardb_version;. Jika versi mesin minor tidak memenuhi persyaratan, Anda harus meningkatkan versi mesin minor.Tabel sumber harus memiliki kunci primer. Kolom kunci primer harus disertakan saat Anda membuat indeks penyimpanan kolom.
Parameter
wal_levelharus diatur kelogical. Hal ini menambahkan informasi yang diperlukan untuk mendukung replikasi logis ke dalam write-ahead logging (WAL).CatatanAnda dapat mengatur parameter wal_level di konsol. Mengubah parameter ini akan me-restart kluster. Rencanakan operasi bisnis Anda dengan sesuai dan lakukan dengan hati-hati.
Aktifkan fitur indeks penyimpanan kolom.
Cara mengaktifkan fitur indeks penyimpanan kolom berbeda-beda tergantung pada versi mesin minor kluster PolarDB for PostgreSQL Anda:
Langkah 2: Siapkan data
Solusi ini menggunakan tabel catatan transaksi dan mensimulasikan pembuatan 100 juta entri data transaksi selama kurang lebih dua hari. Selama periode ini, transaksi terjadi setiap hari mulai pukul 08.00 hingga 16.00, dengan perkiraan volume data harian sekitar 40 juta entri.
-- Tabel catatan transaksi CREATE TABLE market_trades ( trade_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, -- Kunci primer auto-increment trade_ts TIMESTAMP, -- Timestamp transaksi market_id VARCHAR, -- ID pasar price DECIMAL, -- Harga transaksi amount DECIMAL, -- Jumlah transaksi insert_ts TIMESTAMP -- Timestamp penulisan sistem ); INSERT INTO market_trades(trade_ts, market_id, price, amount, insert_ts) SELECT trade_ts, market_id, price, amount, trade_ts + (random() * 500)::INT * INTERVAL '1 millisecond' AS insert_ts FROM ( -- ======================== -- 1. Puncak Hari 1: 2025-06-01 08:00 - 16:00, 40 juta entri -- ======================== SELECT '2025-06-01 08:00:00'::TIMESTAMP + (random() * 28800)::INT * INTERVAL '1 second' + -- 28800 detik = 8 jam (random() * 1000)::INT * INTERVAL '1 millisecond' AS trade_ts, CASE WHEN random() < 0.6 THEN 'BTC-USDT' ELSE 'ETH-USDT' END AS market_id, CASE WHEN random() < 0.6 THEN 30000 + (random() * 1000) ELSE 2000 + (random() * 100) END AS price, random() * 10 + 0.1 AS amount FROM generate_series(1, 40000000) UNION ALL -- ======================== -- 2. Off-peak Hari 1: 2025-06-01 16:00 - 2025-06-02 08:00, 10 juta entri -- ======================== SELECT CASE WHEN random() < 0.5 THEN -- 16:00 - 24:00 '2025-06-01 16:00:00'::TIMESTAMP + (random() * 28800)::INT * INTERVAL '1 second' ELSE -- 00:00 - 08:00 (dini hari hari 2) '2025-06-02 00:00:00'::TIMESTAMP + (random() * 28800)::INT * INTERVAL '1 second' END + (random() * 1000)::INT * INTERVAL '1 millisecond' AS trade_ts, CASE WHEN random() < 0.6 THEN 'BTC-USDT' ELSE 'ETH-USDT' END AS market_id, CASE WHEN random() < 0.6 THEN 30000 + (random() * 1000) ELSE 2000 + (random() * 100) END AS price, random() * 10 + 0.1 AS amount FROM generate_series(1, 10000000) UNION ALL -- ======================== -- 3. Puncak Hari 2: 2025-06-02 08:00 - 16:00, 40 juta entri -- ======================== SELECT '2025-06-02 08:00:00'::TIMESTAMP + (random() * 28800)::INT * INTERVAL '1 second' + (random() * 1000)::INT * INTERVAL '1 millisecond' AS trade_ts, CASE WHEN random() < 0.6 THEN 'BTC-USDT' ELSE 'ETH-USDT' END AS market_id, CASE WHEN random() < 0.6 THEN 30000 + (random() * 1000) ELSE 2000 + (random() * 100) END AS price, random() * 10 + 0.1 AS amount FROM generate_series(1, 40000000) UNION ALL -- ======================== -- 4. Off-peak Hari 2: 2025-06-02 16:00 - 2025-06-03 08:00, 10 juta entri -- ======================== SELECT CASE WHEN random() < 0.5 THEN -- 16:00 - 24:00 '2025-06-02 16:00:00'::TIMESTAMP + (random() * 28800)::INT * INTERVAL '1 second' ELSE -- 00:00 - 08:00 (dini hari hari 3) '2025-06-03 00:00:00'::TIMESTAMP + (random() * 28800)::INT * INTERVAL '1 second' END + (random() * 1000)::INT * INTERVAL '1 millisecond' AS trade_ts, CASE WHEN random() < 0.6 THEN 'BTC-USDT' ELSE 'ETH-USDT' END AS market_id, CASE WHEN random() < 0.6 THEN 30000 + (random() * 1000) ELSE 2000 + (random() * 100) END AS price, random() * 10 + 0.1 AS amount FROM generate_series(1, 10000000) ) AS data;Buat indeks penyimpanan kolom untuk tabel catatan transaksi.
CREATE INDEX idx_csi_market_trades ON market_trades USING CSI;
Langkah 3: Jalankan kueri agregasi candlestick
Skenario: Hitung data candlestick untuk setiap jendela waktu tetap.
Contoh: Hitung harga tertinggi, harga terendah, harga pembukaan, harga penutupan, dan total volume transaksi untuk setiap detik.
Contoh berikut menghitung data candlestick per detik, per menit, per jam, dan per hari.
Agregasi candlestick tingkat kedua
-- Agregasi candlestick tingkat detik
/*+ SET (polar_csi.enable_query on) */
SELECT
time_bucket('1 second', trade_ts) AS candle_ts, -- Data dalam 1 detik
market_id,
MIN(price) AS low, -- Harga terendah dalam 1 detik
MAX(price) AS high, -- Harga tertinggi dalam 1 detik
FIRST(price ORDER BY trade_ts) AS open, -- Harga pembukaan dalam 1 detik
LAST(price ORDER BY trade_ts) AS close, -- Harga penutupan dalam 1 detik
SUM(amount) AS vol -- Total volume transaksi dalam 1 detik
FROM market_trades
WHERE trade_ts >= '2025-06-01 00:00:00' AND trade_ts <= '2025-06-02 00:00:00'
GROUP BY candle_ts, market_id
ORDER BY candle_ts, market_id;Agregasi candlestick tingkat menit
-- Agregasi candlestick tingkat menit
/*+ SET (polar_csi.enable_query on) */
SELECT
time_bucket('1 minute', trade_ts) AS candle_ts, -- Data dalam 1 menit
market_id,
MIN(price) AS low, -- Harga terendah dalam 1 menit
MAX(price) AS high, -- Harga tertinggi dalam 1 menit
FIRST(price ORDER BY trade_ts) AS open, -- Harga pembukaan dalam 1 menit
LAST(price ORDER BY trade_ts) AS close, -- Harga penutupan dalam 1 menit
SUM(amount) AS vol -- Total volume transaksi dalam 1 menit
FROM market_trades
WHERE trade_ts >= '2025-06-01 00:00:00' AND trade_ts <= '2025-06-02 00:00:00'
GROUP BY candle_ts, market_id
ORDER BY candle_ts, market_id;Agregasi candlestick tingkat jam
-- Agregasi candlestick tingkat jam
/*+ SET (polar_csi.enable_query on) */
SELECT
time_bucket('1 hour', trade_ts) AS candle_ts, -- Data dalam 1 jam
market_id,
MIN(price) AS low, -- Harga terendah dalam 1 jam
MAX(price) AS high, -- Harga tertinggi dalam 1 jam
FIRST(price ORDER BY trade_ts) AS open, -- Harga pembukaan dalam 1 jam
LAST(price ORDER BY trade_ts) AS close, -- Harga penutupan dalam 1 jam
SUM(amount) AS vol -- Total volume transaksi dalam 1 jam
FROM market_trades
WHERE trade_ts >= '2025-06-01 00:00:00' AND trade_ts <= '2025-06-02 00:00:00'
GROUP BY candle_ts, market_id
ORDER BY candle_ts, market_id;Agregasi candlestick tingkat hari
-- Agregasi candlestick tingkat hari
/*+ SET (polar_csi.enable_query on) */
SELECT
time_bucket('1 day', trade_ts) AS candle_ts, -- Data dalam 1 hari
market_id,
MIN(price) AS low, -- Harga terendah dalam 1 hari
MAX(price) AS high, -- Harga tertinggi dalam 1 hari
FIRST(price ORDER BY trade_ts) AS open, -- Harga pembukaan dalam 1 hari
LAST(price ORDER BY trade_ts) AS close, -- Harga penutupan dalam 1 hari
SUM(amount) AS vol -- Total volume transaksi dalam 1 hari
FROM market_trades
WHERE trade_ts >= '2025-06-01 00:00:00' AND trade_ts <= '2025-06-02 00:00:00'
GROUP BY candle_ts, market_id
ORDER BY candle_ts, market_id;Deskripsi SQL
/*+ SET (polar_csi.enable_query on) */: Petunjuk ini memaksa kueri menggunakan rencana eksekusi indeks penyimpanan kolom. Dalam beberapa skenario, pengoptimal mungkin salah menentukan bahwa penyimpanan baris lebih optimal. Anda dapat menggunakanHintini untuk memastikan bahwa kueri menggunakan jalur penyimpanan kolom.time_bucket(bucket_width, ts): Fungsi yang disediakan oleh Time Series Database yang mengelompokkan timestamp (ts) berdasarkan interval waktu tertentu (bucket_width).





