Redis oferuje dwa mechanizmy obsługi transakcji – transakcje oparte na MULTI/EXEC oraz ewaluację skryptów Lua. Skrypty Redis Lua są zalecanym podejściem i są dość popularne w użyciu.
Nasi klienci Redis™, którzy mają wdrożone skrypty Lua, często zgłaszają ten błąd — „ZAJĘTY Redis jest zajęty uruchamianiem skryptu. Możesz zadzwonić tylko do SCRIPT KILL lub SHUTDOWN NOSAVE ”. W tym poście wyjaśnimy właściwość transakcyjną skryptów Redis, na czym polega ten błąd i dlaczego musimy zachować szczególną ostrożność w przypadku systemów zarządzanych przez Sentinel, które mogą zostać przełączone w tryb failover.
Transakcyjny charakter skryptów Redis Lua
Redis „transakcje” nie są tak naprawdę transakcjami w tradycyjnym rozumieniu – w przypadku błędów nie ma cofania zapisów dokonanych przez skrypt.
„Atomowość” skryptów Redis jest gwarantowana w następujący sposób:
- Po rozpoczęciu wykonywania skryptu wszystkie inne polecenia/skrypty są blokowane do czasu zakończenia wykonywania skryptu. Inni klienci albo widzą zmiany wprowadzone przez skrypt, albo nie. Dzieje się tak, ponieważ można je wykonać tylko przed skryptem lub po skrypcie.
- Jednak Redis nie wykonuje wycofywania, więc w przypadku błędu w skrypcie wszelkie zmiany już wprowadzone przez skrypt zostaną zachowane, a przyszłe polecenia/skrypty zobaczą te częściowe zmiany.
- Ponieważ wszyscy inni klienci są blokowani podczas wykonywania skryptu, bardzo ważne jest, aby skrypt był dobrze zachowywany i kończył się na czas.
Wartość „lua-time-limit”
Wysoce zalecane jest ukończenie skryptu w określonym czasie. Redis wymusza to w słaby sposób z wartością „lua-time-limit”. Jest to maksymalny dozwolony czas (w ms) działania skryptu. Wartość domyślna to 5 sekund. To naprawdę długi czas dla aktywności związanej z procesorem (skrypty mają ograniczony dostęp i nie mogą uruchamiać poleceń, które uzyskują dostęp do dysku).
Jednak skrypt nie zostanie zabity, gdy zostanie wykonany po tym czasie. Redis ponownie zaczyna akceptować polecenia klienta, ale odpowiada na nie błędem BUSY.
Jeśli musisz zabić skrypt w tym momencie, dostępne są dwie opcje:
- SKRYPT ZABIJ polecenie może być użyte do zatrzymania skryptu, który jeszcze nie wykonał żadnego zapisu.
- Jeżeli skrypt wykonał już zapisy na serwerze i nadal musi zostać zabity, użyj SHUTDOWN NOSAVE aby całkowicie wyłączyć serwer.
Zazwyczaj lepiej jest po prostu poczekać, aż skrypt zakończy swoje działanie. Pełne informacje na temat metod zabijania wykonania skryptu i powiązanych zachowań są dostępne w dokumentacji.
Transakcje Redis i długotrwałe skrypty LuaKliknij, aby tweetowaćZachowanie w systemach wysokiej dostępności monitorowanych przez Sentinel
Systemy wysokiej dostępności zarządzane przez Sentinel dodają do tego nowy problem. W rzeczywistości ta dyskusja dotyczy każdego systemu wysokiej dostępności, który zależy od odpytywania serwerów Redis pod kątem kondycji:
- Długo działające skrypty początkowo blokują polecenia klienta. Później, gdy minie „limit czasu lua”, serwer zacznie odpowiadać z błędami BUSY.
- Strażnicy uznają taki węzeł za niedostępny, a jeśli to się utrzyma poza wartością wyłączenia po milisekundach skonfigurowaną na Strażnikach, określą, że węzeł jest wyłączony.
- Jeśli taki węzeł jest nadrzędnym, zainicjowane zostanie przełączenie awaryjne. Węzeł repliki może zostać awansowany i może zacząć akceptować nowe połączenia od klientów.
- Tymczasem starszy mistrz w końcu zakończy wykonywanie skryptu i wróci do trybu online. Jednak Sentinel w końcu przekonfiguruje go jako replikę i rozpocznie synchronizację z nowym masterem. Wszelkie dane zapisane przez skrypt zostaną utracone.
|
Demonstracja
Konfigurowaliśmy wrażliwy system wysokiej dostępności, aby zademonstrować to zachowanie awaryjne. Konfiguracja obejmuje 2 serwery Redis działające w konfiguracji master/replika, która jest monitorowana przez kworum trzech strażników.
Wartość lua-time-limit została ustawiona na 500 ms, dzięki czemu zaczyna odpowiadać klientom z błędami, jeśli skrypt działa dłużej niż 500 ms. Wartość „w dół po milisekundach” na Sentinel jest ustawiona na 5 sekund, dzięki czemu węzeł, który zgłasza błędy, jest oznaczony jako „DOLNY” po 5 sekundach.
Wykonujemy następujący skrypt Lua na wzorcu:
local i = 0 while (true) do local key = "Key-" .. i local value = "Value-" .. i redis.call('set', key, value) i = i + 1 redis.call('time') end
Dzięki temu zapisujesz wpisy do wzorca Redis. Subskrybujemy wydarzenia na jednym ze strażników, aby obserwować zachowanie.
Skrypt jest inicjowany na wzorcu:
$ redis-cli -a --eval test.lua Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Oto skrócona sekwencja działań widoczna na Sentinel:
3) "+vote-for-leader" 4) "9096772621089bb885eaf7304a011d9f46c5689f 1" 1) "pmessage" 2) "*" 3) "+sdown" <<< master marked DOWN 4) "master test 172.31.2.48 6379" 1) "pmessage" 2) "*" 3) "+odown" 4) "master test 172.31.2.48 6379 #quorum 3/2" 1) "pmessage" 2) "*" 3) "-role-change" << role change initiated 4) "slave 172.31.28.197:6379 172.31.28.197 6379 @ test 172.31.2.48 6379 new reported role is master" 1) "pmessage" 2) "*" 3) "+config-update-from" 4) "sentinel 9096772621089bb885eaf7304a011d9f46c5689f 172.31.2.48 26379 @ test 172.31.2.48 6379" 1) "pmessage" 2) "*" 3) "+switch-master" 4) "test 172.31.2.48 6379 172.31.28.197 6379"
Później, gdy stary mistrz zostanie przeniesiony do sieci, zostanie zmieniony na replikę:
3) "-role-change" 4) "slave 172.31.2.48:6379 172.31.2.48 6379 @ test 172.31.28.197 6379 new reported role is master" 1) "pmessage" 2) "*" 3) "-sdown" 4) "slave 172.31.2.48:6379 172.31.2.48 6379 @ test 172.31.28.197 6379" 1) "pmessage" 2) "*" 3) "+role-change" 4) "slave 172.31.2.48:6379 172.31.2.48 6379 @ test 172.31.28.197 6379 new reported role is slave"
Wszystkie dane zapisane w starym wzorcu za pomocą skryptu zostaną utracone.
Rekomendacje
- Musisz znać cechy swoich długo działających skryptów z wyprzedzeniem przed wdrożeniem ich w środowisku produkcyjnym.
- Jeśli Twój skrypt regularnie przekracza limit czasu lua, musisz dokładnie przejrzeć skrypt pod kątem możliwych optymalizacji. Możesz również podzielić go na części, które ukończą się w akceptowalnym czasie trwania.
- Jeśli musisz uruchamiać skrypty, które naruszają limit czasu lua, rozważ zaplanowanie tych skryptów w okresach, w których aktywność innych klientów będzie niska.
- Wartość limitu czasu lua może również zostać zwiększona. Byłoby to akceptowalne rozwiązanie, gdyby inne aplikacje klienckie, które działają równolegle ze skryptem, tolerowały otrzymywanie bardzo opóźnionych odpowiedzi zamiast błędu ZAJĘCIA i ponawiania później.
Dodatkowe uwagi dotyczące systemów wysokiej dostępności monitorowanych przez Sentinel:
- Jeśli skrypty wykonują tylko operacje odczytu i masz dostępne repliki, możesz przenieść te skrypty do replik.
Zmień parametr Sentinel down-after-milisekundy na wartość, która zapewni, że nie zostaną zainicjowane przełączanie awaryjne. Musisz to zrobić dopiero po dokładnym rozważeniu, ponieważ drastyczne zwiększenie wartości obniży charakterystykę wysokiej dostępności systemu. Może to również spowodować zignorowanie prawdziwych awarii serwera.
|