क्या Redis की ज़रूरत है? PostgreSQL क्यूइंग, लॉक और Pub/Sub देता है (2021)
(spin.atomicobject.com)- वेब सेवाओं में आम PostgreSQL+Redis कॉम्बिनेशन सुविधाजनक है, लेकिन background job queue, distributed locks और Pub/Sub में से कुछ काम सिर्फ PostgreSQL से भी संभाले जा सकते हैं
- PostgreSQL 9.5 का
FOR UPDATE SKIP LOCKEDlocked rows का इंतज़ार किए बिना उन्हें छोड़ देता है, इसलिए इसे ऐसी queue implementation में इस्तेमाल किया जा सकता है जो कई workers को वही job उठाने से रोकती है - application-level distributed locks PostgreSQL के advisory locks से बनाए जा सकते हैं, जो internal lock engine को application-defined उद्देश्यों के लिए reuse करते हैं
- PostgreSQL 9 के
LISTEN/NOTIFYarbitrary string channels को subscribe करने और notifications भेजने की सुविधा देते हैं, इसलिए इन्हें Pub/Sub layer की तरह इस्तेमाल किया जा सकता है; Rails ActionCable भी PostgreSQL इस्तेमाल को default रूप से support करता है - Redis की TTL caching और temporary data manipulation में अब भी मजबूत पकड़ है, लेकिन कुछ systems Redis dependency घटाकर operational cost और development complexity कम कर सकते हैं
Redis की वे भूमिकाएँ जिन्हें PostgreSQL में समाहित किया जा सकता है
- आम वेब services PostgreSQL को data store के रूप में इस्तेमाल करती हैं और Redis को background job queue coordination या सीमित atomic operations के लिए इस्तेमाल करती हैं
- Redis अपने-आप में उपयोगी है, लेकिन इस कॉम्बिनेशन में Redis की कुछ भूमिकाएँ सिर्फ PostgreSQL features से भी बदली जा सकती हैं
Background job queue
- Redis वेब services में background worker pool तक jobs पहुँचाने के लिए job queue coordination में अक्सर इस्तेमाल होता है
- चलाए जाने वाले background jobs और input data को record करता है
- यह guarantee करनी होती है कि कई workers में से सिर्फ एक ही उस job को उठाए
- Redis data structures पर atomic operations में समृद्ध है, इसलिए इस use case के लिए अच्छी तरह fit बैठता है
- PostgreSQL 9.5 से
SELECT ... FOR ...statement मेंSKIP LOCKEDoption इस्तेमाल किया जा सकता है- यह option देने पर PostgreSQL उन rows को ignore करता है जिनके lock release होने का इंतज़ार करना पड़े
FOR UPDATE SKIP LOCKEDदेने पर returned rows पर row-level lock implicitly acquire हो जाता हैSKIP LOCKEDकी वजह से दूसरे transactions के locks पर blocking होने की संभावना नहीं रहती- अगर process करने के लिए कोई दूसरी job है, तो वही job return होती है
- कई workers वही command चलाएँ, तब भी row-level lock की वजह से उन्हें वही row नहीं मिलती
- basic flow यह है कि transaction के अंदर
pendingstatus वाली एक job चुनी जाती है, उस job कोrunningमें बदला जाता है और फिर return किया जाता हैBEGINSELECT ... FOR UPDATE SKIP LOCKEDUPDATE jobs SET status = 'running' ... RETURNING jobs.*COMMIT
- ध्यान देने वाली बात यह है कि जब workers और jobs दोनों की संख्या बहुत अधिक हो, तो queue scan करते हुए locks acquire करने की कोशिश की cost बढ़ सकती है
- व्यवहार में, जिन ज्यादातर apps को संभाला गया उनमें background workers 12 से कम थे, इसलिए यह cost बहुत बड़ी न होने की संभावना थी
Application locks
- third-party service के साथ sync करने वाली routines जैसे cases में, सभी server processes में किसी खास user के लिए सिर्फ एक instance चलना चाहिए
- ऐसे distributed locks Redis का एक और आम use case हैं
- PostgreSQL
advisory locksसे वही उद्देश्य हासिल कर सकता है- advisory locks PostgreSQL के internally इस्तेमाल होने वाले lock engine को application-defined उद्देश्यों के लिए इस्तेमाल करने देते हैं
Pub/Sub
- active clients तक events push करने के लिए भी Redis अक्सर इस्तेमाल होता है
- user को notify करना कि नया message पढ़ा जा सकता है
- data तैयार होते ही client तक stream करना
- आम तौर पर WebSocket event delivery layer होता है और Redis Pub/Sub engine की भूमिका निभाता है
- PostgreSQL 9 से
LISTENऔरNOTIFYstatements से Pub/Sub functionality देता है- PostgreSQL client
LISTENसे किसी arbitrary string वाले specific message channel को subscribe कर सकता है - कोई दूसरा client उस channel पर
NOTIFYभेजे, तो subscribed सभी clients को notification मिलता है - optionally एक छोटा message साथ attach किया जा सकता है
- PostgreSQL client
- अगर आप Rails और ActionCable इस्तेमाल कर रहे हैं, तो PostgreSQL का इस्तेमाल default रूप से supported है
Redis को बनाए रखने लायक क्षेत्र
- Redis PostgreSQL से अलग क्षेत्र संभालता है और उन कामों में मजबूत है जिन्हें PostgreSQL target नहीं करता
- TTL वाली data caching
- temporary data storage और manipulation
- PostgreSQL को सिर्फ एक simple SQL database या ORM के पीछे छिपे opaque component के रूप में देखने के लिए इसकी feature breadth बहुत बड़ी है
- Redis को सौंपे गए कुछ काम PostgreSQL के लिए भी उपयुक्त हो सकते हैं
- Redis को छोड़ना, कई data services पर निर्भरता से पैदा होने वाली operational cost और development complexity घटाने का एक विकल्प हो सकता है
1 टिप्पणियां
Hacker News की राय
हर कोई ज़रूरत से ज़्यादा distributed architecture पर अड़ा रहता है, इसलिए अक्सर Redis के असली फायदे नहीं दिखते। अगर इसे application वाली ही मशीन पर चलाया जाए, तो यह 1 millisecond से भी काफी कम समय में response दे सकता है, और इसलिए application में वे काम किए जा सकते हैं जो Postgres से करना मुश्किल है
Postgres बेहतरीन है, यह सही है, लेकिन वह application वाली ही मशीन की memory में नहीं चलता। अगर आपको सिर्फ queue जैसी चीज़ें चाहिए, तो शायद in-memory key-value store की ज़रूरत न हो। in-memory key-value store का मुख्य मतलब वे काम करना है जिनके लिए RAM की performance characteristics चाहिए होती हैं, और network connection के पार आपको वे characteristics नहीं मिलतीं
Postgres में कोई खास जादू नहीं है; Redis की तरह वह भी किसी दूसरे process में चलने वाला program ही है। local connection में latency घटाने के लिए तेज pipe इस्तेमाल किए जाते हैं, और bulk data transfer के और तेज तरीके भी इस्तेमाल किए जा सकते हैं। मैंने इस तरह कई बार इस्तेमाल किया है
लेकिन internal business tools जैसे मामलों में एक single instance को लंबे समय तक बढ़ाकर चलाया जा सकता है, और इस in-memory cache की वजह से वह बहुत तेज हो जाता है। django-cachalot एक library है जो table में write होते ही cache invalidation को अपने-आप handle करती है। तरीका थोड़ा rough है, लेकिन लगभग बिना मेहनत performance boost देता है, और जिन internal business apps में updates कम होते हैं वे असल में RAM से ही चलती हैं और cache miss होने पर ही सामान्य database query पर लौटती हैं
https://github.com/noripyt/django-cachalot
यहां चर्चा में Redis के पक्ष से काफी defensive बातें हैं, लेकिन बेशक Redis जिन specific areas में बेहतर है, वे मौजूद हैं
फिर भी मुझे नहीं लगता कि लेख का core point वह है। core sentence को ऐसे summarize किया जा सकता है: “PostgreSQL में उससे कहीं ज्यादा features हैं जितनी आप उम्मीद करते हैं, अगर आप उसे सिर्फ एक SQL database या ORM के पीछे छिपी किसी रहस्यमय चीज़ की तरह देखते हैं।” अगर आप database को सिर्फ ORM के पीछे इस्तेमाल कर रहे हैं, तो किसी भी database की capabilities छूटने की संभावना बड़ी है। अगर Redis जैसी कोई दूसरी service जोड़नी पड़ रही है, तो नई dependency जोड़ने के बजाय पहले से configured database इस्तेमाल करना बेहतर हो सकता है
Postgres क्या-क्या कर सकता है, यह समझना अच्छी बात है। यह powerful database है
counterargument यह है कि Redis इस्तेमाल करने की barrier बहुत low है, और बदले में performance high मिलती है, library support भरपूर है, और primary database का load कम किया जा सकता है। उदाहरण के लिए API response cache Postgres से बनाया जा सकता है। TTL के लिए cron job से पुराने cache values साफ किए जा सकते हैं। या फिर बस Redis इस्तेमाल कर लें
advisory locks शानदार और उपयोगी हैं, लेकिन PgBouncer जैसी चीज़ इस्तेमाल करनी हो तो session advisory locks और transaction interleaving के बीच समस्याएं आ सकती हैं। अलग system में network calls, availability, domain knowledge जैसी कमियां होती हैं, लेकिन Redis जितना compromise काफी छोटा है
लेख काफी पुराना है, लेकिन अब यह बहुत common pattern बन चुका है। जिन 90% projects को email भेजने या report generation के लिए सिर्फ job queue चाहिए होती है, वे प्रति सेकंड लाखों messages process नहीं करते, इसलिए stack को सरल बनाने वाला तरीका विचार करने लायक है
Celery में आई समस्याओं को bypass करने के लिए मैं यह pattern अक्सर इस्तेमाल करता था, फिर इसे अलग framework के रूप में निकाल दिया: https://github.com/TkTech/chancy feedback स्वागत है
ऐसे tools बहुत हैं, और उनमें से कुछ commercial services हैं, इसलिए demand साफ दिखती है
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 PostgreSQL के FOR UPDATE SKIP LOCKED और LISTEN/NOTIFY का इस्तेमाल करके job queue, locks और real-time notifications देता है
अगर आप पहले से PostgreSQL इस्तेमाल कर रहे हैं, तो यह Redis की ज़रूरत खत्म करने वाला minimal alternative है
https://github.com/janbjorge/PGQueuer
जानकारी के लिए, यह मैंने बनाया है
Postgres पसंद है, लेकिन इसकी कुछ सीमाएँ हैं
अगर आपको key-value store चाहिए, तो देखना होगा कि क्या आप autovacuum को समझते हैं, connection pool की सीमाएँ जानते हैं, और throughput व safety में से क्या चाहते हैं। अगर queue चाहिए, तो देखना होगा कि वह sequential processing है या नहीं, rate limit है या नहीं, fan-out है या नहीं, और topic-wise separation चाहिए या नहीं। अगर publish/subscribe चाहिए, तो सोचना होगा कि duplicate receive की परवाह है या नहीं, message loss की परवाह है या नहीं, और replay चाहिए या नहीं। अगर lock चाहिए, तो connection pool की सीमा और statement_timeout समझना होगा। ऊपर की ज़्यादातर समस्याएँ हल की जा सकती हैं, लेकिन यह उतना सरल नहीं है
Postgres publish/subscribe में बड़ा अड़ंगा यह है कि message का अधिकतम size 8000 bytes है
सुझाया गया workaround यह है कि data को table में डालें और सिर्फ ID भेजें, लेकिन अगर आप उसे हमेशा के लिए नहीं रखना चाहते, तो उस data की garbage collection करनी पड़ेगी और हर message पर अतिरिक्त काम जुड़ जाएगा। बेशक कुछ मामलों में यह ठीक हो सकता है, लेकिन Redis के कई use cases में इस सीमा के कारण इसे बराबर मानना मुश्किल है
देखें कि क्या pgsql 15,000 client connections संभालता है
Planetscale के किसी व्यक्ति ने podcast में कहा था कि GitHub के MySQL instances में से प्रत्येक 50,000 से ज़्यादा connections संभालता है। दूसरी ओर, Postgres में अगर 100 से ज़्यादा connections चाहिए, तो आपको पहले से ही PgBouncer की ज़रूरत पड़ जाती है
queues, locks, और publish/subscribe संभव हैं। लेकिन Redis का सबसे अहम use case, caching, इसमें छूटा हुआ है
Postgres updates insertion की तुलना में महंगे होने के लिए कुख्यात हैं, garbage बनाते हैं और vacuum की ज़रूरत होती है। caching के लिए गैर-ज़रूरी durability guarantees के कारण writes भी काफी धीमी हो जाती हैं। automatic expiration बहुत सुविधाजनक है और गलतियों को कम करती है
synchronous commit, autovacuum आदि भी बंद किए जा सकते हैं। बेशक Redis फिर भी तेज़ रहेगा, लेकिन यह अंतर शायद इतना न हो कि किसी सामान्य company को उसकी चिंता करनी पड़े
लेख का सार और साफ़ कहें तो, Postgres से शुरू करें और ज़रूरत पड़ने पर Redis पर move कर जाएँ
moving parts की संख्या जितनी हो सके उतनी कम रखना बेहतर है