Բաշխված փական, օգտագործելով Redis

Հե՜յ Հաբր։

Այսօր մենք ձեր ուշադրությանն ենք ներկայացնում Redis-ի միջոցով բաշխված կողպման իրականացման մասին բարդ հոդվածի թարգմանությունը և հրավիրում ենք ձեզ խոսելու Redis-ի հեռանկարների մասին՝ որպես թեմա: Քննարկվող Redlock ալգորիթմի վերլուծություն Մարտին Կլեպմանից, գրքի հեղինակ «Բարձր ծանրաբեռնվածության հավելվածներ», տրված է այստեղ.

Բաշխված կողպումը շատ օգտակար պարզունակ է, որն օգտագործվում է բազմաթիվ միջավայրերում, որտեղ տարբեր գործընթացներ պետք է աշխատեն ընդհանուր ռեսուրսների վրա փոխադարձ բացառող ձևով:

Այնտեղ կան մի շարք գրադարաններ և գրառումներ, որոնք նկարագրում են, թե ինչպես կարելի է իրականացնել DLM (Բաշխված կողպեքի կառավարիչ)՝ օգտագործելով Redis, բայց յուրաքանչյուր գրադարան ունի տարբեր մոտեցում, և նրանց տրամադրած երաշխիքները բավականին թույլ են՝ համեմատած մի փոքր ավելի բարդ դիզայնի հետ:

Այս հոդվածում մենք կփորձենք նկարագրել պայմանական կանոնական ալգորիթմ, որը ցույց է տալիս, թե ինչպես կարելի է իրականացնել բաշխված կողպում Redis-ի միջոցով: Մենք կխոսենք ալգորիթմի մասին, որը կոչվում է Ռեդլոք, այն իրականացնում է բաշխված կողպեքի կառավարիչ և, մեր կարծիքով, այս ալգորիթմն ավելի անվտանգ է, քան սովորական մեկ օրինակով մոտեցումը։ Հուսով ենք, որ համայնքը կվերլուծի այն, կտրամադրի հետադարձ կապ և կօգտագործի այն որպես ելակետ ավելի բարդ կամ այլընտրանքային նախագծերի համար:

Իրականացումներ

Նախքան ալգորիթմի նկարագրությանը անցնելը, մենք տրամադրում ենք մի քանի հղումներ դեպի պատրաստի իրագործումներ։ Նրանք կարող են օգտագործվել որպես հղում:

  • Redlock-rb (իրականացում Ռուբիի համար): Կա նաեւ պատառաքաղ Redlock-rb, որն ավելացնում է փաթեթ (գոհար) բաշխման հեշտության համար և ոչ միայն դրա համար։
  • Redlock-py (Python-ի իրականացում):
  • Aioredlock (իրականացում Asyncio 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-ի համար): Async-ի և կողպման ընդլայնումների աջակցությամբ:
  • ScarletLock (իրականացում C# .NET-ի համար՝ կարգավորելի տվյալների պահեստով)
  • Redlock4Net (իրականացում C# .NET-ի համար)
  • հանգույց-կարմիր արգելափակում (իրականացում NodeJS-ի համար): Ներառում է կողպեքների երկարացման աջակցություն:

Անվտանգության և մատչելիության երաշխիքներ

Մենք պատրաստվում ենք մոդելավորել մեր դիզայնը ընդամենը երեք հատկություններով, որոնք, մեր կարծիքով, ապահովում են նվազագույն երաշխիքներ, որոնք անհրաժեշտ են բաշխված կողպումն արդյունավետ օգտագործելու համար:

  1. Անվտանգության սեփականություն. Փոխադարձ բացառում: Ցանկացած պահի միայն մեկ հաճախորդ կարող է պահել կողպեքը:
  2. Առկայություն Հատկություն A. Փակուղիներ չկան: Միշտ հնարավոր է ի վերջո կողպեք ձեռք բերել, նույնիսկ եթե ռեսուրսը կողպած հաճախորդը ձախողվի կամ ընկնի սկավառակի այլ հատվածում:
  3. Հասանելիություն Հատկություն B. Սխալների հանդուրժողականություն: Քանի դեռ Redis հանգույցների մեծ մասն աշխատում է, հաճախորդները կարող են ձեռք բերել և ազատել կողպեքներ:

Ինչու այս դեպքում ձախողման վերականգնման վրա հիմնված իրականացումը բավարար չէ
Հասկանալու համար, թե ինչն ենք մենք բարելավելու, եկեք վերլուծենք Redis-ի վրա հիմնված կողպման գրադարանների մեծ մասի ներկայիս վիճակը:

Redis-ի միջոցով ռեսուրսը կողպելու ամենապարզ միջոցը օրինակում բանալի ստեղծելն է: Սովորաբար, բանալին ստեղծվում է սահմանափակ ժամկետով, դա ձեռք է բերվում Redis-ում տրամադրված ժամկետանց գործառույթի միջոցով, ուստի վաղ թե ուշ այս բանալին թողարկվում է (հատկությունը 2 մեր ցուցակում): Երբ հաճախորդը պետք է թողարկի ռեսուրսը, այն ջնջում է բանալին:

Առաջին հայացքից այս լուծումը բավականին լավ է աշխատում, բայց կա մի խնդիր՝ մեր ճարտարապետությունը ստեղծում է մեկ ձախողման կետ։ Ի՞նչ է պատահում, եթե հյուրընկալող Redis-ի օրինակը ձախողվի: Ուրեմն եկեք մի ստրուկ ավելացնենք։ Եվ մենք կօգտագործենք այն, եթե հաղորդավարն անհասանելի է: Ցավոք, այս տարբերակը կենսունակ չէ: Դրանով մենք չենք կարողանա պատշաճ կերպով իրականացնել փոխադարձ բացառման հատկությունը, որն անհրաժեշտ է անվտանգությունն ապահովելու համար, քանի որ Redis-ում կրկնօրինակումը ասինխրոն է:

Ակնհայտ է, որ նման մոդելում տեղի է ունենում մրցավազքի պայման.

  1. Հաճախորդ Ա-ն ձեռք է բերում կողպեք վարպետի վրա:
  2. Վարպետը ձախողվում է նախքան բանալին մուտքագրումը ստրուկին փոխանցելը:
  3. Հետևորդը բարձրացվում է առաջնորդի:
  4. Հաճախորդ B-ն ձեռք է բերում կողպեք նույն ռեսուրսի վրա, որը A-ն արդեն կողպել է: ԱՆՎՏԱՆԳՈՒԹՅԱՆ ԽԱԽՏՈՒՄ!

Երբեմն լիովին նորմալ է, որ հատուկ հանգամանքներում, ինչպիսին է ձախողումը, շատ հաճախորդներ կարող են միաժամանակ պահել կողպեքը: Նման դեպքերում կարող է կիրառվել կրկնօրինակման վրա հիմնված լուծում: Հակառակ դեպքում խորհուրդ ենք տալիս այս հոդվածում նկարագրված լուծումը:

Ճիշտ իրականացում մեկ օրինակով

Նախքան վերը նկարագրված մեկ օրինակի կոնֆիգուրացիայի թերությունները հաղթահարելու փորձը, եկեք հասկանանք, թե ինչպես ճիշտ վարվել այս պարզ դեպքի հետ, քանի որ այս լուծումն իրականում վավեր է այն ծրագրերում, որտեղ մրցավազքի պայմանները ժամանակ առ ժամանակ ընդունելի են, ինչպես նաև այն պատճառով, որ արգելափակումը մեկ օրինակը ծառայում է որպես հիմք, որն օգտագործվում է այստեղ նկարագրված բաշխված ալգորիթմում:

Կողպեք ձեռք բերելու համար կատարեք հետևյալը.

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-ի օգտագործումն անվտանգ չէ, քանի որ հաճախորդը կարող է հեռացնել մեկ այլ հաճախորդի կողմից պահվող կողպեքը: Ի հակադրություն, վերը նշված սկրիպտն օգտագործելիս յուրաքանչյուր կողպեքը «ստորագրված է» պատահական տողով, այնպես որ միայն այն հաճախորդը, ով նախկինում տեղադրել է այն, կարող է հեռացնել այն:

Ինչպիսի՞ն պետք է լինի այս պատահական տողը: Ես կռահում եմ, որ այն պետք է լինի 20 բայթ /dev/urandom-ից, բայց դուք կարող եք գտնել ավելի էժան եղանակներ՝ լարը ձեր նպատակների համար եզակի դարձնելու համար: Օրինակ, լավ կլիներ RC4-ը սերմանել /dev/urandom-ով և դրանից հետո ստեղծել կեղծ պատահական հոսք: Ավելի պարզ լուծումը ներառում է unix ժամանակի համադրություն միկրովայրկյան լուծաչափով, գումարած հաճախորդի ID-ն: դա այնքան էլ ապահով չէ, բայց, հավանաբար, շատ համատեքստերում այն ​​համապատասխանում է առաջադրանքին:

Ժամանակը, որը մենք օգտագործում ենք որպես բանալու ծառայության ժամկետի չափ, կոչվում է «կողպման ժամկետ»: Այս արժեքը և՛ կողպեքի ինքնաբերաբար արձակումից առաջ, և՛ այն ժամանակն է, որը հաճախորդը պետք է ավարտի գործողությունը, մինչև մեկ այլ հաճախորդ կարողանա կողպել այդ ռեսուրսը, իրականում չխախտելով փոխադարձ բացառման երաշխիքները: Այս երաշխիքը սահմանափակվում է միայն որոշակի ժամանակի պատուհանով, որը սկսվում է կողպեքի գնման պահից:

Այսպիսով, մենք քննարկել ենք կողպեք ձեռք բերելու և ազատելու լավ միջոց: Համակարգը (եթե մենք խոսում ենք մեկ և միշտ հասանելի օրինակից բաղկացած ոչ բաշխված համակարգի մասին) ապահով է։ Եկեք այս հայեցակարգը տարածենք բաշխված համակարգի վրա, որտեղ նման երաշխիքներ չունենք։

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), ապա իրավիճակը մի փոքր կբարելավվի: Օրինակ, դուք կարող եք խթանել սերվերը՝ ուղարկելով SHUTDOWN հրամանը և վերագործարկելով այն: Քանի որ Redis-ում ժամկետանց գործողությունները իմաստային կերպով իրականացվում են այնպես, որ ժամանակը շարունակում է հոսել նույնիսկ այն ժամանակ, երբ սերվերն անջատված է, մեր բոլոր պահանջները լավ են: Սա նորմալ է, քանի դեռ ապահովված է նորմալ անջատում: Ի՞նչ անել հոսանքազրկման դեպքում. Եթե ​​Redis-ը կազմաձևված է լռելյայնորեն՝ fsync-ով համաժամանակացնելով սկավառակի վրա ամեն վայրկյան, ապա հնարավոր է, որ վերագործարկումից հետո մենք չունենանք մեր բանալին։ Տեսականորեն, եթե մենք ցանկանում ենք երաշխավորել կողպեքի անվտանգությունը ցանկացած օրինակի վերագործարկման ժամանակ, մենք պետք է միացնենք fsync=always երկարաժամկետ տվյալների պահպանման կարգավորումներում: Սա լիովին կսպանի կատարողականությունը՝ մինչև CP համակարգերի մակարդակը, որոնք ավանդաբար օգտագործվում են բաշխված կողպեքները ապահով կերպով իրականացնելու համար:

Բայց իրավիճակն ավելի լավ է, քան թվում է առաջին հայացքից։ Սկզբունքորեն, ալգորիթմի անվտանգությունը պահպանվում է, քանի որ երբ օրինակը վերագործարկվում է ձախողումից հետո, այն այլևս չի մասնակցում որևէ կողպեքի, որն այժմ ակտիվ է:

Դա ապահովելու համար մենք պարզապես պետք է ապահովենք, որ ձախողումից հետո օրինակը անհասանելի մնա որոշակի ժամանակահատվածում՝ մի փոքր գերազանցելով մեր օգտագործած առավելագույն TTL-ը: Այս կերպ մենք կսպասենք մինչև գործողության ժամկետի ավարտը և բոլոր ստեղների ավտոմատ թողարկումը, որոնք ակտիվ էին ձախողման պահին:

Օգտագործելով հետաձգված վերագործարկումներ, սկզբունքորեն հնարավոր է հասնել անվտանգության նույնիսկ Redis-ում որևէ երկարաժամկետ համառության բացակայության դեպքում: Նկատի ունեցեք, սակայն, որ դա կարող է հանգեցնել տուգանքի՝ մատչելիությունը խախտելու համար: Օրինակ, եթե դեպքերի մեծ մասը ձախողվի, համակարգը գլոբալ առումով անհասանելի կդառնա TTL-ի համար (և այս ընթացքում ոչ մի ռեսուրս չի կարող արգելափակվել):

Մենք մեծացնում ենք ալգորիթմի հասանելիությունը՝ երկարացնում ենք արգելափակումը

Եթե ​​հաճախորդների կողմից կատարված աշխատանքը բաղկացած է փոքր քայլերից, ապա հնարավոր է կրճատել լռելյայն կողպման տևողությունը և կիրառել կողպեքների երկարացման մեխանիզմ: Սկզբունքորեն, եթե հաճախորդը զբաղված է հաշվարկներով, և կողպեքի ժամկետի արժեքը վտանգավոր ցածր է, դուք կարող եք բոլոր օրինակներին ուղարկել Lua սկրիպտ, որը երկարացնում է բանալու TTL-ը, եթե բանալին դեռ գոյություն ունի, և դրա արժեքը դեռ պատահական արժեք է, որը ստացվել է կողպեքի ձեռքբերման ժամանակ: .

Հաճախորդը պետք է դիտարկի միայն այն դեպքում, երբ կողպեքը նորից ձեռք բերվի, եթե նրան հաջողվել է կողպել դեպքերի մեծ մասը վավերականության ժամկետի ընթացքում:

Ճիշտ է, տեխնիկապես ալգորիթմը չի փոխվում, ուստի կողպեքներ ձեռք բերելու կրկնվող փորձերի առավելագույն քանակը պետք է սահմանափակվի, հակառակ դեպքում հասանելիության հատկությունները կխախտվեն:

Source: www.habr.com

Добавить комментарий