VPN üzerinden Redis yavaş hissettiriyor: istek başına gidiş-dönüşleri azaltın
VPN, uzaktaki bir Redis çağrısını yerel hale getirmez. İstek başına gidiş-dönüşleri bütçeleyin, bağlantıları yeniden kullanın ve pipeline'ı işlemlerden uzak tutun.
Yerel bir Redis'e karşı 200 ms'de yanıt veren bir sayfa, istemci bir VPN'in arkasına geçtiğinde dört saniye sürüyor, oysa sunucu sayaçları boşta kalıyor. Slow Log bir milisaniyenin üzerinde hiçbir şey göstermiyor, CPU grafiği düz ve örnek diğer istemcilere şikayetsiz hizmet veriyor. Değişen şey mesafe ve mesafeyi katlayan şey kodunuzun durup bekleme sayısı. İsteğin yolunu istemciden dışarı doğru yürüyün ve düzeltme neredeyse her zaman daha hızlı bir sunucu değil, daha az sıralı beklemedir.
Sunucu metriklerinden değil, istemciden başlayın
İlk komut ile son komut arasında istemci sürecini izleyin. İz bir yanıt, bir duraklama ve ardından sonraki komutu gösteriyorsa komut başına tam bir gidiş-dönüş ödüyorsunuz ve hiçbir sunucu ayarı bunu değiştirmez. Slow Log burada yanlış araçtır, çünkü yürütme süresini kaydeder ve iletişim I/O'sunu bilerek dışlar. Yavaş bir endpoint'in yanındaki temiz bir Slow Log, gidiş-dönüş sorununun imzasıdır.
Koda dokunmadan önce yolun kendisini ölçün. redis-cli -h redis.example.internal --latency tekrarlanan PING gönderir ve ortalama istek ve yanıt süresini yazdırır, bu da takas başına maliyetinizdir. Bunu tek bir kullanıcı eyleminde sıralı komut sayısıyla çarpın ve endpoint için bir taban elde edersiniz.
Tek bir kullanıcı eyleminin gerçekte kaç gidiş-dönüş gerektirdiğini sayın
Tek bir endpoint alın ve her Redis çağrısını sırayla yazın; bir ORM, bir session middleware veya bir feature flag aramasının içinde gizlenenler dahil. Yavaş endpoint'lerin çoğu üç çağrının yeteceği yerde yirmi ya da otuz çağrı yapar ve gizli olanlar genellikle çoğunluktur.
Bir bağımlılık zinciri daraltılamaz: ikinci komut birincinin döndürdüğü anahtara ihtiyaç duyuyorsa bekleme gerçektir ve tek seçenekler farklı bir veri modeli ya da tüm adımı bir anda ifade eden sunucu tarafı bir işlemdir. Bağımsız okumalar başka bir durumdur, çünkü hiçbir şey ikinciyi göndermeden önce birinci yanıtı beklemenizi zorlamaz. Herhangi bir şeyi düzenlemeden önce her çağrıyı bağımlı ya da bağımsız olarak işaretleyin ve düzeltmenin şekli belirginleşir.
Önemli olan sayı sıralı beklemelerdir, toplam komutlar değil. Tek bir batch'teki otuz komut hâlâ tek bir beklemeye mal olur.
Yalnızca birbirine bağımlı olmayan komutları batch'leyin
Bir pipeline bağımsız komutları kuyruğa alır ve yanıtları sırayla toplar, N beklemeyi bire çevirir. Çoğu istemci bunu ayrı bir bağlantı modu olarak değil, bir blok, bir callback ya da bir context manager olarak sunar, bu yüzden kurulu sürümünüzün getirdiği API'yi kontrol edin. Kazanç gidiş-dönüş süresiyle ölçeklenir; bu tam olarak neden VPN'de önemli olduğunun ve localhost'ta neredeyse fark edilmediğinin nedenidir.
import redis
r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
pipe.get(key)
print(pipe.execute())
Yalnızca batch istediğinizde transaction=False geçirin. Varsayılanla istemci batch'i MULTI ve EXEC içine sarar, bu da onu atomik yapar ve diğer istemcilerin uçuş sırasında ne görebileceğini değiştirir. Atomiklik bir performans düğmesi değil, doğruluk kararıdır ve bir pipeline sadece ikisi de gidiş-dönüşleri azalttığı için bir işlem değildir.
Her yanıtı okuyun, yalnızca batch gönderimini değil. Kuyruğa alınan bir komut, batch bir bütün olarak tamamlanırken hata döndürebilir ve yanıt listesini atan bir istemci başarısız bir yazmayı sessiz bir yazmaya çevirir. Yanıt listesinin uzunluğunu kuyruğa aldığınız komut sayısıyla karşılaştırın.
Bağlantıyı eylemler arasında canlı tutun
Her yeni bağlantı ad çözümlemesi, bir TCP el sıkışması, bir TLS el sıkışması ve bir AUTH için ödeme yapar ve bir VPN üzerinde bunların her biri batch'leyemeyeceğiniz bir gidiş-dönüştür. İstemciyi süreç başına bir kez oluşturun ve yeniden kullanın ya da kitaplığınızın sunduğu havuzu sınırlı bir boyutla kullanın. İstek başına yeniden bağlanma, bu belirtinin komut başına beklemeden sonra ikinci en yaygın nedenidir.
Özel hostname'i gerçek bir adres yerine yapılandırmada tutun. Sertifika doğrulaması ve hizmet kimliği özel bir ağda da geçerlidir ve bir IP'yi sabitlemek, sağlayıcı bir subnet'i yeniden numaralandırdığı anda bozulur. Yavaş olan kısım çözümleme ise, bağlantı dizesini değil çözümleyici yolunu düzeltin.
Takılı bir tünelin bir worker'ı asması yerine hızla başarısız olması için açık bir socket timeout ayarlayın. Ardından istemcide bağlantı oluşturmayı günlüğe kaydederek havuzun gerçekten yeniden kullanıldığını doğrulayın.
Batch'i bellek sorununa dönüşmeden boyutlandırın
Redis, kuyruğa alınan yanıtları batch süresince tutar ve istemci sonrasında ayrıştırılmış sonucu tutar. On bin büyük değerden oluşan bir pipeline, ağ beklemelerini bellek sıçramaları ve iki uçta uzun duraklamalarla değişir.
Batch boyutunu komut sayısına göre değil yanıt hacmine göre seçin. Tek bir batch'te birkaç yüz küçük komut genellikle rahattır, yüz çok kilobaytlık payload ise değildir. Hem baytlara hem sayıya göre bölün ve her batch'i endpoint'in gerçekten sahip olduğu gecikme bütçesi içinde tutun.
Değişiklikten sonra uçtan uca ölçün: redis-cli -h redis.example.internal --latency-history yolun kendisinin kararlı olup olmadığını gösterir ve kendi istek zamanlayıcınız endpoint'in iyileşip iyileşmediğini gösterir. Endpoint hâlâ yavaşken gecikme geçmişi düzse, kalan maliyet sunucu tarafı iştir ya da kendi serileştirmenizdir ve sonraki adım başka bir batch değişikliği değil işleyicinin profilidir.