25TB-ի վերլուծություն՝ օգտագործելով AWK և R

25TB-ի վերլուծություն՝ օգտագործելով AWK և R
Ինչպես կարդալ այս հոդվածըՆերողություն եմ խնդրում տեքստի այսքան երկար ու քաոսային լինելու համար։ Ձեր ժամանակը խնայելու համար ես յուրաքանչյուր գլուխ սկսում եմ «Ինչ սովորեցի» ներածությամբ, որն ամփոփում է գլխի էությունը մեկ կամ երկու նախադասությամբ:

«Ուղղակի ցույց տուր ինձ լուծումը»: Եթե ​​պարզապես ուզում եք տեսնել, թե որտեղից եմ եկել, ապա անցեք «Ավելի հնարամիտ դառնալ» գլխին, բայց կարծում եմ, որ անհաջողության մասին կարդալն ավելի հետաքրքիր և օգտակար է:

Վերջերս ինձ հանձնարարվեց ստեղծել մի գործընթաց՝ չմշակված ԴՆԹ-ի մեծ ծավալի հաջորդականությունների մշակման համար (տեխնիկապես SNP չիպ): Անհրաժեշտ էր արագորեն տվյալներ ստանալ տվյալ գենետիկական տեղանքի մասին (կոչվում է SNP) հետագա մոդելավորման և այլ առաջադրանքների համար: Օգտագործելով R-ը և AWK-ը՝ ես կարողացա մաքրել և կազմակերպել տվյալները բնական ճանապարհով՝ մեծապես արագացնելով հարցումների մշակումը: Սա ինձ համար հեշտ չէր և պահանջում էր բազմաթիվ կրկնություններ: Այս հոդվածը կօգնի ձեզ խուսափել իմ որոշ սխալներից և ցույց տալ, թե ինչով եմ ես վերջացել:

Նախ՝ մի քանի ներածական բացատրություններ։

Տվյալներ

Մեր համալսարանի գենետիկ տեղեկատվության մշակման կենտրոնը մեզ տրամադրեց տվյալներ 25 ՏԲ TSV-ի տեսքով: Ես ստացա դրանք բաժանված 5 փաթեթների՝ սեղմված Gzip-ով, որոնցից յուրաքանչյուրը պարունակում էր մոտ 240 չորս գիգաբայթանոց ֆայլ։ Յուրաքանչյուր տող պարունակում էր տվյալներ մեկ անհատի մեկ SNP-ի համար: Ընդհանուր առմամբ, տվյալներ են փոխանցվել ~2,5 միլիոն SNP-ի և ~60 հազար մարդու մասին։ Ի լրումն SNP տեղեկատվության, ֆայլերը պարունակում էին բազմաթիվ սյունակներ թվերով, որոնք արտացոլում էին տարբեր բնութագրեր, ինչպիսիք են ընթերցման ինտենսիվությունը, տարբեր ալելների հաճախականությունը և այլն: Ընդհանուր առմամբ կար մոտ 30 եզակի արժեքներով սյունակ։

Նպատակ

Ինչպես տվյալների կառավարման ցանկացած նախագծում, ամենակարևորը որոշելն էր, թե ինչպես կօգտագործվեն տվյալները: Այս դեպքում մենք հիմնականում SNP-ի վրա կընտրենք մոդելներ և աշխատանքային հոսքեր. Այսինքն՝ մեզ անհրաժեշտ կլինեն տվյալներ միայն մեկ SNP-ի վերաբերյալ։ Ես պետք է սովորեի, թե ինչպես հնարավորին չափ հեշտությամբ, արագ և էժան առբերել 2,5 միլիոն SNP-ներից մեկի հետ կապված բոլոր գրառումները:

Ինչպես դա չանել

Հարմար կլիշե մեջբերելու համար.

Ես հազար անգամ չեմ ձախողվել, ես պարզապես հայտնաբերել եմ հազարավոր եղանակներ՝ խուսափելու համար տվյալների փունջը հարցումների համար հարմար ձևաչափով վերլուծելուց:

Փորձեք առաջինը

Ինչ եմ սովորել25 ՏԲ միաժամանակ վերլուծելու էժան միջոց չկա:

Վանդերբիլտի համալսարանում «Մեծ տվյալների մշակման առաջադեմ մեթոդներ» դասընթացն անցնելով՝ ես վստահ էի, որ հնարքը պայուսակի մեջ է: Հավանաբար, մեկ կամ երկու ժամ կպահանջվի Hive սերվերը կարգավորելու համար, որպեսզի անցնի բոլոր տվյալները և զեկուցի արդյունքը: Քանի որ մեր տվյալները պահվում են AWS S3-ում, ես օգտվել եմ ծառայությունից Athena, որը թույլ է տալիս կիրառել Hive SQL հարցումներ S3 տվյալների վրա։ Ձեզ հարկավոր չէ ստեղծել/բարձրացնել Hive կլաստեր, ինչպես նաև վճարում եք միայն ձեր փնտրած տվյալների համար:

Այն բանից հետո, երբ ես Աթենային ցույց տվեցի իմ տվյալները և դրա ձևաչափը, ես որոշ թեստեր անցկացրի հետևյալ հարցումներով.

select * from intensityData limit 10;

Եվ արագ ստացավ լավ կառուցվածքային արդյունքներ: Պատրաստ.

Մինչև մենք փորձեցինք տվյալներն օգտագործել մեր աշխատանքում...

Ինձ խնդրեցին դուրս բերել SNP-ի ողջ տեղեկատվությունը մոդելը փորձարկելու համար: Ես կատարեցի հարցումը.


select * from intensityData 
where snp = 'rs123456';

...և սկսեց սպասել։ Ութ րոպեից և ավելի քան 4 ՏԲ պահանջված տվյալներից հետո ես ստացա արդյունքը: Athena-ն գանձում է հայտնաբերված տվյալների ծավալով՝ 5 դոլար մեկ տերաբայթի համար: Այսպիսով, այս մեկ հարցումն արժեր 20 դոլար և ութ րոպե սպասել: Մոդելը բոլոր տվյալների վրա գործարկելու համար մենք պետք է սպասեինք 38 տարի և վճարեինք 50 միլիոն դոլար, ակնհայտ է, որ դա մեզ հարմար չէր:

Անհրաժեշտ էր օգտագործել Մանրահատակ...

Ինչ եմ սովորելԶգույշ եղեք ձեր Parquet ֆայլերի չափի և դրանց կազմակերպման հետ:

Ես նախ փորձեցի շտկել իրավիճակը՝ փոխակերպելով բոլոր TSV-ները Մանրահատակի ֆայլեր. Դրանք հարմար են տվյալների մեծ հավաքածուների հետ աշխատելու համար, քանի որ դրանցում առկա տեղեկատվությունը պահվում է սյունակային ձևով. յուրաքանչյուր սյունակ գտնվում է իր հիշողության/սկավառակի հատվածում, ի տարբերություն տեքստային ֆայլերի, որոնցում տողերը պարունակում են յուրաքանչյուր սյունակի տարրերը: Եվ եթե ձեզ անհրաժեշտ է ինչ-որ բան գտնել, ապա պարզապես կարդացեք պահանջվող սյունակը: Բացի այդ, յուրաքանչյուր ֆայլ պահում է արժեքների մի շարք սյունակում, այնպես որ, եթե ձեր փնտրած արժեքը սյունակի տիրույթում չէ, Spark-ը ժամանակ չի վատնի ամբողջ ֆայլը սկանավորելու համար:

Ես կատարեցի մի պարզ առաջադրանք AWS սոսինձ փոխարկել մեր TSV-ները մանրահատակի և նոր ֆայլերը գցել Athena-ի մեջ: Այն տեւեց մոտ 5 ժամ։ Բայց երբ ես առաջադրեցի հարցումը, դրա ավարտին պահանջվեց մոտավորապես նույնքան ժամանակ և մի փոքր ավելի քիչ գումար: Փաստն այն է, որ Spark-ը, փորձելով օպտիմալացնել առաջադրանքը, պարզապես բացեց TSV-ի մեկ կտոր և դրեց այն իր սեփական Մանրահատակի կտորի մեջ: Եվ քանի որ յուրաքանչյուր կտոր բավականաչափ մեծ էր բազմաթիվ մարդկանց ամբողջ գրառումները պարունակելու համար, յուրաքանչյուր ֆայլ պարունակում էր բոլոր SNP-ները, ուստի Spark-ը պետք է բացեր բոլոր ֆայլերը՝ իրեն անհրաժեշտ տեղեկատվությունը հանելու համար:

Հետաքրքիրն այն է, որ մանրահատակի լռելյայն (և առաջարկվող) սեղմման տեսակը, snappy, չի կարող բաժանվել: Հետևաբար, յուրաքանչյուր կատարող խրված էր ամբողջական 3,5 ԳԲ տվյալների փաթեթը հանելու և ներբեռնելու առաջադրանքում:

25TB-ի վերլուծություն՝ օգտագործելով AWK և R

Եկեք հասկանանք խնդիրը

Ինչ եմ սովորելՏեսակավորումը դժվար է, հատկապես, եթե տվյալները բաշխված են:

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

AWS-ը հաստատ չի ցանկանում վերադարձնել գումարը «Ես շեղված ուսանող եմ» պատճառով: Այն բանից հետո, երբ ես վազեցի Amazon Glue-ով տեսակավորելը, այն 2 օր աշխատեց և վթարի ենթարկվեց:

Ինչ վերաբերում է բաժանմանը:

Ինչ եմ սովորելSpark-ի միջնորմները պետք է հավասարակշռված լինեն:

Հետո ես առաջացա քրոմոսոմներում տվյալները բաժանելու գաղափարը: Դրանք 23-ն են (և մի քանիսը, եթե հաշվի առնեք միտոքոնդրիալ ԴՆԹ-ն և չքարտեզագրված շրջանները):
Սա թույլ կտա տվյալները բաժանել փոքր մասերի: Եթե ​​դուք ընդամենը մեկ տող ավելացնեք Spark արտահանման գործառույթին Glue script-ում partition_by = "chr", ապա տվյալները պետք է բաժանել դույլերի։

25TB-ի վերլուծություն՝ օգտագործելով AWK և R
Գենոմը բաղկացած է բազմաթիվ բեկորներից, որոնք կոչվում են քրոմոսոմներ:

Ցավոք, չստացվեց: Քրոմոսոմներն ունեն տարբեր չափեր, ինչը նշանակում է տեղեկատվության տարբեր քանակություն: Սա նշանակում է, որ այն առաջադրանքները, որոնք Spark-ը ուղարկեց աշխատողներին, հավասարակշռված չէին և դանդաղ ավարտվեցին, քանի որ որոշ հանգույցներ շուտ ավարտվեցին և անգործության էին մատնված: Այնուամենայնիվ, առաջադրանքները կատարվել են. Բայց մեկ ՊՈԱԿ խնդրելիս անհավասարակշռությունը կրկին խնդիրներ առաջացրեց։ Ավելի մեծ քրոմոսոմների վրա SNP-ների մշակման արժեքը (այսինքն, որտեղ մենք ցանկանում ենք տվյալներ ստանալ) նվազել է ընդամենը 10 անգամ: Շատ, բայց ոչ բավարար:

Իսկ եթե այն էլ ավելի փոքր մասերի բաժանենք։

Ինչ եմ սովորելԵրբեք մի փորձեք կատարել 2,5 միլիոն բաժանում:

Ես որոշեցի ամեն ինչ անել և բաժանեցի յուրաքանչյուր SNP: Սա ապահովում էր, որ միջնորմները հավասար չափերի էին: ՎԱՏ ԳԱՂԱՓԱՐ ԷՐ. Ես օգտագործեցի սոսինձ և ավելացրեցի անմեղ գիծ partition_by = 'snp'. Առաջադրանքը սկսվեց և սկսեց իրագործվել։ Մեկ օր անց ստուգեցի և տեսա, որ S3-ում դեռ ոչինչ գրված չկա, ուստի սպանեցի առաջադրանքը: Կարծես Glue-ն միջանկյալ ֆայլեր էր գրում S3-ի թաքնված վայրում, շատ ֆայլեր, գուցե մի երկու միլիոն: Արդյունքում իմ սխալն արժեցել է ավելի քան հազար դոլար և չի գոհացրել իմ դաստիարակին։

Բաժանում + տեսակավորում

Ինչ եմ սովորելՏեսակավորումը դեռևս դժվար է, ինչպես նաև Spark-ի թյունինգը:

Բաժանման իմ վերջին փորձը ներառում էր ինձ բաժանելով քրոմոսոմները և այնուհետև տեսակավորում յուրաքանչյուր բաժանումը: Տեսականորեն սա կարագացնի յուրաքանչյուր հարցում, քանի որ ցանկալի SNP տվյալները պետք է լինեն տվյալ տիրույթում գտնվող մի քանի մանրահատակի կտորների մեջ: Ցավոք, նույնիսկ բաժանված տվյալների տեսակավորումը դժվար գործ էր: Արդյունքում, ես անցա EMR-ին հատուկ կլաստերի համար և օգտագործեցի ութ հզոր օրինակներ (C5.4xl) և Sparklyr՝ ավելի ճկուն աշխատանքային հոսք ստեղծելու համար...

# Sparklyr snippet to partition by chr and sort w/in partition
# Join the raw data with the snp bins
raw_data
  group_by(chr) %>%
  arrange(Position) %>% 
  Spark_write_Parquet(
    path = DUMP_LOC,
    mode = 'overwrite',
    partition_by = c('chr')
  )

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

Ես ավելի կրեատիվ եմ դառնում

Ինչ եմ սովորելԵրբեմն հատուկ տվյալները պահանջում են հատուկ լուծումներ:

Յուրաքանչյուր SNP ունի դիրքի արժեք: Սա իր քրոմոսոմի երկայնքով հիմքերի թվին համապատասխանող թիվ է։ Սա մեր տվյալները կազմակերպելու գեղեցիկ և բնական միջոց է: Սկզբում ես ուզում էի բաժանել յուրաքանչյուր քրոմոսոմի շրջաններով: Օրինակ, դիրքերը 1 - 2000, 2001 - 4000 եւ այլն: Բայց խնդիրն այն է, որ SNP-ները հավասարաչափ բաշխված չեն քրոմոսոմների միջով, ուստի խմբի չափերը, հետևաբար, շատ տարբեր կլինեն:

25TB-ի վերլուծություն՝ օգտագործելով AWK և R

Արդյունքում ես հանգեցի պաշտոնների բաժանման կատեգորիաների (աստիճան): Օգտագործելով արդեն ներբեռնված տվյալները, ես հարցում կատարեցի՝ եզակի SNP-ների, դրանց դիրքերի և քրոմոսոմների ցուցակ ստանալու համար: Այնուհետև ես տեսակավորեցի տվյալները յուրաքանչյուր քրոմոսոմի ներսում և հավաքեցի SNP-ները տվյալ չափի խմբերի (աղբարկղ): Ասենք 1000-ական SNP: Սա ինձ տվեց SNP-to-group-per-chromosome հարաբերությունը:

Վերջում ես 75 SNP-ից կազմեցի խմբեր (աղբարկղ), պատճառը կբացատրվի ստորև։

snp_to_bin <- unique_snps %>% 
  group_by(chr) %>% 
  arrange(position) %>% 
  mutate(
    rank = 1:n()
    bin = floor(rank/snps_per_bin)
  ) %>% 
  ungroup()

Նախ փորձեք Spark-ի հետ

Ինչ եմ սովորելԿայծի համախմբումը արագ է, բայց բաժանումը դեռ թանկ է:

Ես ուզում էի կարդալ այս փոքր (2,5 միլիոն տող) տվյալների շրջանակը Spark-ում, համատեղել այն չմշակված տվյալների հետ և այն բաժանել նոր ավելացված սյունակով։ bin.


# Join the raw data with the snp bins
data_w_bin <- raw_data %>%
  left_join(sdf_broadcast(snp_to_bin), by ='snp_name') %>%
  group_by(chr_bin) %>%
  arrange(Position) %>% 
  Spark_write_Parquet(
    path = DUMP_LOC,
    mode = 'overwrite',
    partition_by = c('chr_bin')
  )

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

Եվ նորից իմ միտքը չաշխատեց՝ առաջադրանքները որոշ ժամանակ աշխատեցին, ավարտին հասցրին միությունը, իսկ հետո բաժանման միջոցով գործարկված կատարողների նման սկսեցին ձախողվել։

AWK-ի ավելացում

Ինչ եմ սովորելՄի քնեք, երբ ձեզ սովորեցնում են հիմունքները: Անշուշտ, ինչ-որ մեկն արդեն լուծել է ձեր խնդիրը դեռևս 1980-ականներին:

Մինչև այս պահը Spark-ի հետ իմ բոլոր անհաջողությունների պատճառը կլաստերի տվյալների խառնաշփոթն էր: Հավանաբար, իրավիճակը կարող է բարելավվել նախնական բուժմամբ: Ես որոշեցի փորձել չմշակված տեքստի տվյալները բաժանել քրոմոսոմների սյունակների, ուստի հույս ունեի Spark-ին տրամադրել «նախապես բաժանված» տվյալներ:

Ես որոնեցի StackOverflow-ում, թե ինչպես բաժանել ըստ սյունակների արժեքների և գտա այսպիսի հիանալի պատասխան: AWK-ով դուք կարող եք տեքստային ֆայլը բաժանել ըստ սյունակի արժեքների՝ գրելով այն սցենարով, այլ ոչ թե արդյունքներն ուղարկելով stdout.

Ես գրել եմ Bash սցենար՝ այն փորձելու համար: Ներբեռնեց փաթեթավորված TSV-ներից մեկը, այնուհետև բացեց այն՝ օգտագործելով gzip և ուղարկվել է awk.

gzip -dc path/to/chunk/file.gz |
awk -F 't' 
'{print $1",..."$30">"chunked/"$chr"_chr"$15".csv"}'

Դա աշխատեց!

Լրացնելով միջուկները

Ինչ եմ սովորել: gnu parallel - Դա կախարդական բան է, բոլորը պետք է օգտագործեն այն:

Բաժանումը բավականին դանդաղ էր ու երբ սկսեցի htopՀզոր (և թանկ) EC2 օրինակի օգտագործումը ստուգելու համար պարզվեց, որ ես օգտագործում էի միայն մեկ միջուկ և մոտ 200 ՄԲ հիշողություն: Խնդիրը լուծելու և մեծ գումարներ չկորցնելու համար մենք պետք է հասկանանք, թե ինչպես կարելի է զուգահեռացնել աշխատանքը։ Բարեբախտաբար, բացարձակապես զարմանալի գրքում Տվյալների գիտություն հրամանի տողում Ես գտա Ջերոն Յանսենսի մի գլուխ զուգահեռացման մասին: Դրանից ես իմացա gnu parallel, շատ ճկուն մեթոդ Unix-ում բազմաթելեր իրականացնելու համար։

25TB-ի վերլուծություն՝ օգտագործելով AWK և R
Երբ ես սկսեցի բաժանումը, օգտագործելով նոր գործընթացը, ամեն ինչ լավ էր, բայց դեռևս խցան կար. S3 օբյեկտները սկավառակի վրա ներբեռնելը այնքան էլ արագ չէր և ոչ ամբողջությամբ զուգահեռաբար: Դա շտկելու համար ես արեցի սա.

  1. Ես պարզեցի, որ հնարավոր է իրականացնել S3 ներբեռնման փուլը անմիջապես խողովակաշարում, ամբողջությամբ վերացնելով միջանկյալ պահեստավորումը սկավառակի վրա: Սա նշանակում է, որ ես կարող եմ խուսափել չմշակված տվյալները սկավառակի վրա գրելուց և օգտագործել նույնիսկ ավելի փոքր, հետևաբար ավելի էժան պահեստավորում AWS-ում:
  2. թիմը aws configure set default.s3.max_concurrent_requests 50 զգալիորեն ավելացրել է AWS CLI-ի կողմից օգտագործվող թելերի քանակը (կանխադրաբար կան 10):
  3. Ես անցա ցանցի արագության համար օպտիմիզացված EC2 օրինակին՝ անվանման մեջ n տառով: Ես պարզել եմ, որ n-ինդեքսների օգտագործման ժամանակ վերամշակող հզորության կորուստը ավելի քան փոխհատուցվում է բեռնման արագության աճով։ Առաջադրանքների մեծ մասի համար ես օգտագործել եմ c5n.4xl:
  4. Փոխվել է gzip մասին pigz, սա gzip գործիք է, որը կարող է հիանալի բաներ անել ֆայլերի ապակոմպրեսիացման սկզբնական ոչ զուգահեռ առաջադրանքը զուգահեռացնելու համար (սա ամենաքիչն է օգնել):

# Let S3 use as many threads as it wants
aws configure set default.s3.max_concurrent_requests 50

for chunk_file in $(aws s3 ls $DATA_LOC | awk '{print $4}' | grep 'chr'$DESIRED_CHR'.csv') ; do

        aws s3 cp s3://$batch_loc$chunk_file - |
        pigz -dc |
        parallel --block 100M --pipe  
        "awk -F 't' '{print $1",..."$30">"chunked/{#}_chr"$15".csv"}'"

       # Combine all the parallel process chunks to single files
        ls chunked/ |
        cut -d '_' -f 2 |
        sort -u |
        parallel 'cat chunked/*_{} | sort -k5 -n -S 80% -t, | aws s3 cp - '$s3_dest'/batch_'$batch_num'_{}'
        
         # Clean up intermediate data
       rm chunked/*
done

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

Այս թվիթում պետք է նշվեր «TSV»: Ավաղ.

Օգտագործելով նոր վերլուծված տվյալները

Ինչ եմ սովորելSpark-ը սիրում է չսեղմված տվյալներ և չի սիրում միջնորմների համադրումը:

Այժմ տվյալները S3-ում էին չփաթեթավորված (կարդալ՝ համօգտագործված) և կիսապատվիրված ձևաչափով, և ես կարող էի նորից վերադառնալ Spark: Ինձ անակնկալ էր սպասվում. նորից չհասցրի իմ ուզածին: Շատ դժվար էր Spark-ին ասել, թե ինչպես են բաժանվել տվյալները: Եվ նույնիսկ երբ ես դա արեցի, պարզվեց, որ չափից շատ միջնապատեր կան (95 հազար), և երբ ես օգտագործեցի coalesce նվազեցրեց նրանց թիվը ողջամիտ սահմանների, սա ոչնչացրեց իմ բաժանումը: Համոզված եմ, որ դա կարելի է ուղղել, բայց մի երկու օր փնտրելուց հետո լուծում չգտա: Վերջիվերջո ես ավարտեցի Spark-ի բոլոր առաջադրանքները, չնայած որոշ ժամանակ պահանջվեց, և իմ բաժանված Parquet ֆայլերը շատ փոքր չէին (~200 ԿԲ): Այնուամենայնիվ, տվյալները այնտեղ էին, որտեղ անհրաժեշտ էր:

25TB-ի վերլուծություն՝ օգտագործելով AWK և R
Չափազանց փոքր և անհարթ, հիասքանչ:

Տեղական Spark հարցումների փորձարկում

Ինչ եմ սովորելՊարզ խնդիրներ լուծելիս Spark-ը չափազանց մեծ ծախսեր ունի:

Ներբեռնելով տվյալները խելացի ձևաչափով, ես կարողացա ստուգել արագությունը: Ստեղծեք R սկրիպտ՝ տեղական Spark սերվերը գործարկելու համար, այնուհետև բեռնեք Spark տվյալների շրջանակը նշված Parquet խմբի պահեստից (աղբարկղ): Ես փորձեցի բեռնել բոլոր տվյալները, բայց չկարողացա ստիպել Sparklyr-ին ճանաչել բաժանումը:

sc <- Spark_connect(master = "local")

desired_snp <- 'rs34771739'

# Start a timer
start_time <- Sys.time()

# Load the desired bin into Spark
intensity_data <- sc %>% 
  Spark_read_Parquet(
    name = 'intensity_data', 
    path = get_snp_location(desired_snp),
    memory = FALSE )

# Subset bin to snp and then collect to local
test_subset <- intensity_data %>% 
  filter(SNP_Name == desired_snp) %>% 
  collect()

print(Sys.time() - start_time)

Մահապատիժը տևել է 29,415 վայրկյան։ Շատ ավելի լավ, բայց ոչ շատ լավ բանի զանգվածային փորձարկման համար: Բացի այդ, ես չկարողացա արագացնել իրադարձությունները քեշավորման միջոցով, քանի որ երբ ես փորձում էի տվյալների շրջանակը քեշավորել հիշողության մեջ, Spark-ը միշտ խափանում էր, նույնիսկ երբ ես ավելի քան 50 ԳԲ հիշողություն հատկացրի 15-ից պակաս կշռող տվյալների հավաքածուին:

Վերադարձ դեպի AWK

Ինչ եմ սովորելAWK-ում ասոցիատիվ զանգվածները շատ արդյունավետ են:

Ես հասկացա, որ կարող եմ ավելի մեծ արագությունների հասնել։ Ես դա հիշեցի հրաշալի կերպով AWK ձեռնարկ Բրյուս Բարնետի կողմից Ես կարդացի մի հետաքրքիր հատկության մասին, որը կոչվում է «ասոցիատիվ զանգվածներ« Ըստ էության, սրանք առանցքային արժեքների զույգեր են, որոնք ինչ-ինչ պատճառներով այլ կերպ էին կոչվում AWK-ում, և, հետևաբար, ես ինչ-որ կերպ շատ չէի մտածում դրանց մասին: Ռոման Չեպլյակա հիշեցրել է, որ «ասոցիատիվ զանգվածներ» տերմինը շատ ավելի հին է, քան «բանալին-արժեք զույգ» տերմինը։ Նույնիսկ եթե դուք փնտրեք բանալին-արժեքը Google Ngram-ում, դուք չեք տեսնի այս տերմինն այնտեղ, բայց կգտնեք ասոցիատիվ զանգվածներ: Բացի այդ, «բանալին-արժեք զույգը» ամենից հաճախ կապված է տվյալների շտեմարանների հետ, ուստի շատ ավելի իմաստալից է այն համեմատել հաշքապի հետ: Ես հասկացա, որ կարող եմ օգտագործել այս ասոցիատիվ զանգվածները՝ իմ SNP-ները կապակցելու աղբարկղային աղյուսակի և չմշակված տվյալների հետ՝ առանց Spark-ի օգտագործման:

Դա անելու համար AWK սկրիպտում ես օգտագործեցի բլոկը BEGIN. Սա կոդի մի մասն է, որը կատարվում է նախքան տվյալների առաջին տողը փոխանցվում է սցենարի հիմնական մասին:

join_data.awk
BEGIN {
  FS=",";
  batch_num=substr(chunk,7,1);
  chunk_id=substr(chunk,15,2);
  while(getline < "snp_to_bin.csv") {bin[$1] = $2}
}
{
  print $0 > "chunked/chr_"chr"_bin_"bin[$1]"_"batch_num"_"chunk_id".csv"
}

Թիմ while(getline...) բեռնեց բոլոր տողերը CSV խմբից (աղբարկղ), սահմանեց առաջին սյունակը (SNP անվանումը) որպես ասոցիատիվ զանգվածի բանալի bin իսկ երկրորդ արժեքը (խումբը) որպես արժեք։ Հետո բլոկում { }, որը կատարվում է հիմնական ֆայլի բոլոր տողերում, յուրաքանչյուր տող ուղարկվում է ելքային ֆայլ, որը ստանում է եզակի անուն՝ կախված իր խմբից (բնակարան). ..._bin_"bin[$1]"_....

Փոփոխականներ batch_num и chunk_id համընկնում է խողովակաշարի կողմից տրամադրված տվյալների հետ՝ խուսափելով մրցավազքի պայմաններից, և յուրաքանչյուր կատարման շարանը աշխատում է parallel, գրել է իր յուրահատուկ ֆայլին:

Քանի որ ես ցրեցի բոլոր չմշակված տվյալները քրոմոսոմների թղթապանակներում, որոնք մնացել էին AWK-ի հետ իմ նախորդ փորձից, այժմ կարող եմ գրել մեկ այլ Bash սկրիպտ՝ մեկ քրոմոսոմը մշակելու և S3-ին ավելի խորը բաժանված տվյալներ ուղարկելու համար:

DESIRED_CHR='13'

# Download chromosome data from s3 and split into bins
aws s3 ls $DATA_LOC |
awk '{print $4}' |
grep 'chr'$DESIRED_CHR'.csv' |
parallel "echo 'reading {}'; aws s3 cp "$DATA_LOC"{} - | awk -v chr=""$DESIRED_CHR"" -v chunk="{}" -f split_on_chr_bin.awk"

# Combine all the parallel process chunks to single files and upload to rds using R
ls chunked/ |
cut -d '_' -f 4 |
sort -u |
parallel "echo 'zipping bin {}'; cat chunked/*_bin_{}_*.csv | ./upload_as_rds.R '$S3_DEST'/chr_'$DESIRED_CHR'_bin_{}.rds"
rm chunked/*

Սցենարն ունի երկու բաժին parallel.

Առաջին բաժնում տվյալները կարդացվում են ցանկալի քրոմոսոմի մասին տեղեկատվություն պարունակող բոլոր ֆայլերից, այնուհետև այդ տվյալները բաշխվում են թելերի միջով, որոնք ֆայլերը բաշխում են համապատասխան խմբերի (բնակարան): Ռասայական պայմաններից խուսափելու համար, երբ մի քանի շղթաներ գրում են նույն ֆայլին, AWK-ն փոխանցում է ֆայլերի անունները՝ տարբեր վայրերում տվյալներ գրելու համար, օրինակ. chr_10_bin_52_batch_2_aa.csv. Արդյունքում սկավառակի վրա ստեղծվում են բազմաթիվ փոքր ֆայլեր (դրա համար ես օգտագործել եմ տերաբայթ EBS հատորներ):

Փոխակրիչ երկրորդ հատվածից parallel անցնում է խմբերի միջով (աղբարկղ) և միավորում է նրանց անհատական ​​ֆայլերը ընդհանուր CSV c catիսկ հետո դրանք ուղարկում է արտահանման:

Հեռարձակում Ռ.

Ինչ եմ սովորել: Կարող եք կապ հաստատել stdin и stdout R script-ից և հետևաբար օգտագործեք այն խողովակաշարում:

Դուք կարող եք նկատել այս տողը ձեր Bash սցենարում. ...cat chunked/*_bin_{}_*.csv | ./upload_as_rds.R.... Այն թարգմանում է բոլոր միացված խմբային ֆայլերը (աղբարկղ) ստորև նշված R սցենարի մեջ: {} հատուկ տեխնիկա է parallel, որը տեղադրում է ցանկացած տվյալ, որն ուղարկում է նշված հոսքին անմիջապես հրամանի մեջ: Տարբերակ {#} ապահովում է թեմայի եզակի ID, և {%} ներկայացնում է աշխատատեղի համարը (կրկնվում է, բայց ոչ միևնույն ժամանակ): Բոլոր տարբերակների ցանկը կարելի է գտնել այստեղ փաստաթղթեր.

#!/usr/bin/env Rscript
library(readr)
library(aws.s3)

# Read first command line argument
data_destination <- commandArgs(trailingOnly = TRUE)[1]

data_cols <- list(SNP_Name = 'c', ...)

s3saveRDS(
  read_csv(
        file("stdin"), 
        col_names = names(data_cols),
        col_types = data_cols 
    ),
  object = data_destination
)

Երբ փոփոխական file("stdin") փոխանցվել է readr::read_csv, R սկրիպտի մեջ թարգմանված տվյալները բեռնվում են շրջանակի մեջ, որն այնուհետև գտնվում է ձևի մեջ .rds- ֆայլի օգտագործումը aws.s3 ուղղակիորեն գրված է S3-ին:

RDS-ը մանրահատակի կրտսեր տարբերակի պես մի բան է՝ առանց բարձրախոսների պահպանման նրբությունների:

Bash-ի սցենարն ավարտելուց հետո ես փաթեթ ստացա .rds-S3-ում տեղակայված ֆայլեր, որոնք ինձ թույլ տվեցին օգտագործել արդյունավետ սեղմում և ներկառուցված տեսակներ:

Չնայած արգելակ R-ի կիրառմանը, ամեն ինչ շատ արագ աշխատեց։ Զարմանալի չէ, որ R-ի այն մասերը, որոնք կարդում և գրում են տվյալներ, շատ օպտիմիզացված են: Մեկ միջին չափի քրոմոսոմի վրա փորձարկումից հետո աշխատանքը ավարտվեց C5n.4xl օրինակով մոտ երկու ժամում:

S3 սահմանափակումներ

Ինչ եմ սովորելԽելացի ուղու ներդրման շնորհիվ S3-ը կարող է կառավարել բազմաթիվ ֆայլեր:

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

25TB-ի վերլուծություն՝ օգտագործելով AWK և R
S3-ի թղթապանակները պարզապես ցուցադրական են, իրականում համակարգին չի հետաքրքրում սիմվոլը /. S3 FAQ էջից:

Թվում է, որ S3-ը ներկայացնում է դեպի որոշակի ֆայլ տանող ուղին որպես պարզ բանալի մի տեսակ հեշ աղյուսակում կամ փաստաթղթերի վրա հիմնված տվյալների բազայում: Դույլը կարելի է պատկերացնել որպես աղյուսակ, իսկ ֆայլերը կարելի է համարել այդ աղյուսակի գրառումներ:

Քանի որ արագությունն ու արդյունավետությունը կարևոր են Amazon-ում շահույթ ստանալու համար, զարմանալի չէ, որ այս բանալին որպես ֆայլի ուղի համակարգը սարսափելի օպտիմիզացված է: Ես փորձեցի հավասարակշռություն գտնել, որպեսզի ես ստիպված չլինեի շատ հարցումներ կատարել, այլ որ հարցումներն արագ կատարվեին: Պարզվեց, որ ամենալավն է մոտ 20 հազար bin ֆայլ պատրաստելը։ Կարծում եմ, եթե շարունակենք օպտիմալացնել, կարող ենք հասնել արագության բարձրացման (օրինակ, հատուկ դույլ պատրաստել միայն տվյալների համար, այդպիսով նվազեցնելով որոնման աղյուսակի չափը): Սակայն հետագա փորձերի համար ժամանակ կամ գումար չկար։

Ի՞նչ կասեք խաչաձև համատեղելիության մասին:

Ինչ ես սովորեցի. Ժամանակի կորստի թիվ մեկ պատճառը ձեր պահեստավորման եղանակի վաղաժամ օպտիմալացումն է:

Այս պահին շատ կարևոր է ինքներդ ձեզ հարցնել. «Ինչու՞ օգտագործել սեփական ֆայլի ձևաչափ»: Պատճառը բեռնման արագության մեջ է (gzipped CSV ֆայլերի բեռնումը 7 անգամ ավելի երկար պահանջվեց) և մեր աշխատանքային հոսքերի հետ համատեղելիության մեջ: Ես կարող եմ վերանայել, արդյոք R-ն կարող է հեշտությամբ բեռնել Parquet (կամ Arrow) ֆայլերը առանց Spark բեռնման: Մեր լաբորատորիայում բոլորն օգտագործում են R-ն, և եթե ես պետք է տվյալները փոխարկեմ այլ ձևաչափի, ես դեռ ունեմ բնօրինակ տեքստային տվյալները, այնպես որ կարող եմ նորից գործարկել խողովակաշարը:

Աշխատանքի բաժանում

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

Ես կարգաբերել եմ աշխատանքային հոսքը մեկ քրոմոսոմի վրա, այժմ պետք է մշակեմ մնացած բոլոր տվյալները:
Ես ուզում էի բարձրացնել մի քանի EC2 օրինակներ փոխակերպման համար, բայց միևնույն ժամանակ ես վախենում էի շատ անհավասարակշիռ բեռ ստանալ տարբեր մշակման աշխատանքներում (ճիշտ այնպես, ինչպես Spark-ը տառապում էր անհավասարակշիռ միջնորմներից): Բացի այդ, ինձ չէր հետաքրքրում մեկ քրոմոսոմի մեկ օրինակ բարձրացնել, քանի որ AWS հաշիվների համար կա 10 օրինակի լռելյայն սահմանաչափ:

Հետո որոշեցի R-ով սցենար գրել՝ մշակման աշխատանքները օպտիմալացնելու համար:

Նախ, ես խնդրեցի S3-ին հաշվարկել, թե յուրաքանչյուր քրոմոսոմ որքան պահեստային տարածք է զբաղեցնում:

library(aws.s3)
library(tidyverse)

chr_sizes <- get_bucket_df(
  bucket = '...', prefix = '...', max = Inf
) %>% 
  mutate(Size = as.numeric(Size)) %>% 
  filter(Size != 0) %>% 
  mutate(
    # Extract chromosome from the file name 
    chr = str_extract(Key, 'chr.{1,4}.csv') %>%
             str_remove_all('chr|.csv')
  ) %>% 
  group_by(chr) %>% 
  summarise(total_size = sum(Size)/1e+9) # Divide to get value in GB



# A tibble: 27 x 2
   chr   total_size
   <chr>      <dbl>
 1 0           163.
 2 1           967.
 3 10          541.
 4 11          611.
 5 12          542.
 6 13          364.
 7 14          375.
 8 15          372.
 9 16          434.
10 17          443.
# … with 17 more rows

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

num_jobs <- 7
# How big would each job be if perfectly split?
job_size <- sum(chr_sizes$total_size)/7

shuffle_job <- function(i){
  chr_sizes %>%
    sample_frac() %>% 
    mutate(
      cum_size = cumsum(total_size),
      job_num = ceiling(cum_size/job_size)
    ) %>% 
    group_by(job_num) %>% 
    summarise(
      job_chrs = paste(chr, collapse = ','),
      total_job_size = sum(total_size)
    ) %>% 
    mutate(sd = sd(total_job_size)) %>% 
    nest(-sd)
}

shuffle_job(1)



# A tibble: 1 x 2
     sd data            
  <dbl> <list>          
1  153. <tibble [7 × 3]>

Հետո ես վազեցի հազարավոր խառնաշփոթների միջով՝ օգտագործելով purrr և ընտրեցի լավագույնը:

1:1000 %>% 
  map_df(shuffle_job) %>% 
  filter(sd == min(sd)) %>% 
  pull(data) %>% 
  pluck(1)

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

for DESIRED_CHR in "16" "9" "7" "21" "MT"
do
# Code for processing a single chromosome
fi

Վերջում ես ավելացնում եմ անջատման հրամանը.

sudo shutdown -h now

... և ամեն ինչ ստացվեց: Օգտագործելով AWS CLI-ը, ես բարձրացրել եմ օրինակներ՝ օգտագործելով տարբերակը user_data նրանց տվել է Bash-ի սկրիպտներ իրենց առաջադրանքների մշակման համար: Նրանք գործարկվեցին և ավտոմատ կերպով անջատվեցին, ուստի ես չէի վճարում լրացուցիչ վերամշակման հզորության համար:

aws ec2 run-instances ...
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=<<job_name>>}]" 
--user-data file://<<job_script_loc>>

Եկեք փաթեթավորենք:

Ինչ եմ սովորելAPI-ն պետք է լինի պարզ՝ օգտագործման հեշտության և ճկունության համար:

Վերջապես ես ստացա տվյալները ճիշտ տեղում և ձևով: Մնում էր միայն պարզեցնել տվյալների օգտագործման գործընթացը՝ իմ գործընկերների համար հեշտացնելու համար: Ես ուզում էի ստեղծել պարզ API՝ հարցումներ ստեղծելու համար: Եթե ​​ապագայում որոշեմ անցնել .rds Մանրահատակի ֆայլերին, ապա սա խնդիր պետք է լինի ոչ թե իմ գործընկերների, այլ իմ համար: Դրա համար ես որոշեցի պատրաստել ներքին R փաթեթ:

Կառուցեք և փաստաթղթավորեք մի շատ պարզ փաթեթ, որը պարունակում է տվյալների հասանելիության ընդամենը մի քանի գործառույթներ, որոնք կազմակերպված են ֆունկցիայի շուրջ get_snp. Ես նաև կայք եմ պատրաստել իմ գործընկերների համար pkgdown, այնպես որ նրանք կարող են հեշտությամբ տեսնել օրինակներ և փաստաթղթեր:

25TB-ի վերլուծություն՝ օգտագործելով AWK և R

Խելացի քեշավորում

Ինչ եմ սովորելԵթե ​​ձեր տվյալները լավ պատրաստված են, քեշավորումը հեշտ կլինի:

Քանի որ հիմնական աշխատանքային հոսքերից մեկը կիրառեց նույն վերլուծության մոդելը SNP փաթեթի վրա, ես որոշեցի օգտագործել binning-ը իմ օգտին: SNP-ի միջոցով տվյալներ փոխանցելիս խմբից (աղբարկղից) ամբողջ տեղեկատվությունը կցվում է վերադարձված օբյեկտին: Այսինքն՝ հին հարցումները կարող են (տեսականորեն) արագացնել նոր հարցումների մշակումը։

# Part of get_snp()
...
  # Test if our current snp data has the desired snp.
  already_have_snp <- desired_snp %in% prev_snp_results$snps_in_bin

  if(!already_have_snp){
    # Grab info on the bin of the desired snp
    snp_results <- get_snp_bin(desired_snp)

    # Download the snp's bin data
    snp_results$bin_data <- aws.s3::s3readRDS(object = snp_results$data_loc)
  } else {
    # The previous snp data contained the right bin so just use it
    snp_results <- prev_snp_results
  }
...

Փաթեթը կառուցելիս ես գործարկեցի բազմաթիվ հենանիշեր՝ տարբեր մեթոդներ օգտագործելիս արագությունը համեմատելու համար: Խորհուրդ եմ տալիս չանտեսել սա, քանի որ երբեմն արդյունքներն անսպասելի են լինում։ Օրինակ, dplyr::filter շատ ավելի արագ էր, քան ինդեքսավորման վրա հիմնված ֆիլտրման միջոցով տողեր գրավելը, և զտված տվյալների շրջանակից մեկ սյունակ ստանալը շատ ավելի արագ էր, քան ինդեքսավորման շարահյուսությունը:

Խնդրում ենք նկատի ունենալ, որ օբյեկտը prev_snp_results պարունակում է բանալին snps_in_bin. Սա խմբում (աղբարկղ) բոլոր եզակի SNP-ների զանգվածն է, որը թույլ է տալիս արագ ստուգել՝ արդյոք արդեն ունեք տվյալներ նախորդ հարցումից: Այն նաև հեշտացնում է բոլոր SNP-ների պտույտը խմբում (աղբարկղ) այս կոդով.

# Get bin-mates
snps_in_bin <- my_snp_results$snps_in_bin

for(current_snp in snps_in_bin){
  my_snp_results <- get_snp(current_snp, my_snp_results)
  # Do something with results 
}

Արդյունքները

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

Եվ չնայած փաթեթը խնայում է նրանց մանրամասները, ես փորձեցի տվյալների ձևաչափը այնքան պարզ դարձնել, որ նրանք կարողանան պարզել, եթե ես վաղը հանկարծ անհետանամ...

Արագությունը նկատելիորեն աճել է։ Մենք սովորաբար սկանավորում ենք ֆունկցիոնալ նշանակալի գենոմի բեկորներ: Նախկինում մենք չէինք կարող դա անել (պարզվում էր, որ դա չափազանց թանկ է), բայց այժմ, խմբային (աղբարկղ) կառուցվածքի և քեշավորման շնորհիվ, մեկ SNP-ի հարցումը տևում է միջինը 0,1 վայրկյանից պակաս, և տվյալների օգտագործումը այնքան է: ցածր է, որ S3-ի ծախսերը գետնանուշ են:

Ամփոփում

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

Մեծ տվյալների գործիքները բազմակողմանի են: Եթե ​​ժամանակ ունեք, կարող եք գրեթե անկասկած գրել ավելի արագ լուծում՝ օգտագործելով խելացի տվյալների մաքրման, պահպանման և արդյունահանման տեխնիկան: Ի վերջո, դա գալիս է ծախսերի և օգուտների վերլուծության:

Ինչ ես սովորեցի.

  • 25 ՏԲ միաժամանակ վերլուծելու էժան միջոց չկա.
  • զգույշ եղեք ձեր Parquet ֆայլերի չափերի և դրանց կազմակերպման հետ.
  • Spark-ի միջնորմները պետք է հավասարակշռված լինեն.
  • Ընդհանրապես, երբեք մի փորձեք կատարել 2,5 միլիոն բաժանմունք;
  • Տեսակավորումը դեռևս դժվար է, ինչպես նաև Spark-ի կարգավորումը.
  • երբեմն հատուկ տվյալները պահանջում են հատուկ լուծումներ.
  • Կայծի համախմբումը արագ է, բայց բաժանումը դեռ թանկ է.
  • մի քնիր, երբ քեզ սովորեցնում են հիմունքները, ինչ-որ մեկը հավանաբար արդեն լուծել է քո խնդիրը դեռևս 1980-ականներին.
  • gnu parallel - սա կախարդական բան է, բոլորը պետք է օգտագործեն այն;
  • Spark-ը սիրում է չսեղմված տվյալներ և չի սիրում միջնորմների համադրումը;
  • Spark-ը չափազանց մեծ ծախսեր ունի պարզ խնդիրներ լուծելիս.
  • AWK-ի ասոցիատիվ զանգվածները շատ արդյունավետ են.
  • կարող եք կապ հաստատել stdin и stdout R script-ից և, հետևաբար, օգտագործեք այն խողովակաշարում.
  • Խելացի ուղու ներդրման շնորհիվ S3-ը կարող է մշակել բազմաթիվ ֆայլեր.
  • Ժամանակ կորցնելու հիմնական պատճառը ձեր պահեստավորման եղանակի վաղաժամ օպտիմալացումն է.
  • մի փորձեք ձեռքով օպտիմիզացնել առաջադրանքները, թույլ տվեք համակարգչին դա անել.
  • API-ն պետք է լինի պարզ՝ օգտագործման հեշտության և ճկունության համար.
  • Եթե ​​ձեր տվյալները լավ պատրաստված են, քեշավորումը հեշտ կլինի:

Source: www.habr.com

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