Apakah Redis Diperlukan? PostgreSQL Menyediakan Queuing, Lock, dan Pub/Sub (2021)
(spin.atomicobject.com)- Kombinasi PostgreSQL+Redis yang umum pada layanan web memang praktis, tetapi sebagian dari antrean pekerjaan latar belakang, distributed lock, dan Pub/Sub juga dapat ditangani hanya dengan PostgreSQL
FOR UPDATE SKIP LOCKEDdi PostgreSQL 9.5 melewati baris yang terkunci tanpa menunggu, sehingga dapat dimanfaatkan untuk implementasi antrean yang mencegah beberapa worker mengambil pekerjaan yang sama- Distributed lock di tingkat aplikasi dapat dibuat dengan advisory locks PostgreSQL, dengan menggunakan ulang engine lock internal untuk tujuan yang didefinisikan aplikasi
LISTEN/NOTIFYdi PostgreSQL 9 mendukung langganan kanal string arbitrer dan pengiriman notifikasi, sehingga dapat dipakai sebagai lapisan Pub/Sub; Rails ActionCable juga mendukung penggunaan PostgreSQL secara bawaan- Redis tetap unggul untuk caching TTL dan manipulasi data sementara, tetapi sebagian sistem dapat mengurangi ketergantungan pada Redis untuk menurunkan biaya operasional dan kompleksitas pengembangan
Peran Redis yang Dapat Diserap oleh PostgreSQL
- Layanan web pada umumnya memakai PostgreSQL sebagai penyimpanan data, dan Redis untuk koordinasi antrean pekerjaan latar belakang atau operasi atomik terbatas
- Redis sendiri berguna, tetapi sebagian peran Redis dalam kombinasi ini dapat digantikan hanya dengan fitur PostgreSQL
Antrean pekerjaan latar belakang
- Redis sering dipakai dalam layanan web untuk koordinasi antrean pekerjaan yang meneruskan pekerjaan ke pool worker latar belakang
- Mencatat pekerjaan latar belakang yang harus dijalankan beserta data inputnya
- Harus menjamin hanya satu dari banyak worker yang mengambil pekerjaan tersebut
- Redis cocok untuk kebutuhan ini karena kaya akan operasi atomik pada struktur data
- Mulai PostgreSQL 9.5, opsi
SKIP LOCKEDpada pernyataanSELECT ... FOR ...dapat digunakan- Jika opsi ini ditentukan, PostgreSQL akan mengabaikan baris yang harus menunggu pelepasan lock
- Dengan
FOR UPDATE SKIP LOCKED, lock tingkat baris diperoleh secara implisit atas baris yang dikembalikan - Berkat
SKIP LOCKED, tidak ada kemungkinan terblokir oleh lock dari transaksi lain - Jika ada pekerjaan lain yang perlu diproses, pekerjaan itu akan dikembalikan
- Meskipun beberapa worker menjalankan perintah yang sama, mereka tidak akan menerima baris yang sama karena lock tingkat baris
- Alur dasarnya adalah memilih satu pekerjaan berstatus
pendingdi dalam transaksi, mengubah pekerjaan itu menjadirunning, lalu mengembalikannyaBEGINSELECT ... FOR UPDATE SKIP LOCKEDUPDATE jobs SET status = 'running' ... RETURNING jobs.*COMMIT
- Hal yang perlu diperhatikan adalah, ketika jumlah worker dan pekerjaan sama-sama besar, biaya untuk memindai antrean sambil mencoba memperoleh lock dapat meningkat
- Pada sebagian besar aplikasi yang pernah ditangani, jumlah worker latar belakang kurang dari 12, sehingga kemungkinan biaya ini tidak besar
Lock aplikasi
- Ada kasus ketika hanya satu instance yang boleh berjalan untuk pengguna tertentu di seluruh proses server, misalnya rutinitas yang melakukan sinkronisasi dengan layanan pihak ketiga
- Distributed lock seperti ini adalah kasus penggunaan umum lain untuk Redis
- PostgreSQL dapat mencapai tujuan yang sama dengan
advisory locks- Advisory locks memungkinkan engine lock yang digunakan PostgreSQL secara internal dimanfaatkan untuk tujuan yang didefinisikan aplikasi
Pub/Sub
- Redis juga sering dipakai saat mendorong event ke klien aktif
- Memberi tahu pengguna bahwa pesan baru dapat dibaca
- Melakukan streaming ke klien segera setelah data siap
- Umumnya WebSocket menjadi lapisan pengiriman event, sedangkan Redis berperan sebagai engine Pub/Sub
- Sejak PostgreSQL 9, fitur Pub/Sub disediakan melalui pernyataan
LISTENdanNOTIFY- Klien PostgreSQL dapat berlangganan kanal pesan tertentu yang berupa string arbitrer dengan
LISTEN - Jika klien lain mengirim
NOTIFYke kanal tersebut, semua klien yang sedang berlangganan akan menerima notifikasi - Secara opsional, pesan kecil dapat dilampirkan bersama notifikasi
- Klien PostgreSQL dapat berlangganan kanal pesan tertentu yang berupa string arbitrer dengan
- Jika Anda menggunakan Rails dan ActionCable, penggunaan PostgreSQL didukung secara bawaan
Area yang Masih Layak Mempertahankan Redis
- Redis menangani area yang berbeda dari PostgreSQL, dan kuat pada pekerjaan yang bukan target PostgreSQL
- Caching data dengan TTL
- Penyimpanan dan manipulasi data sementara
- PostgreSQL memiliki cakupan fitur yang luas untuk dianggap sekadar database SQL sederhana atau komponen buram di balik ORM
- Sebagian pekerjaan yang diserahkan ke Redis mungkin juga cocok untuk PostgreSQL
- Menghilangkan Redis dapat menjadi opsi untuk mengurangi biaya operasional dan kompleksitas pengembangan yang muncul saat bergantung pada banyak layanan data
1 komentar
Komentar Hacker News
Banyak orang terlalu ngotot dengan arsitektur yang terlalu terdistribusi, jadi sering tidak melihat keunggulan nyata Redis. Jika dijalankan di mesin yang sama dengan aplikasi, responsnya bisa jauh di bawah 1 milidetik, dan itu memungkinkan aplikasi melakukan hal-hal yang sulit dilakukan dengan Postgres
Memang benar Postgres itu hebat, tetapi ia tidak berjalan di memori pada mesin yang sama dengan aplikasi. Kalau yang dibutuhkan hanya hal seperti antrean, mungkin penyimpanan key-value in-memory tidak diperlukan. Inti dari penyimpanan key-value in-memory adalah menangani pekerjaan yang membutuhkan karakteristik performa RAM, dan karakteristik itu tidak bisa didapat jika harus melewati koneksi jaringan
Tidak ada sihir khusus pada Postgres; ia hanyalah program yang berjalan di proses lain seperti Redis. Pada koneksi lokal, ia memakai jalur cepat untuk mengurangi latensi, dan juga bisa memakai mekanisme transfer data massal yang lebih cepat. Saya sudah beberapa kali memakainya seperti itu
Namun untuk hal seperti alat kerja internal, satu instance tunggal bisa dibiarkan hidup lama, dan cache in-memory ini membuatnya sangat cepat. django-cachalot adalah library yang otomatis menangani invalidasi cache setiap kali ada penulisan ke tabel. Pendekatannya memang agak kasar, tetapi memberi peningkatan performa hampir tanpa usaha, dan aplikasi internal yang jarang diperbarui pada praktiknya hampir berjalan sepenuhnya di RAM lalu hanya kembali ke kueri basis data biasa saat terjadi cache miss
https://github.com/noripyt/django-cachalot
Diskusi di sini banyak berisi argumen defensif dari sudut pandang Redis, tetapi tentu ada area tertentu di mana Redis memang lebih baik
Namun menurut saya itu bukan inti artikelnya. Kalimat kuncinya bisa diringkas sebagai: “PostgreSQL memiliki jauh lebih banyak kemampuan daripada yang Anda perkirakan jika Anda mendekatinya hanya sebagai basis data SQL sederhana atau sesuatu yang tersembunyi di balik ORM.” Jika Anda hanya memakai basis data dari balik ORM, besar kemungkinan Anda melewatkan banyak fitur di basis data mana pun. Jika Anda perlu menambahkan layanan lain seperti Redis, mungkin lebih baik memakai basis data yang sudah Anda siapkan daripada menambah dependensi baru
Memahami apa yang bisa dilakukan Postgres itu bagus. Ini basis data yang kuat
Argumen sebaliknya adalah hambatan untuk memakai Redis sangat rendah, dan imbalannya berupa performa tinggi, dukungan library yang kaya, serta bisa mengurangi beban basis data utama. Misalnya, Anda bisa membuat cache respons API di Postgres. TTL bisa ditangani dengan pekerjaan cron yang menyapu nilai cache lama. Atau ya tinggal pakai Redis
Advisory lock itu keren dan berguna, tetapi jika Anda memakai sesuatu seperti PgBouncer, bisa muncul masalah antara session advisory lock dan transaction interleaving. Sistem terpisah memang punya kekurangan seperti panggilan jaringan, ketersediaan, dan pengetahuan domain, tetapi kompromi sebesar Redis tergolong cukup kecil
Ini artikel yang cukup lama, tetapi sekarang polanya sudah sangat umum. Untuk 90% proyek yang hanya butuh job queue untuk pengiriman email atau pembuatan laporan, mereka tidak memproses jutaan pesan per detik, jadi pendekatan yang menyederhanakan stack layak dipertimbangkan
Saya sering memakai pola seperti ini untuk menghindari masalah yang saya alami di Celery, lalu memisahkannya menjadi framework tersendiri: https://github.com/TkTech/chancy masukan sangat diterima
Ada banyak alat seperti ini, dan beberapa di antaranya bahkan layanan komersial, jadi jelas ada permintaannya
https://worker.graphile.org/ (Node.js)
https://riverqueue.com/ (Go)
https://github.com/acaloiaro/neoq (Go)
https://github.com/contribsys/faktory (Go)
https://github.com/sorentwo/oban (Elixir)
https://github.com/procrastinate-org/procrastinate (Python)
PGQueuer memakai FOR UPDATE SKIP LOCKED dan LISTEN/NOTIFY dari PostgreSQL untuk menyediakan job queue, lock, dan notifikasi real-time
Jika Anda sudah memakai PostgreSQL, ini adalah alternatif minimalis yang tidak memerlukan Redis
https://github.com/janbjorge/PGQueuer
Sekadar catatan, ini buatan saya
Saya suka Postgres, tetapi ada beberapa keterbatasan
Jika Anda butuh penyimpanan key-value, Anda perlu mempertimbangkan apakah memahami autovacuum, mengetahui batas connection pool, dan apakah yang diinginkan adalah throughput atau keamanan. Jika butuh queue, lihat apakah pemrosesannya berurutan, ada rate limit, fan-out, atau pemisahan per topik. Jika butuh publish/subscribe, pertimbangkan apakah penerimaan duplikat menjadi masalah, apakah kehilangan pesan menjadi masalah, dan apakah replay diperlukan. Jika butuh locking, Anda perlu mengetahui batas connection pool dan statement_timeout. Sebagian besar masalah di atas memang bisa diatasi, tetapi tidak sesederhana itu
Hambatan besar dalam publish/subscribe Postgres adalah ukuran maksimum pesan hanya 8000 byte
Solusi sementara yang disarankan adalah menaruh data ke tabel lalu hanya mengirim ID-nya, tetapi jika tidak ingin data itu tersimpan selamanya, Anda harus melakukan garbage collection terhadap data tersebut, dan tiap pesan menambah pekerjaan. Tentu ini bisa cocok dalam beberapa kasus, tetapi untuk banyak kasus penggunaan Redis, batasan ini membuatnya sulit dianggap setara
Mari lihat apakah pgsql bisa menangani 15.000 koneksi klien
Seseorang dari Planetscale mengatakan di podcast bahwa instance MySQL milik GitHub masing-masing menangani lebih dari 50 ribu koneksi. Sebaliknya, di Postgres, jika membutuhkan lebih dari 100 koneksi, biasanya Anda sudah memerlukan PgBouncer
Queue, lock, dan publish/subscribe memang bisa dilakukan. Tetapi penggunaan Redis yang paling penting, yaitu caching, tidak ada di sini
Update di Postgres terkenal lebih mahal daripada insert, menghasilkan sampah, dan memerlukan vacuum. Karena jaminan durabilitas yang sebenarnya tidak terlalu penting untuk caching, penulisan juga menjadi cukup lambat. Kedaluwarsa otomatis sangat praktis dan membantu mengurangi kesalahan
Synchronous commit, autovacuum, dan lainnya juga bisa dimatikan. Tentu Redis masih akan lebih cepat, tetapi perbedaannya mungkin tidak cukup penting untuk diperhatikan oleh perusahaan pada umumnya
Jika inti tulisannya mau disampaikan lebih jelas, mulailah dengan Postgres lalu pindah ke Redis saat benar-benar diperlukan
Sebaiknya jumlah komponen yang bergerak dijaga sesedikit mungkin