Redis көмегімен бөлінген құлыптау

Эй Хабр!

Бүгін біз сіздердің назарларыңызға Redis көмегімен бөлінген құлыптауды жүзеге асыру туралы күрделі мақаланың аудармасын ұсынамыз және тақырып ретінде Redis болашағы туралы сөйлесуге шақырамыз. «Кітаптың авторы Мартин Клепманнан қаралып отырған Redlock алгоритмін талдау.Жоғары жүктеме қолданбалары", берілген осында.

Бөлінген құлыптау әртүрлі процестер ортақ ресурстарда өзара эксклюзивті түрде жұмыс істеуі керек көптеген орталарда қолданылатын өте пайдалы примитив болып табылады.

Мұнда Redis көмегімен DLM (таратылған құлыптау менеджері) қалай жүзеге асырылатынын сипаттайтын бірқатар кітапханалар мен хабарламалар бар, бірақ әр кітапхана басқа тәсілді қолданады және олар беретін кепілдіктер сәл күрделірек дизайнмен қол жеткізуге болатынмен салыстырғанда өте әлсіз.

Бұл мақалада біз Redis көмегімен бөлінген құлыптауды жүзеге асыру жолын көрсететін шартты канондық алгоритмді сипаттауға тырысамыз. деп аталатын алгоритм туралы сөйлесеміз Redlock, ол таратылған құлыптау менеджерін жүзеге асырады және біздің ойымызша, бұл алгоритм әдеттегі бір даналық тәсілге қарағанда қауіпсіз. Қауымдастық оны талдап, кері байланыс береді және оны күрделі немесе балама жобалар үшін бастапқы нүкте ретінде пайдаланады деп үміттенеміз.

Іске асыру

Алгоритмнің сипаттамасына көшпес бұрын, біз дайын іске асыруға бірнеше сілтемелер береміз. Оларды анықтама ретінде пайдалануға болады.

  • Redlock-rb (Ruby үшін іске асыру). Сондай-ақ бар шанышқы Redlock-rb, таратудың қарапайымдылығы үшін пакетті (асыл тасты) қосады, бұл үшін ғана емес.
  • Redlock-py (Python іске асыру).
  • Әуе құлыптауы (Ayncio Python үшін іске асыру).
  • Redlock-php (PHP үшін енгізу).
  • PHPRedisMutex (PHP үшін басқа іске асыру)
  • cheprasov/php-redis-lock (Құлыптарға арналған PHP кітапханасы)
  • Redsync (Go үшін іске асыру).
  • Редиссон (Java үшін енгізу).
  • Redis::DistLock (Perl үшін іске асыру).
  • Redlock-cpp (C++ үшін енгізу).
  • Redlock-cs (C#/.NET үшін енгізу).
  • RedLock.net (C#/.NET үшін енгізу). Асинхронды және құлып кеңейтімдерін қолдауымен.
  • ScarletLock (конфигурацияланатын деректер қоймасы бар C# .NET үшін енгізу)
  • Redlock4Net (C# .NET үшін енгізу)
  • түйін-редблок (NodeJS үшін енгізу). Құлыптарды ұзартуға арналған қолдауды қамтиды.

Қауіпсіздік және қол жетімділік кепілдіктері

Біз үлестірілген құлыптауды тиімді пайдалану үшін қажетті минималды кепілдіктерді қамтамасыз ететін үш қасиетпен дизайнымызды модельдейтін боламыз.

  1. Қауіпсіздік қасиеті: өзара алып тастау. Кез келген уақытта тек бір клиент құлыпты ұстай алады.
  2. Қол жетімділік қасиеті A: Тұйықталулар жоқ. Ресурсты құлыптаған клиент сәтсіз болса немесе басқа диск сегментіне түссе де, ақыр соңында құлыпты алу әрқашан мүмкін.
  3. Қол жетімділік қасиеті B: ақауларға төзімділік. Redis түйіндерінің көпшілігі жұмыс істеп тұрған кезде, клиенттер құлыптарды алуға және босатуға қабілетті.

Неліктен бұл жағдайда сәтсіздікті қалпына келтіруге негізделген енгізу жеткіліксіз
Біз нені жақсартатынымызды түсіну үшін Redis негізіндегі көптеген таратылған құлыптау кітапханаларымен жұмыстың ағымдағы жағдайын талдап көрейік.

Redis көмегімен ресурсты құлыптаудың ең қарапайым жолы - данада кілт жасау. Әдетте, кілт шектеулі қызмет ету мерзімімен жасалады, бұған Redis-те берілген мерзімі аяқталады мүмкіндігін пайдалану арқылы қол жеткізіледі, сондықтан ерте ме, кеш пе бұл кілт шығарылады (біздің тізімдегі 2-қасиет). Клиент ресурсты босату қажет болғанда, ол кілтті жояды.

Бір қарағанда, бұл шешім өте жақсы жұмыс істейді, бірақ мәселе бар: біздің архитектура бір ғана сәтсіздік нүктесін жасайды. Хост Redis данасы сәтсіз болса не болады? Онда құлды қосайық! Ал егер жүргізуші қолжетімсіз болса, біз оны пайдаланамыз. Өкінішке орай, бұл опция өміршең емес. Бұл әрекетті орындау арқылы біз қауіпсіздікті қамтамасыз ету үшін қажет өзара алып тастау сипатын дұрыс жүзеге асыра алмаймыз, себебі Redis ішіндегі репликация асинхронды.

Әлбетте, мұндай модельде жарыс жағдайы орын алады:

  1. А клиенті шеберде құлыпты алады.
  2. Кілт жазбасы бағыныштыға берілмей тұрып, басты құрылғы сәтсіз аяқталады.
  3. Ізбасар көшбасшыға көтеріледі.
  4. B клиенті А құлыптап қойған сол ресурста құлыпты алады. ҚАУІПСІЗДІК БҰЗУ!

Кейде ақаулық сияқты ерекше жағдайларда көптеген клиенттер құлыпты бір уақытта ұстай алатыны қалыпты жағдай. Мұндай жағдайларда репликацияға негізделген шешімді қолдануға болады. Әйтпесе, осы мақалада сипатталған шешімді ұсынамыз.

Бір данасы арқылы дұрыс іске асыру

Жоғарыда сипатталған бір даналық конфигурацияның кемшіліктерін жоюға әрекет жасамас бұрын, осы қарапайым жағдайды қалай дұрыс өңдеу керектігін түсінейік, өйткені бұл шешім жарыс шарты мезгіл-мезгіл қолайлы болатын қолданбаларда жарамды, сонымен қатар блоктауға байланысты. жалғыз данасы осы жерде сипатталған таратылған алгоритмде пайдаланылатын негіз ретінде қызмет етеді.

Құлып алу үшін мына әрекетті орындаңыз:

SET resource_name my_random_value NX PX 30000

Бұл пәрмен кілтті ол әлі жоқ болса ғана орнатады (NX опциясы), жарамдылық мерзімі 30000 миллисекунд (PX опциясы). Кілт «myrandomvalue" Бұл мән барлық клиенттерде және барлық құлыптау сұрауларында бірегей болуы керек.
Негізінде, құлыпты қауіпсіз босату үшін кездейсоқ мән пайдаланылады, сценарий Redis-ке айтады: кілт бар болса ғана алып тастаңыз және онда сақталған мән күтілгендей болады. Бұған келесі Lua сценарийі арқылы қол жеткізіледі:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

Бұл басқа клиент ұстаған құлыпты алып тастауды болдырмау үшін маңызды. Мысалы, клиент құлыпты алуы мүмкін, содан кейін бірінші құлыптан ұзағырақ болатын кейбір операцияда құлыпталуы мүмкін (кілттің жарамдылық мерзімі аяқталуы үшін) және кейінірек басқа клиент орналастырған құлыпты алып тастайды.
Қарапайым DEL пайдалану қауіпті, себебі клиент басқа клиент ұстаған құлыпты алып тастай алады. Керісінше, жоғарыдағы сценарийді пайдаланған кезде әрбір құлыпқа кездейсоқ жолмен «қол қойылады», сондықтан оны бұрын орналастырған клиент ғана оны жоя алады.

Бұл кездейсоқ жол қандай болуы керек? Менің ойымша, бұл /dev/urandom ішінен 20 байт болуы керек, бірақ жолды өз мақсаттарыңыз үшін бірегей етудің қымбат емес жолдарын таба аласыз. Мысалы, RC4-ті /dev/urandom көмегімен сепкен дұрыс, содан кейін одан жалған кездейсоқ ағынды жасау керек. Қарапайымырақ шешім микросекундтық ажыратымдылықтағы unix уақытының комбинациясын және клиент идентификаторын қамтиды; бұл қауіпсіз емес, бірақ ол көптеген контексттерде тапсырмаға байланысты болуы мүмкін.

Кілттің қызмет ету мерзімінің өлшемі ретінде біз қолданатын уақыт «құлыптың қызмет ету мерзімі» деп аталады. Бұл мән құлыптау автоматты түрде босатылғанға дейінгі уақыт мөлшері және басқа клиент өз кезегінде өзара алып тастау кепілдіктерін бұзбай сол ресурсты құлыптай алмас бұрын клиент операцияны аяқтауы керек уақыт мөлшері болып табылады. Бұл кепілдік құлыпты сатып алған сәттен бастап басталатын белгілі бір уақыт терезесімен ғана шектеледі.

Сондықтан біз құлыпты алудың және босатудың жақсы әдісін талқыладық. Жүйе (егер біз жалғыз және әрқашан қол жетімді данадан тұратын таратылмаған жүйе туралы айтатын болсақ) қауіпсіз. Бұл тұжырымдаманы бізде мұндай кепілдіктер жоқ бөлінген жүйеге дейін кеңейтейік.

Redlock алгоритмі

Алгоритмнің бөлінген нұсқасы бізде N Redis шеберлері бар деп болжайды. Бұл түйіндер бір-бірінен толығымен тәуелсіз, сондықтан біз репликацияны немесе басқа жасырын координация жүйесін пайдаланбаймыз. Біз бір данада құлыпты қауіпсіз алу және босату жолын қарастырдық. Алгоритмнің бір данамен жұмыс істегенде дәл осы әдісті қолданатынын біз кәдімгідей қабылдаймыз. Біздің мысалдарымызда біз N мәнін 5-ке қойдық, бұл ақылға қонымды мән. Осылайша, олардың бір-бірінен тәуелсіз әрекет ететініне көз жеткізу үшін әртүрлі компьютерлерде немесе виртуалды машиналарда 5 Redis шеберін пайдалану қажет болады.

Құлыпты алу үшін клиент келесі әрекеттерді орындайды:

  1. Ағымдағы уақытты миллисекундпен алады.
  2. Барлық жағдайларда бірдей кілт атауын және кездейсоқ мәндерді пайдалана отырып, барлық N даналарында құлыпты алуға әрекеттенеді. 2-кезеңде клиент құлыпты әр дана негізінде орнатқанда, клиент оны алу үшін кідіртуді пайдаланады, бұл құлып автоматты түрде босатылатын уақытпен салыстырғанда жеткілікті қысқа. Мысалы, блоктау ұзақтығы 10 секунд болса, кідіріс ~5-50 миллисекунд аралығында болуы мүмкін. Бұл клиенттің сәтсіз Redis түйініне жетуге тырысып ұзақ уақыт бойы бұғатталған күйінде қалуы мүмкін жағдайды жояды: егер дана қолжетімсіз болса, біз мүмкіндігінше тезірек басқа данаға қосылуға тырысамыз.
  3. Құлыпты алу үшін клиент қанша уақыт өткенін есептейді; Бұл әрекетті орындау үшін, ол 1-қадамда алынған уақыт белгісін нақты уақыт мәнінен шегереді. Клиент даналардың көпшілігінде (кемінде 3) құлыпты ала алған жағдайда ғана және оған кеткен жалпы уақытты шегереді. құлыпты алу, құлыптау ұзақтығынан аз болса, құлып алынды деп есептеледі.
  4. Егер құлып алынған болса, құлыптау ұзақтығы 3-қадамда есептелген өткен уақытты шегеріп тастағандағы бастапқы құлыптау ұзақтығы ретінде қабылданады.
  5. Егер клиент қандай да бір себептермен құлыпты ала алмаса (ол N/2+1 даналарын құлыптай алмаса немесе құлыптау ұзақтығы теріс болса), онда ол барлық даналарды (тіпті блоктай алмайды деп ойлағандардың да) құлпын ашуға әрекет жасайды. ).

Алгоритм асинхронды ма?

Бұл алгоритм барлық процестер жұмыс істейтін синхрондалған сағат болмаса да, әрбір процестегі жергілікті уақыт бұрынғысынша шамамен бірдей қарқынмен өтеді және құлыптан кейінгі жалпы уақытпен салыстырғанда қате аз болады деген болжамға негізделген. автоматты түрде шығарылады. Бұл болжам кәдімгі компьютерлерге тән жағдайға өте ұқсас: әрбір компьютерде жергілікті сағат бар және біз әдетте әртүрлі компьютерлер арасындағы уақыт айырмашылығы аз екеніне сене аламыз.

Осы кезде біз өзара алып тастау ережесін мұқият тұжырымдауымыз керек: құлыпты ұстап тұрған клиент құлыптың жарамды уақытында (бұл мән 3-қадамда алынған), тағы біраз уақытты (барлығы бірнеше) алып тастаған жағдайда ғана, өзара алып тастауға кепілдік беріледі. процестер арасындағы уақыт айырмашылығын өтеу үшін миллисекундтар).

Келесі қызықты мақалада уақыт аралықтарын үйлестіруді қажет ететін жүйелер туралы көбірек айтылады: Жалға алу: таратылған файл кэшінің үйлесімділігі үшін тиімді қатеге төзімді механизм.

Сәтсіздікке қайталап көріңіз

Клиент құлыпты ала алмаса, ол кездейсоқ кідірістен кейін әрекетті қайталауы керек; бұл бір ресурста бір уақытта құлып алуға тырысатын бірнеше клиенттерді синхрондауды жою үшін жасалады (бұл жеңімпаздар жоқ «мидың бөлінуі» жағдайына әкелуі мүмкін). Бұған қоса, клиент Redis даналарының көпшілігінде құлыпты тезірек алуға тырысса, бөлінген ми жағдайы орын алуы мүмкін терезе соғұрлым тар болады (және соғұрлым қайталау қажеттілігі азаяды). Сондықтан, ең дұрысы, клиент мультиплекстеу арқылы SET пәрмендерін N даналарға бір уақытта жіберуге тырысуы керек.

Құлыптардың көпшілігін ала алмаған клиенттер үшін ресурстағы құлыпты қайтадан сатып алмас бұрын кілттің мерзімінің аяқталуын күтпеу үшін (ішінара) алынған құлыптарды босатудың қаншалықты маңызды екенін осы жерде атап өткен жөн. (бірақ желі фрагментациясы орын алса және клиент Redis даналарымен байланысын жоғалтса, кілттің жарамдылық мерзімінің аяқталуын күту кезінде қолжетімділік үшін айыппұл төлеуге тура келеді).

Құлыпты босатыңыз

Құлыптан босату - клиенттің белгілі бір дананы сәтті құлыптағанына қарамастан, барлық даналардың құлпын ашуды талап ететін қарапайым операция.

Қауіпсіздікті қарастыру

Алгоритм қауіпсіз бе? Әртүрлі сценарийлерде не болатынын елестетуге тырысайық.

Бастау үшін, клиент көптеген даналарда құлыпты ала алды деп есептейік. Әрбір данада барлығына бірдей қызмет ету мерзімі бар кілт болады. Дегенмен, бұл кілттердің әрқайсысы басқа уақытта орнатылды, сондықтан олардың мерзімі әртүрлі уақытта аяқталады. Бірақ, егер бірінші кілт T1-ден кем емес уақытта орнатылса (бірінші серверге хабарласпас бұрын таңдаған уақыт) және соңғы кілт T2-ден кем емес уақытта орнатылса (жауап алынған уақыт). соңғы серверден), содан кейін мерзімі бітетін жиынтықтағы бірінші кілт кем дегенде аман болатынына сенімдіміз MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT. Барлық басқа кілттердің мерзімі кейінірек бітеді, сондықтан біз барлық кілттердің кем дегенде осы уақыт ішінде бір уақытта жарамды болатынына сенімді бола аламыз.

Көптеген кілттер жарамды болып қалатын уақыт ішінде басқа клиент құлыпты ала алмайды, себебі N/2+1 SET NX операциялары N/2+1 кілттері бұрыннан бар болса, сәтті бола алмайды. Сондықтан құлыпты бір рет сатып алған соң, оны бір уақытта қайта алу мүмкін емес (бұл өзара алып тастау қасиетін бұзады).
Дегенмен, біз бір уақытта құлыпты алуға тырысатын бірнеше клиенттің бір уақытта табысқа жете алмайтынына көз жеткізгіміз келеді.

Клиент даналардың көпшілігін құлыптау ұзақтығының максималды ұзақтығына дейін немесе одан көп уақытқа құлыптаса, ол құлыпты жарамсыз деп санайды және даналардың құлпын ашады. Сондықтан, клиент жарамдылық мерзімінен аз уақыт ішінде көптеген даналарды бұғаттай алған жағдайды ғана ескеруіміз керек. Бұл жағдайда, жоғарыда келтірілген дәлелге қатысты, уақыт ішінде MIN_VALIDITY ешбір клиент құлыпты қайта ала алмайды. Сондықтан көптеген клиенттер N/2+1 даналарын бір уақытта құлыптай алады (ол 2-кезеңнің соңында аяқталады) тек көпшілікті құлыптау уақыты құлыпты жарамсыз ететін TTL уақытынан үлкен болған кезде ғана.

Қауіпсіздіктің ресми дәлелін бере аласыз ба, бар ұқсас алгоритмдерді көрсете аласыз ба немесе жоғарыдағы қатені таба аласыз ба?

Арнайы мүмкіндіктерді қарастыру

Жүйенің қолжетімділігі үш негізгі сипаттамаға байланысты:

  1. Құлыптарды автоматты түрде босату (кілттердің жарамдылық мерзімі біткенде): кілттер құлыптар үшін пайдалану үшін ақырында қол жетімді болады.
  2. Клиенттер әдетте қалаған құлып алынбаған немесе сатып алынған және жұмыс аяқталған кезде құлыптарды алып тастау арқылы бір-біріне көмектесетіндігі; сондықтан құлыпты қайта алу үшін кілттердің мерзімі біткенше күтудің қажеті жоқ шығар.
  3. Клиент құлыпты алу үшін қайта әрекет жасау қажет болғанда, ол көптеген құлыптарды алу үшін қажетті кезеңнен салыстырмалы түрде ұзағырақ уақыт күтеді. Бұл ресурстар үшін бәсекелесу кезінде туындайтын екі жақты жағдайдың ықтималдығын азайтады.

Дегенмен, желі сегменттерінің TTL-ге тең қолжетімділік айыппұлы бар, сондықтан егер сабақтас сегменттер болса, айыппұл шексіз болуы мүмкін. Бұл клиент құлыпты алып, оны босатпай тұрып басқа сегментке жұлып алған сайын орын алады.

Негізінде, шексіз іргелес желі сегменттерін ескере отырып, жүйе шексіз уақыт кезеңі ішінде қолжетімсіз болып қалуы мүмкін.

Өнімділік, орындамау және fsync

Көптеген адамдар Redis-ті пайдаланады, өйткені оларға құлыптарды алу және босату үшін қажетті кідіріс және секундына аяқталуы мүмкін сатып алу/шығару саны тұрғысынан жоғары құлыптау серверінің өнімділігі қажет. Бұл талапты қанағаттандыру үшін кешіктіруді азайту үшін N Redis серверлерімен байланысу стратегиясы бар. Бұл мультиплекстеу стратегиясы (немесе «кедей адамның мультиплексі», мұнда розетка блокталмаған режимге қойылады, барлық пәрмендерді жібереді және клиент пен әрбір дананың арасындағы айналу уақыты ұқсас деп есептей отырып, командаларды кейінірек оқиды) .

Дегенмен, егер біз сәтсіздіктерден сенімді қалпына келтіру үлгісін жасауға ұмтылатын болсақ, деректерді ұзақ мерзімді сақтаумен байланысты қарастыруды да ескеруіміз керек.

Негізінде, мәселені түсіндіру үшін Redis-ті ұзақ мерзімді деректерді сақтау мүмкіндігінсіз конфигурациялаймыз делік. Клиент 3 дананың 5-ін блоктай алады. Клиент бұғаттауға қол жеткізген даналардың бірі қайта іске қосылды және дәл қазір сол ресурс үшін тағы 3 данасы бар, біз оларды блоктай аламыз, ал басқа клиент, өз кезегінде, қауіпсіздік сипатын бұза отырып, қайта іске қосылған дананы блоктай алады. құлыптардың эксклюзивтілігін болжайды.

Алдағы деректерді (AOF) қоссаңыз, жағдай сәл жақсарады. Мысалы, ӨШІРУ пәрменін жіберу және оны қайта іске қосу арқылы серверді алға жылжытуға болады. Redis-тегі жарамдылық мерзімінің аяқталу әрекеттері семантикалық түрде сервер өшірілген кезде де уақыт ағынын жалғастыратын етіп жүзеге асырылғандықтан, біздің барлық талаптарымыз жақсы. Қалыпты өшіру қамтамасыз етілсе, бұл қалыпты жағдай. Электр қуаты үзілген жағдайда не істеу керек? Егер Redis әдепкі бойынша конфигурацияланса, fsync дискіде секунд сайын синхрондалса, қайта іске қосқаннан кейін бізде кілт болмауы мүмкін. Теориялық тұрғыдан, кез келген дананы қайта іске қосу кезінде құлыптау қауіпсіздігіне кепілдік бергіміз келсе, біз қосуымыз керек fsync=always деректерді ұзақ мерзімді сақтауға арналған параметрлерде. Бұл таратылған құлыптарды қауіпсіз енгізу үшін дәстүрлі түрде пайдаланылатын CP жүйелерінің деңгейіне дейін өнімділікті толығымен жояды.

Бірақ жағдай бір қарағанда жақсырақ. Негізінде, алгоритмнің қауіпсіздігі сақталады, себебі сәтсіздіктен кейін дананы қайта іске қосқанда, ол қазір белсенді болып тұрған кез келген құлыпқа енді қатыспайды.

Мұны қамтамасыз ету үшін, сәтсіздіктен кейін дананың біз қолданатын максималды TTL мәнінен сәл асатын уақыт аралығында қолжетімсіз болып қалуын қамтамасыз етуіміз керек. Осылайша, біз жарамдылық мерзімі аяқталғанша және сәтсіздік кезінде белсенді болған барлық кілттердің автоматты түрде босатылуын күтеміз.

Кешіктірілген қайта қосуларды пайдалану, негізінен, Redis-те ұзақ мерзімді тұрақтылық болмаған жағдайда да қауіпсіздікке қол жеткізуге болады. Дегенмен, бұл қол жетімділікті бұзғаны үшін айыппұлға әкелуі мүмкін екенін ескеріңіз. Мысалы, даналардың көпшілігі сәтсіз болса, жүйе TTL үшін жаһандық түрде қолжетімсіз болады (және осы уақыт ішінде ешбір ресурс бұғатталуы мүмкін емес).

Біз алгоритмнің қолжетімділігін арттырамыз: блоктауды кеңейтеміз

Клиенттер орындайтын жұмыс шағын қадамдардан тұратын болса, әдепкі құлыптау ұзақтығын қысқартуға және құлыптарды ұзарту механизмін жүзеге асыруға болады. Негізінде, егер клиент есептеумен айналысса және құлыптың жарамдылық мерзімі қауіпті төмен болса, кілт әлі де бар болса және оның мәні әлі де кездейсоқ мән болған кезде алынған кездейсоқ мән болса, кілттің TTL мәнін кеңейтетін барлық даналарға Lua сценарийін жіберуге болады. құлып алынды.

Клиент жарамдылық мерзімі ішінде даналардың көпшілігін құлыптай алған жағдайда ғана қайта сатып алынатын құлыпты қарастыруы керек.

Рас, техникалық алгоритм өзгермейді, сондықтан құлыптарды алудың қайталанатын әрекеттерінің максималды саны шектелуі керек, әйтпесе қол жетімділік сипаттары бұзылады.

Ақпарат көзі: www.habr.com

пікір қалдыру