Redis’e gerek var mı? PostgreSQL kuyruklama, kilitleme ve Pub/Sub sunuyor (2021)
(spin.atomicobject.com)- Web servislerinde yaygın olan PostgreSQL+Redis kombinasyonu kullanışlıdır; ancak arka plan iş kuyruğu, dağıtık kilit ve Pub/Sub gibi görevlerin bir kısmı yalnızca PostgreSQL ile de çözülebilir
- PostgreSQL 9.5’teki
FOR UPDATE SKIP LOCKED, kilitli satırları beklemeden atlayarak birden fazla worker’ın aynı işi almasını engelleyen kuyruk uygulamalarında kullanılabilir - Uygulama düzeyindeki dağıtık kilitler, PostgreSQL’in advisory locks özelliğiyle oluşturulabilir; bu, dahili kilit motorunu uygulama tarafından tanımlanan amaçlar için yeniden kullanır
- PostgreSQL 9’daki
LISTEN/NOTIFY, rastgele metin kanallarına aboneliği ve bildirim göndermeyi destekleyerek Pub/Sub katmanı olarak kullanılabilir; Rails ActionCable da PostgreSQL kullanımını varsayılan olarak destekler - Redis, TTL önbellekleme ve geçici veri işleme konusunda hâlâ güçlüdür; ancak bazı sistemler Redis bağımlılığını azaltarak operasyonel maliyetleri ve geliştirme karmaşıklığını düşürebilir
PostgreSQL’in üstlenebileceği Redis rolleri
- Tipik web servisleri PostgreSQL’i veri deposu olarak, Redis’i ise arka plan iş kuyruğunu koordine etmek veya sınırlı atomik işlemler için kullanır
- Redis’in kendisi faydalıdır; ancak bu kombinasyonda Redis’in üstlendiği bazı roller yalnızca PostgreSQL özellikleriyle de ikame edilebilir
Arka plan iş kuyruğu
- Redis, web servislerinde işleri arka plan worker havuzuna ileten iş kuyruğu koordinasyonu için sık kullanılır
- Çalıştırılması gereken arka plan işlerini ve girdi verilerini kaydeder
- Birden fazla worker’dan yalnızca birinin ilgili işi almasını garanti etmesi gerekir
- Redis, veri yapıları üzerinde zengin atomik işlemler sunduğu için bu kullanım için uygundur
- PostgreSQL 9.5’ten itibaren
SELECT ... FOR ...ifadesindeSKIP LOCKEDseçeneği kullanılabilir- Bu seçenek belirtildiğinde PostgreSQL, kilidin serbest bırakılmasını beklemesi gereken satırları yok sayar
FOR UPDATE SKIP LOCKEDbelirtildiğinde dönen satırlar için satır düzeyi kilit örtük olarak alınırSKIP LOCKEDsayesinde başka işlemlerin kilitleri yüzünden bloklanma olasılığı yoktur- İşlenecek başka iş varsa o iş döndürülür
- Birden fazla worker aynı komutu çalıştırsa bile satır düzeyi kilit nedeniyle aynı satırı almaz
- Temel akış, bir transaction içinde
pendingdurumundaki tek bir işi seçmek, bu işirunningdurumuna geçirmek ve ardından döndürmek şeklindedirBEGINSELECT ... FOR UPDATE SKIP LOCKEDUPDATE jobs SET status = 'running' ... RETURNING jobs.*COMMIT
- Dikkat edilmesi gereken nokta, hem worker hem de iş sayısı çok fazla olduğunda kuyruğu tarayıp kilit almaya çalışmanın maliyetinin artabilmesidir
- Gerçekte ele alınan çoğu uygulamada arka plan worker sayısı 12’den azdı; bu nedenle bu maliyetin büyük olmama olasılığı yüksekti
Uygulama kilitleri
- Üçüncü taraf servislerle senkronizasyon yapan rutinlerde olduğu gibi, tüm sunucu süreçleri arasında belirli bir kullanıcı için yalnızca tek bir örneğin çalışmasını gerektiren durumlar olabilir
- Bu tür dağıtık kilitler, Redis’in bir başka yaygın kullanım alanıdır
- PostgreSQL,
advisory locksile aynı amaca ulaşabilir- advisory locks, PostgreSQL’in dahili olarak kullandığı kilit motorunu uygulama tarafından tanımlanan amaçlar için kullanmayı sağlar
Pub/Sub
- Etkin istemcilere olay push etmek için de Redis sık kullanılır
- Kullanıcıya yeni bir mesajı okuyabileceğini bildirir
- Veriler hazır oldukça istemciye stream eder
- Genellikle WebSocket olay iletim katmanıdır, Redis ise Pub/Sub motoru rolünü üstlenir
- PostgreSQL 9’dan itibaren
LISTENveNOTIFYifadeleriyle Pub/Sub işlevi sunar- PostgreSQL istemcisi, rastgele bir metin olan belirli bir mesaj kanalına
LISTENile abone olabilir - Başka bir istemci ilgili kanala
NOTIFYgönderdiğinde abone olan tüm istemciler bildirim alır - İsteğe bağlı olarak küçük bir mesaj da eklenebilir
- PostgreSQL istemcisi, rastgele bir metin olan belirli bir mesaj kanalına
- Rails ve ActionCable kullanıyorsanız PostgreSQL kullanımı varsayılan olarak desteklenir
Redis’i tutmaya değer alanlar
- Redis, PostgreSQL’den farklı alanları üstlenir ve PostgreSQL’in hedeflemediği işlerde güçlüdür
- TTL’li veri önbellekleme
- Geçici veri saklama ve işleme
- PostgreSQL, basit bir SQL veritabanı ya da ORM arkasındaki opak bir bileşen olarak görülemeyecek kadar geniş bir özellik yelpazesine sahiptir
- Redis’e verdiğiniz işlerin bir kısmı PostgreSQL için de uygun olabilir
- Redis’i çıkarmak, birden fazla veri servisine bağımlılıktan doğan operasyonel maliyetleri ve geliştirme karmaşıklığını azaltmak için bir seçenek olabilir
1 yorum
Hacker News yorumları
Herkes aşırı dağıtılmış mimari konusunda ısrarcı olunca Redis’in gerçek avantajlarını göremediği durumlar çok oluyor. Uygulamayla aynı makinede çalıştırırsanız 1 milisaniyeden çok daha kısa sürede yanıt verebilir; bu da uygulamanın Postgres ile yapması zor olan şeyleri yapabilmesini sağlar
Postgres’in harika olduğu doğru, ama uygulamayla aynı makinedeki bellekte çalışan bir şey değil. Yalnızca kuyruk gibi şeylere ihtiyacınız varsa, bellek içi anahtar-değer deposuna ihtiyaç duymayabilirsiniz. Bellek içi anahtar-değer deposunun özü, RAM’in performans özelliklerine ihtiyaç duyan işleri yapmaktır; ağ bağlantısının ötesinde bu özellikleri elde edemezsiniz
Postgres’te özel bir sihir yok; Redis gibi başka bir süreçte çalışan bir programdan ibaret. Yerel bağlantıda gecikmeyi azaltmak için hızlı bir kanal kullanır ve daha hızlı toplu veri aktarım yöntemlerinden de yararlanabilir. Bunu bu şekilde birçok kez kullandım
Ancak kurum içi iş araçları gibi durumlarda tek bir örneği uzun süre açık tutabilirsiniz ve bu bellek içi önbellek sayesinde çok hızlanır. django-cachalot, tabloda her yazma işlemi olduğunda önbellek geçersiz kılmayı otomatik yapan bir kütüphanedir. Biraz kaba bir yöntem ama neredeyse hiç çaba harcamadan performans artışı sağlar; güncellemelerin seyrek olduğu kurum içi iş uygulamaları fiilen RAM’de çalışır, yalnızca önbellekte olmadığında normal veritabanı sorgusuna döner
https://github.com/noripyt/django-cachalot
Buradaki tartışmada Redis açısından gelen savunmacı yorumlar çok, ama elbette Redis’in daha iyi olduğu belirli alanlar var
Yine de bence yazının ana fikri bu değil. Ana cümle şu şekilde özetlenebilir: “PostgreSQL, ona basit bir SQL veritabanı ya da bir ORM’nin arkasına gizlenmiş gizemli bir varlık gibi yaklaştığınızda beklediğinizden çok daha fazla işleve sahip.” Veritabanını yalnızca ORM’nin arkasından kullanıyorsanız, hangi veritabanı olursa olsun özelliklerini kaçırıyor olma ihtimaliniz yüksektir. Redis gibi başka bir servis eklemeniz gerekiyorsa, yeni bir bağımlılık eklemek yerine hâlihazırda kurulu olan veritabanını kullanmak daha iyi olabilir
Postgres’in neler yapabildiğini anlamak iyi bir şey. Güçlü bir veritabanı
Karşı argüman ise Redis kullanmanın eşiğinin çok düşük olması ve karşılığında yüksek performans, zengin kütüphane desteği ve temel veritabanının yükünü azaltma avantajı sunması. Örneğin API yanıt önbelleğini Postgres ile kurabilirsiniz. TTL için cron işiyle eski önbellek değerlerini temizlersiniz. Ya da doğrudan Redis kullanırsınız
Advisory lock’lar hoş ve kullanışlıdır, ancak PgBouncer gibi bir şey kullanacaksanız oturum advisory lock’ları ile transaction interleaving arasında sorun çıkabilir. Ayrı bir sistemin ağ çağrıları, erişilebilirlik ve alan bilgisi gibi dezavantajları vardır, ama Redis düzeyindeki ödünleşim oldukça düşüktür
Oldukça eski bir yazı, ama artık çok yaygın bir kalıp haline geldi. E-posta gönderimi ya da rapor üretimi için yalnızca bir iş kuyruğuna ihtiyaç duyan projelerin %90’ı saniyede milyonlarca mesaj işlemiyor; bu yüzden yığını basitleştiren bu yaklaşım değerlendirmeye değer
Celery’de yaşadığım sorunları aşmak için bu kalıbı sık sık kullanıyordum ve sonra bunu ayrı bir framework olarak ayırdım: https://github.com/TkTech/chancy geri bildirimlere açığım
Bu tür araç çok ve bunların bazıları ticari hizmet; yani açıkça bir talep var
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’in FOR UPDATE SKIP LOCKED ve LISTEN/NOTIFY özelliklerini kullanarak iş kuyruğu, kilitler ve gerçek zamanlı bildirimler sağlar
Zaten PostgreSQL kullanıyorsanız Redis gerektirmeyen minimal bir alternatiftir
https://github.com/janbjorge/PGQueuer
Bu arada bunu ben yaptım
Postgres'u seviyorum ama bazı sınırlamaları var
Bir anahtar-değer deposuna ihtiyacınız varsa autovacuum'u bilip bilmediğinize, bağlantı havuzu sınırlarını bilip bilmediğinize ve throughput ile güvenlikten hangisini istediğinize bakmanız gerekir. Kuyruğa ihtiyacınız varsa sıralı işleme mi, hız sınırlaması mı, fan-out mu, konuya göre ayrım mı gerektiğine bakmalısınız. Yayınla/abone ol gerekiyorsa yinelenen alımları önemseyip önemsemediğinize, mesaj kaybını önemseyip önemsemediğinize ve replay gerekip gerekmediğine bakmalısınız. Kilit gerekiyorsa bağlantı havuzu sınırlarını ve statement_timeout'u bilmelisiniz. Yukarıdaki sorunların çoğu çözülebilir, ama o kadar da basit değil
Postgres yayınla/abone ol tarafındaki büyük darboğaz, mesaj azami boyutunun 8000 bayt olması
Önerilen geçici çözüm, veriyi tabloya koyup yalnızca ID göndermek; ama bunu sonsuza kadar tutmak istemiyorsanız o veriyi garbage collect etmeniz gerekir ve her mesaja ek iş yükü gelir. Elbette bunun sorun olmadığı durumlar var, ama birçok Redis kullanım senaryosunda bu sınır yüzünden eşdeğer saymak zor
pgsql'ın 15000 istemci bağlantısını kaldırıp kaldırmadığına bakalım
Planetscale tarafındaki biri bir podcast'te GitHub'ın MySQL instance'larının her birinin 50 binden fazla bağlantıyı kaldırdığını söylemişti. Buna karşılık Postgres'te 100'den fazla bağlantıya ihtiyacınız varsa zaten PgBouncer gerekli hale geliyor
Kuyruk, kilit, yayınla/abone ol mümkün. Ama Redis'in en önemli kullanım alanı olan önbellekleme eksik
Postgres'te update işlemleri insert'ten daha pahalı olmasıyla ünlüdür, çöp üretir ve vacuum gerektirir. Önbelleklemede önemli olmayan dayanıklılık garantileri yüzünden yazma da oldukça yavaşlar. Otomatik süre sonu çok kullanışlıdır ve hataları azaltır
Senkron commit, autovacuum vb. de kapatılabilir. Elbette Redis hâlâ daha hızlı olacaktır, ama fark tipik bir şirketin önemseyeceği kadar büyük olmayabilir
Yazının ana fikrini daha açık söylemek gerekirse, Postgres ile başlayın ve ihtiyaç doğarsa Redis'e geçin
Hareketli parça sayısını olabildiğince az tutmak iyidir