Benar-benar tidak ada 'cara terbaik' untuk menyimpan data deret waktu, dan itu jujur tergantung pada sejumlah faktor. Namun, saya akan fokus pada dua faktor terutama, dengan mereka adalah:
(1) Seberapa seriuskah proyek ini sehingga layak Anda upayakan untuk mengoptimalkan skema?
(2) Apa yang pola akses query Anda benar-benar akan menjadi seperti?
Dengan pertanyaan-pertanyaan itu dalam pikiran, mari kita bahas beberapa opsi skema.
Meja datar
Opsi untuk menggunakan tabel datar lebih banyak terkait dengan pertanyaan (1) , di mana jika ini bukan proyek serius atau skala besar, Anda akan merasa jauh lebih mudah untuk tidak terlalu memikirkan skema, dan cukup gunakan tabel datar, seperti:
CREATE flat_table(
trip_id integer,
tstamp timestamptz,
speed float,
distance float,
temperature float,
,...);
Tidak banyak kasus di mana saya akan merekomendasikan kursus ini, hanya jika ini adalah proyek kecil yang tidak menjamin banyak waktu Anda.
Dimensi dan Fakta
Jadi, jika Anda telah mengatasi rintangan pertanyaan (1) , dan Anda menginginkan skema kinerja yang lebih banyak, ini adalah salah satu opsi pertama yang perlu dipertimbangkan. Ini mencakup beberapa normailisasi dasar, tetapi mengekstraksi kuantitas 'dimensi' dari kuantitas 'fakta' yang terukur.
Pada dasarnya, Anda akan menginginkan tabel untuk merekam info tentang perjalanan,
CREATE trips(
trip_id integer,
other_info text);
dan meja untuk mencatat cap waktu,
CREATE tstamps(
tstamp_id integer,
tstamp timestamptz);
dan akhirnya semua fakta terukur Anda, dengan referensi kunci asing ke tabel dimensi (yaitu meas_facts(trip_id)
referensi trips(trip_id)
& meas_facts(tstamp_id)
referensi tstamps(tstamp_id)
)
CREATE meas_facts(
trip_id integer,
tstamp_id integer,
speed float,
distance float,
temperature float,
,...);
Ini mungkin kelihatannya tidak terlalu membantu pada awalnya, tetapi jika Anda memiliki misalnya ribuan perjalanan bersamaan, maka mereka semua mungkin melakukan pengukuran sekali per detik, pada yang kedua. Dalam hal ini, Anda harus merekam ulang cap waktu setiap kali untuk setiap perjalanan, daripada hanya menggunakan satu entri dalam tstamps
tabel.
Use case: Case ini akan baik jika ada banyak perjalanan bersamaan yang Anda rekam data, dan Anda tidak keberatan mengakses semua tipe pengukuran secara bersamaan.
Karena Postgres dibaca oleh baris, kapan pun Anda inginkan, misalnya, speed
pengukuran pada rentang waktu tertentu, Anda harus membaca seluruh baris dari meas_facts
tabel, yang pasti akan memperlambat kueri, meskipun jika kumpulan data yang Anda kerjakan adalah tidak terlalu besar, maka Anda bahkan tidak akan melihat perbedaannya.
Memisahkan Fakta Yang Terukur Anda
Untuk memperpanjang bagian terakhir hanya sedikit lebih jauh, Anda dapat memecah pengukuran Anda menjadi tabel yang terpisah, di mana misalnya saya akan menunjukkan tabel untuk kecepatan dan jarak:
CREATE speed_facts(
trip_id integer,
tstamp_id integer,
speed float);
dan
CREATE distance_facts(
trip_id integer,
tstamp_id integer,
distance float);
Tentu saja, Anda dapat melihat bagaimana ini dapat diperluas ke pengukuran lainnya.
Use case: Jadi ini tidak akan memberi Anda kecepatan luar biasa untuk sebuah kueri, mungkin hanya peningkatan linear dalam kecepatan ketika Anda bertanya tentang satu tipe pengukuran. Ini karena ketika Anda ingin mencari info tentang kecepatan, Anda hanya perlu membaca baris dari speed_facts
tabel, daripada semua info tambahan yang tidak dibutuhkan yang akan hadir di deretan meas_facts
tabel.
Jadi, Anda perlu membaca bulks besar data tentang satu jenis pengukuran saja, Anda bisa mendapatkan beberapa manfaat. Dengan kasus data 10 jam yang diusulkan pada interval satu detik, Anda hanya akan membaca 36.000 baris, sehingga Anda tidak akan pernah benar-benar menemukan manfaat yang signifikan dari melakukan ini. Namun, jika Anda ingin melihat data pengukuran kecepatan untuk 5.000 perjalanan yang semuanya sekitar 10 jam, sekarang Anda sedang membaca 180 juta baris. Peningkatan kecepatan linear untuk kueri semacam itu dapat menghasilkan beberapa manfaat, asalkan Anda hanya perlu mengakses satu atau dua tipe pengukuran sekaligus.
Array / HStore / & TOAST
Anda mungkin tidak perlu khawatir tentang bagian ini, tetapi saya tahu kasus di mana itu penting. Jika Anda perlu mengakses sejumlah besar data deret waktu, dan Anda tahu Anda perlu mengakses semuanya dalam satu blok besar, Anda dapat menggunakan struktur yang akan menggunakan TOAST Tables , yang pada dasarnya menyimpan data Anda dalam ukuran yang lebih besar, terkompresi segmen. Ini mengarah ke akses yang lebih cepat ke data, selama tujuan Anda adalah untuk mengakses semua data.
Salah satu contoh implementasi bisa
CREATE uber_table(
trip_id integer,
tstart timestamptz,
speed float[],
distance float[],
temperature float[],
,...);
Dalam tabel ini, tstart
akan menyimpan cap waktu untuk entri pertama dalam array, dan setiap entri berikutnya akan menjadi nilai pembacaan untuk detik berikutnya. Ini mengharuskan Anda untuk mengelola cap waktu yang relevan untuk setiap nilai array dalam perangkat lunak aplikasi.
Kemungkinan lain adalah
CREATE uber_table(
trip_id integer,
speed hstore,
distance hstore,
temperature hstore,
,...);
di mana Anda menambahkan nilai pengukuran Anda sebagai (kunci, nilai) pasangan (cap waktu, pengukuran).
Use case: Ini adalah implementasi yang mungkin lebih baik diserahkan kepada seseorang yang lebih nyaman dengan PostgreSQL, dan hanya jika Anda yakin tentang pola akses Anda yang membutuhkan pola akses massal.
Kesimpulannya?
Wow, ini jauh lebih lama dari yang saya harapkan, maaf. :)
Pada dasarnya, ada sejumlah opsi, tetapi Anda mungkin akan mendapatkan keuntungan terbesar dengan menggunakan yang kedua atau ketiga, karena cocok dengan case yang lebih umum.
PS: Pertanyaan awal Anda menyiratkan bahwa Anda akan memuat data Anda secara massal setelah semuanya dikumpulkan. Jika Anda mengalirkan data ke instance PostgreSQL Anda, Anda perlu melakukan beberapa pekerjaan lebih lanjut untuk menangani baik konsumsi data maupun beban kerja kueri, tetapi kami akan membiarkannya untuk lain waktu. ;)