Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Բարձր կատարողականությունը մեծ տվյալների հետ աշխատելու հիմնական պահանջներից մեկն է: Սբերբանկի տվյալների բեռնման բաժնում մենք գրեթե բոլոր գործարքները մղում ենք մեր Hadoop-ի վրա հիմնված Data Cloud-ի մեջ և, հետևաբար, գործ ունենք տեղեկատվության իսկապես մեծ հոսքերի հետ: Բնականաբար, մենք միշտ ուղիներ ենք փնտրում արդյունավետությունը բարելավելու համար, և այժմ ուզում ենք պատմել, թե ինչպես կարողացանք կարկատել RegionServer HBase-ը և HDFS հաճախորդը, ինչի շնորհիվ մենք կարողացանք զգալիորեն մեծացնել ընթերցման գործողությունների արագությունը:
Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Այնուամենայնիվ, նախքան բարելավումների էությանը անցնելը, արժե խոսել սահմանափակումների մասին, որոնք, սկզբունքորեն, չեն կարող շրջանցվել, եթե նստած եք HDD-ի վրա:

Ինչու HDD-ն ու արագ պատահական մուտքի ընթերցումները անհամատեղելի են
Ինչպես գիտեք, HBase-ը և շատ այլ տվյալների բազաներ տվյալները պահում են մի քանի տասնյակ կիլոբայթ չափի բլոկներում: Լռելյայն այն մոտ 64 ԿԲ է: Հիմա եկեք պատկերացնենք, որ մենք պետք է ստանանք ընդամենը 100 բայթ, և մենք խնդրում ենք HBase-ին տրամադրել այս տվյալները՝ օգտագործելով որոշակի բանալի։ Քանի որ HFiles-ում բլոկի չափը 64 ԿԲ է, հարցումը 640 անգամ ավելի մեծ կլինի (ընդամենը մեկ րոպե):

Հաջորդը, քանի որ հարցումը կանցնի HDFS-ի և դրա մետատվյալների քեշավորման մեխանիզմի միջոցով ShortCircuitCache (որը թույլ է տալիս ուղղակի մուտք գործել ֆայլեր), դա հանգեցնում է սկավառակից արդեն 1 ՄԲ կարդալուն: Այնուամենայնիվ, սա կարող է ճշգրտվել պարամետրով dfs.client.read.shortcircuit.buffer.size և շատ դեպքերում իմաստ ունի նվազեցնել այս արժեքը, օրինակ մինչև 126 ԿԲ:

Ենթադրենք, որ մենք դա անում ենք, բայց բացի այդ, երբ մենք սկսում ենք տվյալներ կարդալ java api-ի միջոցով, ինչպիսիք են FileChannel.read-ի նման գործառույթները և խնդրում ենք օպերացիոն համակարգին կարդալ նշված քանակի տվյալները, այն կարդում է «միայն դեպքում» 2 անգամ ավելի շատ: , այսինքն. 256 ԿԲ մեր դեպքում: Դա պայմանավորված է նրանով, որ java-ն այս պահվածքը կանխելու համար FADV_RANDOM դրոշը սահմանելու հեշտ միջոց չունի:

Արդյունքում, մեր 100 բայթը ստանալու համար գլխարկի տակ կարդում են 2600 անգամ ավելին: Թվում է, թե լուծումն ակնհայտ է, եկեք բլոկի չափը կրճատենք մինչև կիլոբայթ, նշենք նշված դրոշը և ձեռք բերենք մեծ լուսավորական արագացում։ Բայց խնդիրն այն է, որ բլոկի չափը 2 անգամ փոքրացնելով, մենք նաև 2 անգամ կրճատում ենք մեկ միավորի համար կարդացվող բայթերի քանակը:

FADV_RANDOM դրոշը դնելուց որոշակի շահույթ կարելի է ստանալ, բայց միայն բարձր բազմաթելերով և 128 ԿԲ բլոկի չափով, բայց սա առավելագույնը մի քանի տասնյակ տոկոս է.

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Փորձարկումներն անցկացվել են 100 ֆայլերի վրա, որոնցից յուրաքանչյուրը 1 ԳԲ է և տեղակայված է 10 HDD-ի վրա:

Եկեք հաշվարկենք, թե ինչի վրա կարող ենք սկզբունքորեն հաշվել այս արագությամբ.
Ենթադրենք, մենք կարդում ենք 10 սկավառակից 280 ՄԲ/վ արագությամբ, այսինքն. 3 միլիոն անգամ 100 բայթ: Բայց ինչպես հիշում ենք, մեզ անհրաժեշտ տվյալները 2600 անգամ պակաս են կարդացածից: Այսպիսով, 3 միլիոնը բաժանում ենք 2600-ի և ստանում 1100 գրառում վայրկյանում։

Վհատեցուցիչ, այնպես չէ՞: Այդպիսին է բնությունը Պատահական մուտք HDD-ի տվյալների հասանելիություն՝ անկախ բլոկի չափից: Սա պատահական մուտքի ֆիզիկական սահմանն է, և ոչ մի տվյալների բազա չի կարող ավելին սեղմել նման պայմաններում:

Ինչպե՞ս են այդ դեպքում տվյալների շտեմարանները հասնում շատ ավելի բարձր արագությունների: Այս հարցին պատասխանելու համար եկեք տեսնենք, թե ինչ է կատարվում հետևյալ նկարում.

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Այստեղ մենք տեսնում ենք, որ առաջին մի քանի րոպեների ընթացքում արագությունը իսկապես կազմում է մոտ հազար ռեկորդ վայրկյանում: Այնուամենայնիվ, հետագայում, քանի որ պահանջվածից շատ ավելին է ընթերցվում, տվյալները հայտնվում են օպերացիոն համակարգի (linux) բուֆ/քեշում, և արագությունը մեծանում է մինչև վայրկյանում ավելի արժանապատիվ 60 հազար:

Այսպիսով, հետագայում մենք կզբաղվենք մուտքի արագացմամբ միայն այն տվյալներին, որոնք գտնվում են ՕՀ-ի քեշում կամ գտնվում են SSD/NVMe պահեստավորման սարքերում՝ համեմատելի մուտքի արագությամբ:

Մեր դեպքում մենք թեստեր կանցկացնենք 4 սերվերից բաղկացած նստարանին, որոնցից յուրաքանչյուրը գանձվում է հետևյալ կերպ.

Պրոցեսոր՝ Xeon E5-2680 v4 @ 2.40 ԳՀց 64 թելեր:
Հիշողություն՝ 730 ԳԲ։
java տարբերակ՝ 1.8.0_111

Եվ այստեղ առանցքային կետը աղյուսակների տվյալների քանակն է, որը պետք է կարդալ: Փաստն այն է, որ եթե դուք տվյալներ եք կարդում աղյուսակից, որն ամբողջությամբ տեղադրված է HBase քեշում, ապա այն նույնիսկ չի հասնի կարդալու օպերացիոն համակարգի buff/cache-ից: Քանի որ HBase-ը լռելյայն հատկացնում է հիշողության 40%-ը BlockCache կոչվող կառուցվածքին: Ըստ էության, սա ConcurrentHashMap-ն է, որտեղ բանալին ֆայլի անունն է + բլոկի օֆսեթը, իսկ արժեքը՝ իրական տվյալներն այս օֆսեթում:

Այսպիսով, միայն այս կառույցից կարդալիս մենք մենք տեսնում ենք գերազանց արագություն, ինչպես վայրկյանում մեկ միլիոն հարցում: Բայց եկեք պատկերացնենք, որ մենք չենք կարող հարյուրավոր գիգաբայթ հիշողություն հատկացնել միայն տվյալների բազայի կարիքների համար, քանի որ կան շատ այլ օգտակար բաներ, որոնք աշխատում են այս սերվերների վրա:

Օրինակ, մեր դեպքում մեկ RS-ում BlockCache-ի ծավալը կազմում է մոտ 12 ԳԲ: Մենք վայրէջք կատարեցինք երկու RS մեկ հանգույցի վրա, այսինքն. Բոլոր հանգույցներում BlockCache-ի համար հատկացված է 96 ԳԲ: Եվ շատ անգամ ավելի շատ տվյալներ կան, օրինակ, թող լինի 4 աղյուսակ, յուրաքանչյուրը 130 ռեգիոն, որոնցում ֆայլերը 800 ՄԲ չափի են՝ սեղմված FAST_DIFF-ով, այսինքն. ընդհանուր 410 ԳԲ (սա մաքուր տվյալ է, այսինքն՝ առանց վերարտադրության գործակիցը հաշվի առնելու):

Այսպիսով, BlockCache-ը կազմում է տվյալների ընդհանուր ծավալի ընդամենը մոտ 23%-ը, և սա շատ ավելի մոտ է BigData կոչվող իրական պայմաններին: Եվ հենց այստեղ է սկսվում զվարճանքը, քանի որ ակնհայտ է, որ որքան քիչ է քեշը, այնքան վատ է կատարումը: Ի վերջո, եթե բաց թողնեք, ստիպված կլինեք շատ աշխատանք կատարել, այսինքն. անցեք համակարգի գործառույթների զանգերին: Այնուամենայնիվ, դա հնարավոր չէ խուսափել, ուստի եկեք նայենք բոլորովին այլ տեսանկյունից. ի՞նչ է պատահում քեշի ներսում գտնվող տվյալների հետ:

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

1. Տեղադրեք բլոկ 1-ը քեշում
2. Հեռացրեք բլոկ 1-ը քեշից
3. Տեղադրեք բլոկ 2-ը քեշում
4. Հեռացրեք բլոկ 2-ը քեշից
5. Տեղադրեք բլոկ 3-ը քեշում

5 գործողություն ավարտված է: Սակայն այս իրավիճակը նորմալ անվանել չի կարելի, իրականում մենք HBase-ին ստիպում ենք մի փունջ լրիվ անպետք աշխատանք կատարել։ Այն անընդհատ կարդում է տվյալներ OS-ի քեշից, տեղադրում այն ​​BlockCache-ում, միայն թե դրանք գրեթե անմիջապես դուրս է նետվում, քանի որ տվյալների նոր մաս է հասել: Գրառման սկզբի անիմացիան ցույց է տալիս խնդրի էությունը՝ Աղբահանը դուրս է գալիս մասշտաբներից, մթնոլորտը տաքանում է, փոքրիկ Գրետան հեռավոր ու տաք Շվեդիայում նեղվում է։ Եվ մենք ՏՏ ոլորտի մարդիկ իսկապես չենք սիրում, երբ երեխաները տխուր են, ուստի մենք սկսում ենք մտածել, թե ինչ կարող ենք անել դրա դեմ:

Իսկ եթե քեշի մեջ դնես ոչ բոլոր բլոկները, այլ դրանց միայն որոշակի տոկոսը, որպեսզի քեշը չհորդանա: Եկեք սկսենք պարզապես ավելացնելով ընդամենը մի քանի տող կոդ գործառույթի սկզբին՝ տվյալներ BlockCache-ում տեղադրելու համար.

  public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory) {
    if (cacheDataBlockPercent != 100 && buf.getBlockType().isData()) {
      if (cacheKey.getOffset() % 100 >= cacheDataBlockPercent) {
        return;
      }
    }
...

Բանն այստեղ հետևյալն է. օֆսեթը ֆայլում բլոկի դիրքն է, և նրա վերջին թվանշանները պատահականորեն և հավասարաչափ բաշխված են 00-ից մինչև 99: Հետևաբար, մենք բաց կթողնենք միայն այն, որոնք ընկնում են մեզ անհրաժեշտ տիրույթում:

Օրինակ, սահմանեք cacheDataBlockPercent = 20 և տեսեք, թե ինչ է տեղի ունենում.

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Արդյունքն ակնհայտ է. Ստորև բերված գծապատկերներում պարզ է դառնում, թե ինչու է տեղի ունեցել նման արագացում. մենք խնայում ենք GC-ի շատ ռեսուրսներ՝ առանց սիզիփյան աշխատանքի՝ տվյալների քեշում տեղադրելու, միայն այն անմիջապես նետելու մարսյան շների արտահոսքը:

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Միևնույն ժամանակ, պրոցեսորի օգտագործումը մեծանում է, բայց շատ ավելի քիչ է, քան արտադրողականությունը.

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Հարկ է նաև նշել, որ BlockCache-ում պահվող բլոկները տարբեր են: Մեծ մասը՝ մոտ 95%-ը, ինքնին տվյալներն են: Իսկ մնացածը մետատվյալներ են, ինչպիսիք են Bloom ֆիլտրերը կամ LEAF_INDEX և т.д.. Այս տվյալները բավարար չեն, բայց շատ օգտակար են, քանի որ նախքան տվյալներին ուղղակիորեն մուտք գործելը, HBase-ը դիմում է մետա-ին, որպեսզի հասկանա, թե արդյոք անհրաժեշտ է որոնել այստեղ հետագա և, եթե այո, որտեղ է գտնվում կոնկրետ հետաքրքրության բլոկն:

Հետևաբար, կոդում մենք տեսնում ենք ստուգման պայման buf.getBlockType().isData() և այս մետայի շնորհիվ մենք այն ամեն դեպքում կթողնենք քեշում։

Այժմ եկեք մեծացնենք բեռը և մի փոքր խստացնենք հնարավորությունը մեկ քայլով: Առաջին փորձարկման ժամանակ մենք դարձրեցինք անջատման տոկոսը = 20, և BlockCache-ը մի փոքր չօգտագործվեց: Այժմ եկեք սահմանենք այն 23% և յուրաքանչյուր 100 րոպեն մեկ ավելացնենք 5 թել՝ տեսնելու, թե որ կետում է տեղի ունենում հագեցվածությունը.

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Այստեղ մենք տեսնում ենք, որ օրիգինալ տարբերակը գրեթե անմիջապես հարվածում է առաստաղին՝ վայրկյանում մոտ 100 հազար հարցում: Մինչդեռ կարկատանը տալիս է մինչև 300 հազ. արագացում։ Միևնույն ժամանակ, պարզ է, որ հետագա արագացումն այլևս այնքան էլ «անվճար» չէ, աճում է նաև պրոցեսորի օգտագործումը։

Այնուամենայնիվ, սա այնքան էլ էլեգանտ լուծում չէ, քանի որ մենք նախապես չգիտենք, թե բլոկների քանի տոկոսը պետք է պահվի, դա կախված է բեռնվածության պրոֆիլից: Հետևաբար, ներդրվել է այս պարամետրը ավտոմատ կերպով կարգավորելու մեխանիզմ՝ կախված ընթերցման գործողությունների ակտիվությունից:

Սա վերահսկելու համար ավելացվել է երեք տարբերակ.

hbase.lru.cache.heavy.eviction.count.limit — սահմանում է, թե քանի անգամ պետք է գործարկվի քեշից տվյալների հեռացման գործընթացը, նախքան մենք սկսենք օգտագործել օպտիմալացում (այսինքն՝ բաց թողնել բլոկները): Լռելյայնորեն այն հավասար է MAX_INT = 2147483647-ի և իրականում նշանակում է, որ ֆունկցիան երբեք չի սկսի աշխատել այս արժեքով: Քանի որ վտարման գործընթացը սկսվում է յուրաքանչյուր 5 - 10 վայրկյանը մեկ (դա կախված է բեռից) և 2147483647 * 10 / 60 / 60 / 24 / 365 = 680 տարի: Այնուամենայնիվ, մենք կարող ենք սահմանել այս պարամետրը 0-ի և գործարկումից անմիջապես հետո գործառույթը գործարկել:

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

Օրինակ, մենք գիտենք, որ կարճաժամկետ ընթերցումները սովորաբար տևում են մոտ 1 րոպե: Կարիք չկա սկսել բլոկներ դուրս նետել, քեշը ժամանակ չի ունենա հնանալու, և այնուհետև մենք կարող ենք այս պարամետրը հավասարեցնել, օրինակ, 10-ի: Դա կհանգեցնի նրան, որ օպտիմալացումը կսկսի գործել միայն այն դեպքում, երբ երկար- տերմինի ակտիվ ընթերցանությունը սկսվել է, այսինքն. 100 վայրկյանում։ Այսպիսով, եթե մենք ունենանք կարճաժամկետ ընթերցում, ապա բոլոր բլոկները կմտնեն քեշ և հասանելի կլինեն (բացառությամբ նրանց, որոնք կվտարվեն ստանդարտ ալգորիթմով): Եվ երբ մենք երկարաժամկետ ընթերցումներ ենք անում, գործառույթը միացված է, և մենք կունենայինք շատ ավելի բարձր կատարողականություն:

hbase.lru.cache.heavy.eviction.mb.size.limit — սահմանում է, թե քանի մեգաբայթ կուզենայինք տեղադրել քեշում (և, իհարկե, վտարել) 10 վայրկյանում։ Գործառույթը կփորձի հասնել այս արժեքին և պահպանել այն: Բանն այն է, որ եթե մենք գիգաբայթերը խցկենք քեշի մեջ, ապա ստիպված կլինենք վտարել գիգաբայթերը, և դա, ինչպես տեսանք վերևում, շատ թանկ է: Այնուամենայնիվ, չպետք է փորձեք այն չափազանց փոքր դնել, քանի որ դա կհանգեցնի արգելափակման բաց թողնման ռեժիմի վաղաժամկետ դուրս գալուն: Հզոր սերվերների համար (մոտ 20-40 ֆիզիկական միջուկ) օպտիմալ է սահմանել մոտ 300-400 ՄԲ: Միջին դասի համար (~10 միջուկ) 200-300 ՄԲ: Թույլ համակարգերի համար (2-5 միջուկ) 50-100 ՄԲ կարող է նորմալ լինել (չփորձարկված դրանց վրա):

Եկեք նայենք, թե ինչպես է սա աշխատում. ենթադրենք, մենք սահմանել ենք hbase.lru.cache.heavy.eviction.mb.size.limit = 500, կա ինչ-որ բեռնվածություն (ընթերցում) և այնուհետև յուրաքանչյուր ~10 վայրկյանը մեկ մենք հաշվարկում ենք, թե քանի բայթ է եղել: վտարվել՝ օգտագործելով բանաձևը.

Overhead = Freed Bytes Sum (MB) * 100 / Limit (MB) - 100;

Եթե ​​իրականում վտարվել է 2000 ՄԲ, ապա վերին ծախսը հավասար է.

2000 * 100 / 500 - 100 = 300%

Ալգորիթմները փորձում են պահպանել ոչ ավելի, քան մի քանի տասնյակ տոկոս, այնպես որ գործառույթը կնվազեցնի քեշավորված բլոկների տոկոսը՝ դրանով իսկ ներդնելով ինքնակարգավորման մեխանիզմ:

Այնուամենայնիվ, եթե ծանրաբեռնվածությունը իջնի, ենթադրենք միայն 200 ՄԲ-ը դուրս է մղվում, և Overhead-ը դառնում է բացասական (այսպես կոչված գերակատարում).

200 * 100 / 500 - 100 = -60%

Ընդհակառակը, գործառույթը կավելացնի քեշավորված բլոկների տոկոսը, մինչև Overhead-ը դառնա դրական:

Ստորև բերված է մի օրինակ, թե ինչպես է դա երևում իրական տվյալների վրա: Պետք չէ փորձել հասնել 0%-ի, դա անհնար է։ Շատ լավ է, երբ այն կազմում է մոտ 30 - 100%, սա օգնում է խուսափել կարճաժամկետ թռիչքների ժամանակ օպտիմալացման ռեժիմից վաղաժամ ելքից:

hbase.lru.cache.heavy.eviction.overhead.coefficient — սահմանում է, թե որքան արագ ենք մենք ցանկանում ստանալ արդյունքը: Եթե ​​մենք հաստատ գիտենք, որ մեր ընթերցումները հիմնականում երկար են և չենք ցանկանում սպասել, կարող ենք ավելացնել այս հարաբերակցությունը և ավելի արագ ստանալ բարձր կատարողականություն:

Օրինակ, մենք սահմանել ենք այս գործակիցը = 0.01: Սա նշանակում է, որ Overhead-ը (տես վերևում) կբազմապատկվի այս թվով ստացված արդյունքով, և քեշավորված բլոկների տոկոսը կկրճատվի: Ենթադրենք, որ Overhead = 300% և գործակից = 0.01, ապա քեշավորված բլոկների տոկոսը կնվազի 3% -ով:

Նմանատիպ «Backpressure» տրամաբանությունն իրականացվում է նաև բացասական Overhead (գերանցում) արժեքների համար: Քանի որ ընթերցումների և վտարումների ծավալի կարճաժամկետ տատանումները միշտ հնարավոր են, այս մեխանիզմը թույլ է տալիս խուսափել օպտիմալացման ռեժիմից վաղաժամ ելքից: Backpressure-ն ունի շրջված տրամաբանություն. որքան ուժեղ է գերակատարումը, այնքան ավելի շատ բլոկներ են պահվում:

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Իրականացման կոդը

        LruBlockCache cache = this.cache.get();
        if (cache == null) {
          break;
        }
        freedSumMb += cache.evict()/1024/1024;
        /*
        * Sometimes we are reading more data than can fit into BlockCache
        * and it is the cause a high rate of evictions.
        * This in turn leads to heavy Garbage Collector works.
        * So a lot of blocks put into BlockCache but never read,
        * but spending a lot of CPU resources.
        * Here we will analyze how many bytes were freed and decide
        * decide whether the time has come to reduce amount of caching blocks.
        * It help avoid put too many blocks into BlockCache
        * when evict() works very active and save CPU for other jobs.
        * More delails: https://issues.apache.org/jira/browse/HBASE-23887
        */

        // First of all we have to control how much time
        // has passed since previuos evict() was launched
        // This is should be almost the same time (+/- 10s)
        // because we get comparable volumes of freed bytes each time.
        // 10s because this is default period to run evict() (see above this.wait)
        long stopTime = System.currentTimeMillis();
        if ((stopTime - startTime) > 1000 * 10 - 1) {
          // Here we have to calc what situation we have got.
          // We have the limit "hbase.lru.cache.heavy.eviction.bytes.size.limit"
          // and can calculte overhead on it.
          // We will use this information to decide,
          // how to change percent of caching blocks.
          freedDataOverheadPercent =
            (int) (freedSumMb * 100 / cache.heavyEvictionMbSizeLimit) - 100;
          if (freedSumMb > cache.heavyEvictionMbSizeLimit) {
            // Now we are in the situation when we are above the limit
            // But maybe we are going to ignore it because it will end quite soon
            heavyEvictionCount++;
            if (heavyEvictionCount > cache.heavyEvictionCountLimit) {
              // It is going for a long time and we have to reduce of caching
              // blocks now. So we calculate here how many blocks we want to skip.
              // It depends on:
             // 1. Overhead - if overhead is big we could more aggressive
              // reducing amount of caching blocks.
              // 2. How fast we want to get the result. If we know that our
              // heavy reading for a long time, we don't want to wait and can
              // increase the coefficient and get good performance quite soon.
              // But if we don't sure we can do it slowly and it could prevent
              // premature exit from this mode. So, when the coefficient is
              // higher we can get better performance when heavy reading is stable.
              // But when reading is changing we can adjust to it and set
              // the coefficient to lower value.
              int change =
                (int) (freedDataOverheadPercent * cache.heavyEvictionOverheadCoefficient);
              // But practice shows that 15% of reducing is quite enough.
              // We are not greedy (it could lead to premature exit).
              change = Math.min(15, change);
              change = Math.max(0, change); // I think it will never happen but check for sure
              // So this is the key point, here we are reducing % of caching blocks
              cache.cacheDataBlockPercent -= change;
              // If we go down too deep we have to stop here, 1% any way should be.
              cache.cacheDataBlockPercent = Math.max(1, cache.cacheDataBlockPercent);
            }
          } else {
            // Well, we have got overshooting.
            // Mayby it is just short-term fluctuation and we can stay in this mode.
            // It help avoid permature exit during short-term fluctuation.
            // If overshooting less than 90%, we will try to increase the percent of
            // caching blocks and hope it is enough.
            if (freedSumMb >= cache.heavyEvictionMbSizeLimit * 0.1) {
              // Simple logic: more overshooting - more caching blocks (backpressure)
              int change = (int) (-freedDataOverheadPercent * 0.1 + 1);
              cache.cacheDataBlockPercent += change;
              // But it can't be more then 100%, so check it.
              cache.cacheDataBlockPercent = Math.min(100, cache.cacheDataBlockPercent);
            } else {
              // Looks like heavy reading is over.
              // Just exit form this mode.
              heavyEvictionCount = 0;
              cache.cacheDataBlockPercent = 100;
            }
          }
          LOG.info("BlockCache evicted (MB): {}, overhead (%): {}, " +
            "heavy eviction counter: {}, " +
            "current caching DataBlock (%): {}",
            freedSumMb, freedDataOverheadPercent,
            heavyEvictionCount, cache.cacheDataBlockPercent);

          freedSumMb = 0;
          startTime = stopTime;
       }

Հիմա այս ամենին նայենք իրական օրինակով։ Մենք ունենք հետևյալ թեստային սցենարը.

  1. Եկեք սկսենք սկան անել (25 շղթա, խմբաքանակ = 100)
  2. 5 րոպե հետո ավելացրեք բազմաշերտ (25 թել, խմբաքանակ = 100)
  3. 5 րոպե անց անջատեք բազմաֆունկցիոնալ սարքերը (մնում է նորից սկանավորումը)

Մենք կատարում ենք երկու գործարկում, նախ hbase.lru.cache.heavy.eviction.count.limit = 10000 (որն իրականում անջատում է հնարավորությունը), այնուհետև սահմանում ենք սահմանաչափ = 0 (միավորում է այն):

Ստորև բերված տեղեկամատյաններում մենք տեսնում ենք, թե ինչպես է գործառույթը միացված և վերակայում Overshooting-ը 14-71%: Ժամանակ առ ժամանակ ծանրաբեռնվածությունը նվազում է, ինչը միացնում է Backpressure-ը և HBase-ը նորից պահում է ավելի շատ բլոկներ։

Մուտք RegionServer
վտարված (MB)՝ 0, հարաբերակցությունը 0.0, վերադիր (%)՝ -100, ծանր վտարման հաշվիչը՝ 0, ընթացիկ պահվող տվյալների բլոկ (%)՝ 100
վտարված (MB)՝ 0, հարաբերակցությունը 0.0, վերադիր (%)՝ -100, ծանր վտարման հաշվիչը՝ 0, ընթացիկ պահվող տվյալների բլոկ (%)՝ 100
վտարված (MB): 2170, հարաբերակցությունը 1.09, վերադիր (%)՝ 985, ծանր վտարման հաշվիչ՝ 1, ընթացիկ պահվող տվյալների բլոկ (%)՝ 91 < սկիզբ
վտարված (MB): 3763, հարաբերակցությունը 1.08, վերադիր (%)՝ 1781, ծանր վտարման հաշվիչը՝ 2, ընթացիկ պահվող տվյալների բլոկ (%)՝ 76
վտարված (MB): 3306, հարաբերակցությունը 1.07, վերադիր (%)՝ 1553, ծանր վտարման հաշվիչը՝ 3, ընթացիկ պահվող տվյալների բլոկ (%)՝ 61
վտարված (MB): 2508, հարաբերակցությունը 1.06, վերադիր (%)՝ 1154, ծանր վտարման հաշվիչը՝ 4, ընթացիկ պահվող տվյալների բլոկ (%)՝ 50
վտարված (MB): 1824, հարաբերակցությունը 1.04, վերադիր (%)՝ 812, ծանր վտարման հաշվիչը՝ 5, ընթացիկ պահվող տվյալների բլոկ (%)՝ 42
վտարված (MB): 1482, հարաբերակցությունը 1.03, վերադիր (%)՝ 641, ծանր վտարման հաշվիչը՝ 6, ընթացիկ պահվող տվյալների բլոկ (%)՝ 36
վտարված (MB): 1140, հարաբերակցությունը 1.01, վերադիր (%)՝ 470, ծանր վտարման հաշվիչը՝ 7, ընթացիկ պահվող տվյալների բլոկ (%)՝ 32
վտարված (MB): 913, հարաբերակցությունը 1.0, վերադիր (%)՝ 356, ծանր վտարման հաշվիչը՝ 8, ընթացիկ պահվող տվյալների բլոկ (%)՝ 29
վտարված (MB): 912, հարաբերակցությունը 0.89, վերադիր (%)՝ 356, ծանր վտարման հաշվիչը՝ 9, ընթացիկ պահվող տվյալների բլոկ (%)՝ 26
վտարված (MB): 684, հարաբերակցությունը 0.76, վերադիր (%)՝ 242, ծանր վտարման հաշվիչը՝ 10, ընթացիկ պահվող տվյալների բլոկ (%)՝ 24
վտարված (MB): 684, հարաբերակցությունը 0.61, վերադիր (%)՝ 242, ծանր վտարման հաշվիչը՝ 11, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 456, հարաբերակցությունը 0.51, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 12, ընթացիկ պահվող տվյալների բլոկ (%)՝ 21
վտարված (MB): 456, հարաբերակցությունը 0.42, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 13, ընթացիկ պահվող տվյալների բլոկ (%)՝ 20
վտարված (MB): 456, հարաբերակցությունը 0.33, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 14, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 15, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 342, հարաբերակցությունը 0.32, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 16, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 342, հարաբերակցությունը 0.31, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 17, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.3, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 18, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.29, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 19, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.27, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 20, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.25, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 21, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.24, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 22, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.22, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 23, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.21, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 24, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.2, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 25, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 228, հարաբերակցությունը 0.17, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 26, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB)՝ 456, հարաբերակցությունը 0.17, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 27, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18 < ավելացված է (բայց աղյուսակը նույնն է)
վտարված (MB): 456, հարաբերակցությունը 0.15, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 28, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 342, հարաբերակցությունը 0.13, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 29, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 342, հարաբերակցությունը 0.11, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 30, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 342, հարաբերակցությունը 0.09, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 31, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 228, հարաբերակցությունը 0.08, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 32, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 228, հարաբերակցությունը 0.07, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 33, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 228, հարաբերակցությունը 0.06, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 34, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 228, հարաբերակցությունը 0.05, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 35, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 228, հարաբերակցությունը 0.05, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 36, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 228, հարաբերակցությունը 0.04, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 37, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 109, հարաբերակցությունը 0.04, վերադիր (%): -46, ծանր վտարման հաշվիչ` 37, ընթացիկ պահվող տվյալների բլոկ (%): 22 < հետադարձ ճնշում
վտարված (MB): 798, հարաբերակցությունը 0.24, վերադիր (%)՝ 299, ծանր վտարման հաշվիչը՝ 38, ընթացիկ պահվող տվյալների բլոկ (%)՝ 20
վտարված (MB): 798, հարաբերակցությունը 0.29, վերադիր (%)՝ 299, ծանր վտարման հաշվիչը՝ 39, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 570, հարաբերակցությունը 0.27, վերադիր (%)՝ 185, ծանր վտարման հաշվիչը՝ 40, ընթացիկ պահվող տվյալների բլոկ (%)՝ 17
վտարված (MB): 456, հարաբերակցությունը 0.22, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 41, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 342, հարաբերակցությունը 0.16, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 42, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 342, հարաբերակցությունը 0.11, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 43, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 228, հարաբերակցությունը 0.09, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 44, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 228, հարաբերակցությունը 0.07, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 45, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 228, հարաբերակցությունը 0.05, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 46, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 222, հարաբերակցությունը 0.04, վերադիր (%)՝ 11, ծանր վտարման հաշվիչը՝ 47, ընթացիկ պահվող տվյալների բլոկ (%)՝ 16
վտարված (MB): 104, հարաբերակցությունը 0.03, վերադիր (%): -48, ծանր վտարման հաշվիչը` 47, ընթացիկ պահվող տվյալների բլոկ (%): 21 < ընդհատվում է
վտարված (MB): 684, հարաբերակցությունը 0.2, վերադիր (%)՝ 242, ծանր վտարման հաշվիչը՝ 48, ընթացիկ պահվող տվյալների բլոկ (%)՝ 19
վտարված (MB): 570, հարաբերակցությունը 0.23, վերադիր (%)՝ 185, ծանր վտարման հաշվիչը՝ 49, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 342, հարաբերակցությունը 0.22, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 50, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 228, հարաբերակցությունը 0.21, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 51, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 228, հարաբերակցությունը 0.2, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 52, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 228, հարաբերակցությունը 0.18, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 53, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 228, հարաբերակցությունը 0.16, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 54, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 228, հարաբերակցությունը 0.14, վերադիր (%)՝ 14, ծանր վտարման հաշվիչը՝ 55, ընթացիկ պահվող տվյալների բլոկ (%)՝ 18
վտարված (MB): 112, հարաբերակցությունը 0.14, վերադիր (%): -44, ծանր վտարման հաշվիչ` 55, ընթացիկ պահվող տվյալների բլոկ (%): 23 < հետադարձ ճնշում
վտարված (MB): 456, հարաբերակցությունը 0.26, վերադիր (%)՝ 128, ծանր վտարման հաշվիչը՝ 56, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.31, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 57, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 58, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 59, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 60, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 61, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 62, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 63, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.32, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 64, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 65, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 66, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.32, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 67, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 68, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.32, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 69, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.32, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 70, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 71, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 72, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 73, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 74, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 75, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB): 342, հարաբերակցությունը 0.33, վերադիր (%)՝ 71, ծանր վտարման հաշվիչը՝ 76, ընթացիկ պահվող տվյալների բլոկ (%)՝ 22
վտարված (MB)՝ 21, հարաբերակցությունը 0.33, վերադիր (%)՝ -90, ծանր վտարման հաշվիչը՝ 76, ընթացիկ պահվող տվյալների բլոկ (%)՝ 32
վտարված (MB)՝ 0, հարաբերակցությունը 0.0, վերադիր (%)՝ -100, ծանր վտարման հաշվիչը՝ 0, ընթացիկ պահվող տվյալների բլոկ (%)՝ 100
վտարված (MB)՝ 0, հարաբերակցությունը 0.0, վերադիր (%)՝ -100, ծանր վտարման հաշվիչը՝ 0, ընթացիկ պահվող տվյալների բլոկ (%)՝ 100

Սկանավորումներն անհրաժեշտ էին ցույց տալու նույն գործընթացը երկու քեշի բաժինների միջև փոխհարաբերությունների գրաֆիկի տեսքով՝ միայնակ (որտեղ բլոկներ, որոնք նախկինում երբեք չեն պահանջվել) և բազմաբնակարան (տվյալները «պահանջվում են» առնվազն մեկ անգամ պահվում են այստեղ):

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

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

Ամբողջական կոդը կարելի է գտնել Pull Request-ում HBASE 23887 github-ում:

Այնուամենայնիվ, վայրկյանում 300 հազար ընթերցում այն ​​ամենը չէ, ինչին կարելի է հասնել այս սարքաշարի վրա այս պայմաններում: Բանն այն է, որ երբ անհրաժեշտ է տվյալների մուտք գործել HDFS-ի միջոցով, օգտագործվում է ShortCircuitCache (այսուհետ՝ SSC) մեխանիզմը, որը թույլ է տալիս ուղղակիորեն մուտք գործել տվյալներ՝ խուսափելով ցանցային փոխազդեցություններից:

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

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Սա գիտակցելով՝ մենք հասկացանք, որ խնդիրը կարելի է շրջանցել՝ ստեղծելով անկախ SSC-ների զանգված.

private final ShortCircuitCache[] shortCircuitCache;
...
shortCircuitCache = new ShortCircuitCache[this.clientShortCircuitNum];
for (int i = 0; i < this.clientShortCircuitNum; i++)
  this.shortCircuitCache[i] = new ShortCircuitCache(…);

Եվ հետո աշխատեք նրանց հետ՝ բացառելով խաչմերուկները նաև վերջին օֆսեթ թվի վրա.

public ShortCircuitCache getShortCircuitCache(long idx) {
    return shortCircuitCache[(int) (idx % clientShortCircuitNum)];
}

Այժմ դուք կարող եք սկսել փորձարկումը: Դա անելու համար մենք HDFS-ից ֆայլեր կկարդանք պարզ բազմաթելային հավելվածով: Սահմանեք պարամետրերը.

conf.set("dfs.client.read.shortcircuit", "true");
conf.set("dfs.client.read.shortcircuit.buffer.size", "65536"); // по дефолту = 1 МБ и это сильно замедляет чтение, поэтому лучше привести в соответствие к реальным нуждам
conf.set("dfs.client.short.circuit.num", num); // от 1 до 10

Եվ պարզապես կարդացեք ֆայլերը.

FSDataInputStream in = fileSystem.open(path);
for (int i = 0; i < count; i++) {
    position += 65536;
    if (position > 900000000)
        position = 0L;
    int res = in.read(position, byteBuffer, 0, 65536);
}

Այս կոդը կատարվում է առանձին թելերով, և մենք կավելացնենք միաժամանակ կարդացվող ֆայլերի քանակը (10-ից մինչև 200՝ հորիզոնական առանցք) և քեշերի քանակը (1-ից 10՝ գրաֆիկական): Ուղղահայաց առանցքը ցույց է տալիս արագացումը, որն առաջանում է SSC-ի աճից այն դեպքում, երբ կա միայն մեկ քեշ:

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Ինչպես կարդալ գրաֆիկը. 100 հազար ընթերցումների կատարման ժամանակը մեկ քեշով 64 ԿԲ բլոկներում պահանջում է 78 վայրկյան: Մինչդեռ 5 քեշի դեպքում դա տեւում է 16 վայրկյան։ Նրանք. կա ~5 անգամ արագացում։ Ինչպես երևում է գրաֆիկից, էֆեկտն այնքան էլ նկատելի չէ փոքր թվով զուգահեռ ընթերցումների դեպքում, այն սկսում է նկատելի դեր խաղալ, երբ կան ավելի քան 50 շղթաների ընթերցումներ: Նկատելի է նաև, որ SSC-ների թիվը 6-ից մեծանում է: և վերևում տալիս է կատարողականի զգալիորեն ավելի փոքր աճ:

Ծանոթագրություն 1. քանի որ թեստի արդյունքները բավականին անկայուն են (տես ստորև), իրականացվել է 3 փորձարկում և ստացված արժեքները միջինացվել են:

Ծանոթագրություն 2. Պատահական մուտքի կազմաձևման արդյունքը նույնն է, թեև մուտքն ինքնին մի փոքր ավելի դանդաղ է:

Այնուամենայնիվ, անհրաժեշտ է հստակեցնել, որ, ի տարբերություն HBase-ի դեպքի, այս արագացումը միշտ չէ, որ անվճար է։ Այստեղ մենք «բացում ենք» պրոցեսորի կարողությունը՝ ավելի շատ աշխատանք կատարելու՝ կողպեքներից կախված լինելու փոխարեն:

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Այստեղ դուք կարող եք նկատել, որ ընդհանուր առմամբ, քեշերի քանակի աճը տալիս է CPU-ի օգտագործման մոտավորապես համամասնական աճ: Այնուամենայնիվ, կան մի փոքր ավելի շատ հաղթող համակցություններ:

Օրինակ, եկեք ավելի սերտ նայենք SSC = 3 պարամետրին: Շարքի վրա կատարողականի աճը մոտ 3.3 անգամ է: Ստորև ներկայացված են բոլոր երեք առանձին փուլերի արդյունքները:

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Մինչդեռ պրոցեսորի սպառումն ավելանում է մոտ 2.8 անգամ։ Տարբերությունն այնքան էլ մեծ չէ, բայց փոքրիկ Գրետան արդեն ուրախ է և կարող է ժամանակ ունենալ դպրոց հաճախելու և դասերի գնալու։

Այսպիսով, սա դրական ազդեցություն կունենա ցանկացած գործիքի համար, որն օգտագործում է HDFS-ի զանգվածային հասանելիություն (օրինակ՝ Spark և այլն), պայմանով, որ հավելվածի ծածկագիրը թեթև է (այսինքն՝ խրոցը գտնվում է HDFS հաճախորդի կողմից) և առկա է պրոցեսորի անվճար սնուցում։ . Ստուգելու համար եկեք ստուգենք, թե ինչ ազդեցություն կունենա BlockCache-ի օպտիմալացման և SSC թյունինգի համատեղ օգտագործումը HBase-ից կարդալու համար:

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Կարելի է տեսնել, որ նման պայմաններում ազդեցությունն այնքան էլ մեծ չէ, որքան զտված թեստերում (ընթերցում է առանց որևէ մշակման), բայց այստեղ միանգամայն հնարավոր է քամել լրացուցիչ 80K: Երկու օպտիմալացումները միասին ապահովում են մինչև 4 անգամ արագություն:

Այս օպտիմալացման համար նույնպես PR արվեց [HDFS-15202], որը միավորվել է, և այս գործառույթը հասանելի կլինի ապագա թողարկումներում:

Եվ վերջապես, հետաքրքիր էր համեմատել նմանատիպ լայն սյունակային տվյալների բազայի՝ Cassandra-ի և HBase-ի ընթերցանության կատարումը:

Դա անելու համար մենք գործարկեցինք ստանդարտ YCSB բեռնվածության փորձարկման կոմունալ օրինակներ երկու հոսթից (ընդհանուր 800 թեմա): Սերվերի կողմից - RegionServer-ի և Cassandra-ի 4 օրինակ 4 հոսթների վրա (ոչ այն, որտեղ հաճախորդները աշխատում են, որպեսզի խուսափեն նրանց ազդեցությունից): Ընթերցումները ստացվել են չափերի աղյուսակներից.

HBase – 300 ԳԲ HDFS-ով (100 ԳԲ մաքուր տվյալներ)

Cassandra - 250 ԳԲ (կրկնօրինակման գործակից = 3)

Նրանք. ծավալը մոտավորապես նույնն էր (HBase-ում մի փոքր ավելի):

HBase պարամետրեր.

dfs.client.short.circuit.num = 5 (HDFS հաճախորդի օպտիմիզացում)

hbase.lru.cache.heavy.eviction.count.limit = 30 - սա նշանակում է, որ կարկատանը կսկսի աշխատել 30 վտարումից հետո (~5 րոպե)

hbase.lru.cache.heavy.eviction.mb.size.limit = 300 - քեշավորման և վտարման նպատակային ծավալը

YCSB տեղեկամատյանները վերլուծվել և կազմվել են Excel գրաֆիկների մեջ.

Ինչպես բարձրացնել ընթերցման արագությունը HBase-ից մինչև 3 անգամ և HDFS-ից մինչև 5 անգամ

Ինչպես տեսնում եք, այս օպտիմալացումները հնարավորություն են տալիս համեմատել այս տվյալների բազաների աշխատանքը այս պայմաններում և հասնել վայրկյանում 450 հազար ընթերցման:

Հուսով ենք, որ այս տեղեկատվությունը կարող է օգտակար լինել ինչ-որ մեկին արտադրողականության համար հետաքրքիր պայքարի ժամանակ:

Source: www.habr.com

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