Redis преко VPN-а делује споро: смањите број кругова по захтеву
VPN не претвара удаљени Redis позив у локални. Планирајте круге по захтеву, поново користите везе и држите pipeline даље од трансакција.
Страница која уз локални Redis одговара за 200 мс потребује четири секунде када клијент седи иза VPN-а, док бројачи сервера мирују. Slow Log не показује ништа изнад милисекунде, график CPU-а је раван, а инстанца служи друге клијенте без жалбе. Оно што се променило је раздаљина, а оно што раздаљину умножава је број пута када ваш код стане и чека. Прођите пут захтева од клијента ка споља и поправка је скоро увек мање узастопних чекања, а не бржи сервер.
Пођите од клијента, а не од метрика сервера
Посматрајте процес клијента између прве и последње команде. Ако траг показује одговор, паузу, па следећу команду, плаћате цео круг по команди и никакво подешавање сервера то неће променити. Slow Log је овде погрешан инструмент, јер бележи време извршавања и комуникациони И/О намерно искључује. Чист Slow Log поред спорог endpointa је потпис проблема са бројем кругова.
Прво измерите сам пут, па тек онда дирајте код. redis-cli -h redis.example.internal --latency шаље поновљене PING-ове и исписује просечно време захтева и одговора, што је ваш трошак по једној размени. Помножите то бројем узастопних команди у једној корисничкој акцији и добијате доњу границу за endpoint.
Избројте колико кругова једна корисничка акција заиста захтева
Узмите један endpoint и по реду запишите сваки Redis позив, укључујући оне скривене у ORM-у, session middleware-у или тражењу feature flag-а. Већина спорих endpointa направи двадесет или тридесет позива тамо где би три била довољна, а скривени су обично већина.
Ланац зависности не може да се сабије: ако другој команди треба кључ који је вратила прва, чекање је стварно и једине опције су другачији модел података или серверска операција која цео корак изрази одједном. Независна читања су други случај, јер вас ништа не тера да чекате први одговор пре него што пошаљете други. Означите сваки позив као зависан или независан пре него што било шта измените, и облик поправке постаје очигледан.
Важан је број узастопних чекања, а не укупан број команди. Тридесет команди у једном batch-у и даље кошта једно чекање.
У batch стављајте само команде које не зависе једна од друге
Pipeline ставља независне команде у ред и скупља одговоре по реду, претварајући N чекања у једно. Већина клијената га излаже као блок, callback или context manager, а не као посебан режим везе, зато проверите API који доноси ваша инсталирана верзија. Добитак расте са временом круга, и баш зато је важан на VPN-у, а на localhost-у се једва примећује.
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())
Проследите transaction=False када желите само batch. Уз подразумевано клијент увија batch у MULTI и EXEC, што га чини атомичним и мења шта други клијенти виде у лету. Атомичност је одлука о исправности, а не дугме за перформансе, и pipeline није трансакција само зато што оба смањују број кругова.
Прочитајте сваки одговор, а не само слање batch-а. Команда у реду може да врати грешку док batch као целина успешно заврши, а клијент који одбаци листу одговора претвара неуспели упис у тихи. Упоредите дужину листе одговора са бројем команди које сте ставили у ред.
Држите везу живом између акција
Свака нова веза плаћа разрешавање имена, TCP хандсхаке, TLS хандсхаке и AUTH, а преко VPN-а је свако од тога круг који не можете да batch-ујете. Направите клијента једном по процесу и користите га поново, или користите pool који ваша библиотека нуди са ограниченом величином. Поновно повезивање по захтеву је други најчешћи узрок овог симптома после чекања на сваку команду.
Држите приватни hostname у конфигурацији, а не као дословну адресу. Валидација сертификата и идентитет услуге и даље важе на приватној мрежи, а прикивање IP-а се ломи у тренутку када провајдер пренумерише subnet. Ако је разрешавање имена спори део, поправите пут до ресолвера, а не connection string.
Поставите експлицитан socket timeout да заглављен тунел брзо падне, уместо да обеси worker. Затим потврдите да се pool заиста поново користи тако што ћете логовати стварање веза у клијенту.
Димензионирајте batch пре него што постане проблем са меморијом
Redis држи одговоре у реду за време трајања batch-а, а клијент потом држи парсирани резултат. Pipeline са десет хиљада великих вредности мења чекање на мрежу за скокове меморије и дугачке паузе на оба краја.
Изаберите величину batch-а према обиму одговора, а не према броју команди. Неколико стотина малих команди у једном batch-у је обично удобно, док сто payload-а од више килобајта није. Делите по бајтовима и по броју, и држите сваки batch унутар латенције коју endpoint заиста има.
После измене мерите од почетка до краја: redis-cli -h redis.example.internal --latency-history показује да ли је сам пут стабилан, а ваш мерилац захтева показује да ли се endpoint побољшао. Ако је endpoint и даље спор док је историја латенције равна, преостали трошак је рад на серверу или ваша сериализација, а следећи корак је профил handler-а, а не још једна измена batch-а.