Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

Baru-baru ini saya memberi tahu Anda caranya, menggunakan resep standar meningkatkan kinerja kueri baca SQL dari basis data PostgreSQL. Hari ini kita akan berbicara tentang caranya pencatatan dapat dilakukan dengan lebih efisien dalam database tanpa menggunakan "twist" apa pun dalam konfigurasi - cukup dengan mengatur aliran data dengan benar.

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

#1. Pembagian

Sebuah artikel tentang bagaimana dan mengapa hal ini layak untuk diorganisir partisi yang diterapkan “secara teori” sudah ada, di sini kita akan berbicara tentang praktik penerapan beberapa pendekatan dalam diri kita layanan pemantauan untuk ratusan server PostgreSQL.

"Hal-hal di masa lalu..."

Awalnya, seperti MVP lainnya, proyek kami dimulai dengan beban yang cukup ringan - pemantauan dilakukan hanya untuk selusin server paling kritis, semua tabel relatif kompak... Namun seiring berjalannya waktu, jumlah host yang dipantau menjadi lebih banyak dan banyak lagi, dan sekali lagi kami mencoba melakukan sesuatu dengan salah satunya tabel berukuran 1.5TB, kami menyadari bahwa meskipun mungkin untuk terus hidup seperti ini, hal ini sangat merepotkan.

Saat-saat hampir seperti masa-masa epik, versi PostgreSQL 9.x yang berbeda relevan, jadi semua partisi harus dilakukan "secara manual" - melalui pewarisan tabel dan pemicu perutean dengan dinamis EXECUTE.

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB
Solusi yang dihasilkan ternyata cukup universal sehingga bisa diterjemahkan ke semua tabel:

  • Tabel induk “header” kosong dideklarasikan, yang menjelaskan semuanya indeks dan pemicu yang diperlukan.
  • Catatan dari sudut pandang klien dibuat di tabel "root", dan digunakan secara internal pemicu perutean BEFORE INSERT catatan itu "secara fisik" dimasukkan ke bagian yang diinginkan. Jika belum ada hal seperti itu, kami mendapat pengecualian dan...
  • … dengan menggunakan CREATE TABLE ... (LIKE ... INCLUDING ...) dibuat berdasarkan templat tabel induk bagian dengan batasan pada tanggal yang diinginkansehingga ketika data diambil, pembacaan hanya dilakukan di dalamnya.

PG10: percobaan pertama

Namun mempartisi melalui pewarisan secara historis tidak terlalu cocok untuk bekerja dengan aliran tulis aktif atau partisi turunan dalam jumlah besar. Misalnya, Anda dapat mengingat bahwa ada algoritma untuk memilih bagian yang diperlukan kompleksitas kuadrat, yang berfungsi dengan 100+ bagian, Anda sendiri memahami caranya...

Di PG10 situasi ini sangat dioptimalkan dengan menerapkan dukungan partisi asli. Oleh karena itu, kami langsung mencoba menerapkannya segera setelah migrasi penyimpanan, namun...

Ternyata setelah menggali manual, tabel yang dipartisi secara asli dalam versi ini adalah:

  • tidak mendukung deskripsi indeks
  • tidak mendukung pemicu di dalamnya
  • tidak bisa menjadi “keturunan” siapa pun
  • tidak mendukung INSERT ... ON CONFLICT
  • tidak dapat menghasilkan bagian secara otomatis

Setelah menerima pukulan menyakitkan di dahi dengan penggaruk, kami menyadari bahwa tidak mungkin dilakukan tanpa memodifikasi aplikasi, dan menunda penelitian lebih lanjut selama enam bulan.

PG10: kesempatan kedua

Jadi, kami mulai menyelesaikan masalah yang muncul satu per satu:

  1. Karena pemicu dan ON CONFLICT Kami menyadari bahwa kami masih membutuhkannya di sana-sini, jadi kami membuat tahap peralihan untuk menyelesaikannya tabel proksi.
  2. Singkirkan "perutean" di pemicu - yaitu, dari EXECUTE.
  3. Mereka mengeluarkannya secara terpisah tabel templat dengan semua indekssehingga mereka bahkan tidak ada di tabel proxy.

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB
Akhirnya, setelah semua ini, kami mempartisi tabel utama secara asli. Pembuatan bagian baru masih bergantung pada hati nurani aplikasi.

Kamus "Menggergaji".

Seperti dalam sistem analitis lainnya, kami juga memilikinya "fakta" dan "potongan" (kamus). Dalam kasus kami, dalam kapasitas ini mereka bertindak, misalnya, badan templat kueri lambat serupa atau teks kueri itu sendiri.

“Fakta” ​​sudah lama dipecah berdasarkan hari, jadi kami dengan tenang menghapus bagian yang sudah ketinggalan zaman, dan itu tidak mengganggu kami (log!). Tapi ada masalah dengan kamus...

Bukan berarti jumlahnya banyak, tapi kira-kira 100 TB “fakta” ​​menghasilkan kamus 2.5 TB. Anda tidak dapat dengan mudah menghapus apa pun dari tabel seperti itu, Anda tidak dapat mengompresinya dalam waktu yang cukup, dan menulis ke dalamnya secara bertahap menjadi lebih lambat.

Seperti kamus... di dalamnya, setiap entri harus disajikan tepat satu kali... dan ini benar, tapi!.. Tidak ada yang menghentikan kita untuk memilikinya kamus terpisah untuk setiap hari! Ya, hal ini menimbulkan redundansi tertentu, namun memungkinkan:

  • menulis/membaca lebih cepat karena ukuran bagian yang lebih kecil
  • mengkonsumsi lebih sedikit memori dengan bekerja dengan indeks yang lebih kompak
  • menyimpan lebih sedikit data karena kemampuan untuk dengan cepat menghapus usang

Sebagai hasil dari seluruh tindakan yang kompleks Beban CPU berkurang ~30%, beban disk sebesar ~50%:

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB
Pada saat yang sama, kami terus menulis hal yang persis sama ke dalam database, hanya dengan beban yang lebih sedikit.

#2. Evolusi dan pemfaktoran ulang basis data

Jadi kami memutuskan apa yang kami miliki setiap hari memiliki bagiannya sendiri dengan data. Sebenarnya, CHECK (dt = '2018-10-12'::date) — dan ada kunci partisi dan syarat agar rekaman masuk ke bagian tertentu.

Karena semua laporan di layanan kami dibuat dalam konteks tanggal tertentu, indeks untuk laporan tersebut sejak “masa tidak dipartisi” adalah semua jenis (Server, Tanggal, Templat Rencana), (Server, Tanggal, simpul rencana), (Tanggal, Kelas kesalahan, Server), ...

Tapi sekarang mereka tinggal di setiap bagian salinan Anda setiap indeks tersebut... Dan dalam setiap bagian tanggal adalah sebuah konstanta... Ternyata sekarang kita berada di setiap indeks tersebut cukup masukkan konstanta sebagai salah satu bidang yang meningkatkan volume dan waktu pencariannya, tetapi tidak membawa hasil apa pun. Mereka meninggalkan penggaruk untuk diri mereka sendiri, ups...

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB
Arah optimasinya jelas - sederhana hapus bidang tanggal dari semua indeks pada tabel yang dipartisi. Mengingat volume kami, keuntungannya sangat besar 1TB/minggu!

Sekarang perhatikan bahwa terabyte ini masih harus direkam. Artinya, kami juga disk sekarang seharusnya memuat lebih sedikit! Gambar ini dengan jelas menunjukkan efek yang diperoleh dari pembersihan, yang kami curahkan selama seminggu:

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

#3. “Menyebarkan” beban puncak

Salah satu masalah besar dari sistem yang dimuat adalah sinkronisasi berlebihan beberapa operasi yang tidak memerlukannya. Terkadang “karena mereka tidak menyadarinya”, terkadang “lebih mudah seperti itu”, tetapi cepat atau lambat Anda harus menghilangkannya.

Mari kita perbesar gambar sebelumnya dan lihat bahwa kita memiliki disk "pompa" di bawah beban dengan amplitudo ganda antara sampel yang berdekatan, yang jelas “secara statistik” tidak boleh terjadi dengan sejumlah operasi seperti itu:

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

Hal ini cukup mudah untuk dicapai. Kami sudah mulai memantau hampir 1000 server, masing-masing diproses oleh thread logis terpisah, dan setiap thread mengatur ulang akumulasi informasi untuk dikirim ke database pada frekuensi tertentu, kira-kira seperti ini:

setInterval(sendToDB, interval)

Masalahnya di sini justru terletak pada kenyataan itu semua thread dimulai pada waktu yang hampir bersamaan, jadi waktu pengirimannya hampir selalu bertepatan dengan “to the point”. Ups #2...

Untungnya, hal ini cukup mudah untuk diperbaiki, menambahkan run-up "acak". Oleh waktu:

setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))

#4. Kami menyimpan apa yang kami butuhkan dalam cache

Masalah beban tinggi tradisional yang ketiga adalah tidak ada cache dimana dia bisa menjadi.

Misalnya, kami memungkinkan untuk menganalisis dalam hal node rencana (semua ini Seq Scan on users), tetapi langsung berpikir bahwa sebagian besar mereka sama - mereka lupa.

Tidak, tentu saja, tidak ada yang ditulis ke database lagi, ini memutus pemicunya INSERT ... ON CONFLICT DO NOTHING. Namun data ini masih masuk ke database, dan itu tidak diperlukan membaca untuk memeriksa konflik harus dilakukan. Ups #3...

Perbedaan jumlah record yang dikirim ke database sebelum/sesudah caching diaktifkan terlihat jelas:

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

Dan ini adalah penurunan beban penyimpanan yang menyertainya:

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

Total

“Terabyte-per-hari” terdengar menakutkan. Jika Anda melakukan segalanya dengan benar, maka ini adil 2^40 byte / 86400 detik = ~12.5MB/dtkyang bahkan dapat dipegang oleh sekrup IDE desktop. 🙂

Tapi serius, bahkan dengan beban "condong" sepuluh kali lipat di siang hari, Anda dapat dengan mudah memenuhi kemampuan SSD modern.

Kami menulis dalam PostgreSQL di sublight: 1 host, 1 hari, 1TB

Sumber: www.habr.com

Tambah komentar