2 puan yazan GN⁺ 2024-11-04 | 1 yorum | WhatsApp'ta paylaş
  • 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 ... ifadesinde SKIP LOCKED seç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 LOCKED belirtildiğinde dönen satırlar için satır düzeyi kilit örtük olarak alınır
    • SKIP LOCKED sayesinde 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 pending durumundaki tek bir işi seçmek, bu işi running durumuna geçirmek ve ardından döndürmek şeklindedir
    • BEGIN
    • SELECT ... FOR UPDATE SKIP LOCKED
    • UPDATE 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 locks ile 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 LISTEN ve NOTIFY ifadeleriyle Pub/Sub işlevi sunar
    • PostgreSQL istemcisi, rastgele bir metin olan belirli bir mesaj kanalına LISTEN ile abone olabilir
    • Başka bir istemci ilgili kanala NOTIFY gönderdiğinde abone olan tüm istemciler bildirim alır
    • İsteğe bağlı olarak küçük bir mesaj da eklenebilir
  • 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

 
GN⁺ 2024-11-04
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

    • Makinedeki Redis’i yalnızca tek bir yerel süreç bellek içi önbellek olarak kullanacaksa, doğrudan kullandığınız programlama dilinin veri yapılarını kullanmak daha iyidir
    • Redis ve Postgres’in yereldeki ek yükünün ne kadar farklı olduğunu merak ediyorum. Postgres’in neden yerelde çalışmıyor sayıldığını da anlamıyorum
      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
    • Django’da Redis destekli bir önbellek yerleşik olarak var ve bellek içi önbellek seçeneği de mevcut, ama “üretim için değil” diye işaretlenmiş. Çünkü birden fazla Django örneği varsa her bellek içi önbellek birbirinden farklı oluyor
      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
    • Bu kurulumda Redis’ten daha hızlı olan şey hash map’tir. Postgres’in uygulamayla aynı sunucuda çalışmaması için de bir neden yok; hatta bu oldukça yaygın bir kurulumdur
    • Aşırı tasarım ve erken dağıtıklaştırma gerçek sorunlar, ama Redis “Remote Dictionary Server”ın kısaltmasıdır. Asıl amacı yerelde çalışmak değildir. Elbette, dilin yerleşik sözlük yapısının aralık sorgularını desteklememesi gibi durumlarda yerel çalıştırma da makul bir tasarım tercihi olabilir
  • 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

    • Üretimde yönetilecek bir bileşenin daha az olması avantajdır. PostgreSQL ile başlayın; performans, ölçek veya maliyet nedeniyle gerçekten gerekli olduğunda özel amaçlı sistemler ekleyin
  • 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)

    • Basit bir iş kuyruğu arıyordum. Huey de fena değil ama zaten Postgres kullanıyordum ve kuyruğa eklediğim işler saatte yaklaşık bir kez olduğu için Redis tabanlı çözüm hep gereğinden fazla geliyordu
    • Node tarafında PG-Boss da var: https://github.com/timgit/pg-boss
  • 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

    • Uzun süren transaction'lar vacuum'un tuple kaldırmasını engellediğinde uygulamanın çökmeden ayakta kalıp kalmadığına da bakmak gerekir
  • 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

    • Postgres ile MySQL arasındaki fark burada gerçekten şaşırtıcı derecede büyük
      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
    • Bağlantı havuzlama kullanabilirsiniz. AWS RDS Proxy gibi birkaç tıklamayla kullanılabilir
  • 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

    • Gereksiz özelliklerin çoğu kapatılabilir. Örneğin bir tabloyu unlogged olarak oluşturabilirsiniz: https://www.postgresql.org/docs/current/sql-createtable.html...
      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

    • Redis'i dağıtmak özellikle zor değil. İleride ne gibi beklenmedik yan etkiler çıkaracağı belirsiz bir geçiş planlamak yerine en baştan Redis kullanmak da gayet makul