Hyrje
«Duhet të vraposh me të gjitha forcat, vetëm për të mbetur në vend,
dhe për të shkuar diku, duhet të vraposh të paktën dy herë më shpejt!»
(c) Alicia në vendin e mrekullive
Disa kohë më parë më kërkuan të lexoj një leksion analistëve të kompanisë sonë mbi temat e projektimit të modeleve të dhënave, sepse duke qëndruar për një kohë të gjatë në projekte (ndonjëherë për disa vite) ne humbasim për atë që po ndodh rreth nesh në botën e teknologjive IT. Në kompaninë tonë (ndodhi kështu) në shumë projekte nuk përdoren bazat e dhënave NoSQL (të paktën deri tani), prandaj në leksionin tim i kushtova disa vëmendje atyre në shembullin HBase dhe përpiqesha ta orientoj materialin për ata që kurrë nuk kanë punuar me to. Në veçanti, ilustrova disa karakteristika të projektimit të modelit të dhënave në një shembull që disa vite më parë e lexova . Duke analizuar shembujt, krahasova disa variante të zgjidhjes së të njëjtës problem, për të komunikuar më mirë idetë kryesore te dëgjuesit.
Së fundmi, ndërsa isha duke u mërzitur, më erdhi në mendje pyetja (fundjavat e gjata të majit në karantinë ndihmojnë në këtë drejtim), sa teoritë do të korrespondojnë me praktikat? Kështu lindi ideja e këtij artikulli. Një zhvillues, i cili ka punuar për një kohë të gjatë me NoSQL, mund të mos marrë ndonjë gjë të re nga ai (prandaj mund të kalojë gjysmë artikulli menjëherë). Por për analistët, të cilët nuk kanë punuar ngushtë me NoSQL, besoj se do të jetë e dobishme për të marrë një kuptim bazik të karakteristikave të projektimit të modeleve të dhënash për HBase.
Analizimi i shembujve
Sipas mendimit tim, para se të filloni përdorimin e bazave të dhënash NoSQL, duhet të mendoni mirë dhe të peshojmë "për" dhe "kundër". Shpesh, një detyrë mund të zgjidhet edhe me sistemet tradicionale relacionale të menaxhimit të të dhënave. Prandaj, është më mirë të mos përdorni NoSQL pa baza të rëndësishme. Nëse vendosni gjithsesi të përdorni një bazë të dhënash NoSQL, duhet të keni parasysh se qasjet ndaj projektimit këtu janë paksa të ndryshme. Sidomos disa prej tyre mund të jenë të pazakonta për ata që kanë punuar vetëm me sistemet relacionale të menaxhimit të të dhënave (sipas vëzhgimeve të mia). Kështu, në botën "relacionale" ne zakonisht fillojmë nga modelimi i fushës së objektit dhe më pas, kur është e nevojshme, kryejmë de-normalizimin e modelit. Në NoSQL, ne duhet të marrim parasysh menjëherë skenarët e parashikuar për punën me të dhënat , dhe fillimisht të de-normalizojmë të dhënat. Përveç kësaj, ka një sërë dallimesh të tjera, për të cilat do të shkruhet më poshtë.
Le të shqyrtojmë detyrën e mëposhtme "sintetike", me të cilën do të punojmë më tej:
Duhet ndërtuar struktura e ruajtjes së listës së miqve të përdoruesve në një rrjet social abstrakt. Për ta thjeshtuar, do të supozojmë se të gjitha lidhjet janë të drejtuara (si në Instagram, e jo në LinkedIn). Strukturën duhet ta lejojë të ndihmojë efikasitetin:
- Të përgjigjet në pyetjen, a e lexon përdoruesi A përdoruesin B (shablloni i leximit)
- Të lejojë shtimin/fshirjen e lidhjeve në rastin e subscribimit/ndalimit të përdoruesit A nga përdoruesi B (shablloni i ndryshimit të të dhënave)
Sigurisht, ka shumë mënyra për zgjidhjen e problemit. Në një DB relacional normale, ne ndoshta do të bënim vetëm një tabelë lidhjesh (ndoshta të tipizuar, nëse, për shembull, kërkohet të ruajmë grupin e përdoruesve: familja, puna etj., në të cilin përfshihet ky "mik"), dhe për optimizimin e shpejtësisë së aksesit do të shtonim indekse/particionim. Tabelën përfundimtare do të duket më pak kështu:
user_id
friend_id
Vasja
Petja
Vasja
Olja
këtu dhe më tej për qartësi dhe kuptim më të mirë, në vend të ID-së do të përdor emrat
Në rastin e HBase e dimë se:
- kërkimi efikas, që nuk çon në skanimin e plotë të tabelës, është i mundur exkluzivisht sipas çelësit
- Prandaj, tĂ« shkruash pyetje SQL qĂ« shumĂ« njerĂ«z i njohin nĂ« baza tĂ« tilla Ă«shtĂ« njĂ« ide e keqe; teknikisht, sigurisht, mund tĂ« dĂ«rgosh njĂ« pyetje SQL me Join dhe logjikĂ« tĂ« tjera nĂ« HBase nga Impala, por sa efikase do tĂ« jetĂ«âŠ
Prandaj, ID e përdoruesit jemi të detyruar ta përdorim si çelës. Një mendim fillestar për "ku dhe si ta ruajmë ID e miqve?" mund të jetë ideja e ruajtjes së tyre në kolona. Ky variant më i dukshëm dhe "naiv" do të dukej kështu (ta quajmë Varianti 1 (default), për t'u referuar më vonë):
RowKey
Kolona
Vasja
1: Petja
2: Olya
3: Dasha
Petja
1: Masha
2: Vasya
KĂ«tu, çdo rresht korrespondon me njĂ« pĂ«rdorues tĂ« vetĂ«m tĂ« rrjetit. Kolonat kanĂ« emra: 1, 2, ⊠- sipas numrit tĂ« miqve, dhe nĂ« kolonat ruhen ID e miqve. ĂshtĂ« e rĂ«ndĂ«sishme tĂ« theksohet se çdo rresht do tĂ« ketĂ« njĂ« numĂ«r tĂ« ndryshĂ«m kolonash. NĂ« shembullin e mĂ«sipĂ«rm, njĂ« rresht ka tre kolona (1, 2 dhe 3), ndĂ«rsa tjetri - vetĂ«m dy (1 dhe 2) - kĂ«tu ne vetĂ« kemi shfrytĂ«zuar dy karakteristika tĂ« HBase qĂ« nuk i kanĂ« databazat relacionale:
- mundësinë për të ndryshuar dinamikisht përbërjen e kolonave (shtojmë një mik -> shtojmë kolonë, heqim një mik -> heqim kolonë)
- në rreshta të ndryshëm mund të ketë përbërje të ndryshme kolonash
Le të kontrollojmë strukturën tonë për t'u siguruar që përmbush kërkesat e detyrës:
- Leximi i të dhënave: për të kuptuar nëse Vasja është i regjistruar për Olyn, do të duhet të lexojmë gjithë rreshtin sipër çelësit RowKey = «Vasja» dhe të kalojmë përmes vlerave të kolonave derisa të «takojmë» Olyn. Ose të kalojmë përmes të gjithë vlerave të kolonave, «të mos takojmë» Olyn dhe të kthejmë përgjigjen False;
- Ndryshimi i të dhënave: shtimi i një shoku: për një detyrë të tillë gjithashtu do të na duhet të lexojmë gjithë rreshtin sipër çelësit RowKey = «Vasja», për të numëruar gjithsej shokët e tij. Ky numër i përgjithshëm i shokëve na nevojitet për të përcaktuar numrin e kolonës ku duhet të regjistrojmë ID-në e shokut të ri.
- Ndryshimi i të dhënave: heqja e një shoku:
- Duhet të lexojmë gjithë rreshtin sipër çelësit RowKey = «Vasja» dhe të kalojmë përmes kolonave për të gjetur atë kolonë ku është regjistruar shoku që po hiqet;
- Më pas, pas heqjes së shokut, na duhet të «shkallëzojmë» të dhënat në një kolone, për të mos marrë «gaps» në numërimin e tyre.
Tani le të vlerësojmë se sa efikase do të jenë algoritmet që duhet të implementojmë në anën e «aplikacionit të supozuar», duke përdorur . Le të shënojmë madhësinë e rrjetit tonë hipotetik social si n. Atëherë numri maksimal i miqve që një përdorues mund të ketë është (n-1). Këtë (-1) mund ta injorojmë për qëllimet tona, pasi në përputhje me simbolet O, është e parëndësishme.
- Leximi i të dhënave: është e nevojshme të lexojmë tërë rreshtin dhe të shqyrtojmë, në kufirin, të gjitha kolonnat e tij. Pra, vlerësimi i sipërm i shpenzimeve do të jetë përafërsisht O(n)
- Ndryshimi i të dhënave: shtimi i një shoku: për të përcaktuar numrin e miqve, është e nevojshme të kalojmë nëpër të gjitha kolonnat e rreshtit, pas të cilave shtojmë një kolonë të re => O(n)
- Ndryshimi i të dhënave: heqja e një shoku:
- NjĂ«soj si shtimi â Ă«shtĂ« e nevojshme tĂ« kalojmĂ«, nĂ« kufirin, tĂ« gjitha kolonnat => O(n)
- Pas pastrimit të kolonave, na nevojitet t'i "zhvendosim" ato. Nëse e realizojmë këtë "drejt", në limit do të nevojiten (n-1) operacione të tjera. Por këtu dhe më tej në pjesën praktike do të aplikojmë një qasje tjetër, e cila do të realizojë një "pseudo-zhvendosje" me një numër të fiksuar operacionesh - pra, do të harxhojë kohë konstante pa marrë parasysh n. Ky kohë konstant (nëse jemi të saktë, O(2)) në krahasim me O(n) mund të injorohet. Qasja ilustrohet në figurën më poshtë: ne thjesht kopjojmë të dhënat nga kolona "fundore" në atë nga e cila duam të heqim të dhënat, pas së cilës e fshijmë kolonën e fundit:

Si pasojë, në të gjitha skenarët kemi arritur një kompleksitet asimptotik të llogaritjes O(n).
Besoj se tashmë e keni vënë re se na duhet pothuajse gjithmonë të lexojmë tërë rreshtin nga baza e të dhënave, dhe duke qenë se në dy nga tri rastet, e bëjmë këtë vetëm për të kaluar nëpër të gjitha kolonat dhe për të llogaritur numrin e përgjithshëm të miqve. Prandaj, si një përpjekje për optimizim, mund të shtojmë një kolonë "count", në të cilën të ruhet numri total i miqve të çdo përdoruesi të rrjetit. Në këtë rast, ne nuk kemi nevojë të lexojmë tërë rreshtin për të llogaritur numrin total të miqve, por mund të lexojmë vetëm një kolonë "count". E rëndësishme është të mos harrojmë të përditësojmë "count" kur manipulojmë me të dhënat. Kështu, arrijmë një përmirësim Opsioni 2 (count):
RowKey
Kolona
Vasja
1: Petja
2: Olya
3: Dasha
count: 3
Petja
1: Masha
2: Vasya
count: 2
Në krahasim me opsionin e parë:
- Leximi i të dhënave: për të marrë përgjigjen për pyetjen "A e lexon Vania Olën?" nuk ka ndryshuar => O(n)
- Ndryshimi i të dhënave: shtimi i një shoku: Kemi thjeshtuar futjen e një gjyshi të ri, pasi tani nuk kemi nevojë të lexojmë tërë rreshtin dhe të kalojmë nëpër kolonat e tij, por mund të marrim vetëm vlerën e kolonës "count" dhe kështu të përcaktojmë menjëherë numrin e kolonës për futjen e gjyshit të ri. Kjo çon në një ulje të kompleksitetit të llogaritjes deri në O(1)
- Ndryshimi i të dhënave: heqja e një shoku: Kur gjatë fshirjes së një shoku, ne gjithashtu mund të përdorim këtë kolonë për të reduktuar numrin e operacioneve të hyres për të "shkruar" të dhënat një hap majtas. Por nevoja për të kaluar kolonat për të gjetur atë që duhet të fshihet mbetet, kështu që => O(n)
- Nga ana tjetër, tani në azhurnimin e të dhënave, na nevojitet të azhurnojmë gjithashtu kolonën "count" çdo herë, por kjo merr një kohë konstante, e cila në kuadrin e simbolikës O mund të injorohet.
Në përgjithësi, opsioni 2 duket pak më optimal, por kjo është më shumë "evolucion në vend të revolucionit". Për të realizuar një "revolucion", na nevojitet Opsioni 3 (col).
TĂ« pĂ«rmbysim gjithçka "me kokĂ« poshtĂ«": caktojmĂ« emrin e kolonĂ«s si identifikuesin e pĂ«rdoruesit.! ĂfarĂ« do tĂ« regjistrohet nĂ« kolonĂ« - pĂ«r ne nuk ka rĂ«ndĂ«si, le tĂ« jetĂ« numri 1 (nĂ« pĂ«rgjithĂ«si, nga e dobishme atje mund tĂ« ruhet, pĂ«r shembull, grupi "familje/miq/etj."). Ky qasje mund tĂ« befasojĂ« njĂ« "tĂ« zakonshĂ«m" tĂ« papĂ«rgatitur, i cili nuk kishte pĂ«rvoja me bazat e tĂ« dhĂ«nave NoSQL, por pikĂ«risht ky qasje lejon tĂ« pĂ«rdoret potenciali i HBase nĂ« kĂ«tĂ« detyrĂ« shumĂ« mĂ« efikas.
RowKey
Kolona
Vasja
Petja: 1
Olja: 1
Dasha: 1
Petja
Masha: 1
Vasja: 1
Këtu ne marrim disa avantazhe të menjëhershme. Për ta kuptuar këtë, le të analizojmë strukturën e re dhe të vlerësojmë kompleksitetin e llogaritjes:
- Leximi i tĂ« dhĂ«nave: pĂ«r tĂ« pĂ«rgjigjur pyetjes, a ka Vasja qenĂ« i regjistruar pĂ«r Olin, Ă«shtĂ« e mjaftueshme tĂ« lexohet njĂ« kolona "Ola": nĂ«se ekziston, pĂ«rgjigja Ă«shtĂ« e vĂ«rtetĂ«, nĂ«se jo â e falsa => O(1)
- Ndryshimi i të dhënave: shtimi i një shoku: Shtimi i një shoku: mjafton vetëm të shtoni një kolona të re "ID e shokut" => O(1)
- Ndryshimi i të dhënave: heqja e një shoku: mjafton vetëm të fshini kolona "ID e shokut" => O(1)
Siç e shohim, njĂ« pĂ«rparĂ«si thelbĂ«sore e kĂ«tij modeli ruajtjeje Ă«shtĂ« se nĂ« tĂ« gjitha skenarĂ«t e nevojshĂ«m operojmĂ« vetĂ«m me njĂ« kolona, duke shmangur leximin e tĂ«rĂ« rreshtit nga baza, dhe pĂ«r mĂ« tepĂ«r, kalimin nĂ«pĂ«r tĂ« gjitha kolonat e kĂ«tij rreshti. KĂ«tu mund tĂ« ndalemi, porâŠ
Mund tĂ« shqetĂ«sohemi dhe tĂ« shkojmĂ« pak mĂ« tutje nĂ« rrugĂ«n e optimizimit tĂ« performancĂ«s dhe zvogĂ«limit tĂ« operacioneve tĂ« input-output gjatĂ« aksesit nĂ« bazĂ«n e tĂ« dhĂ«nave. ĂfarĂ« ndodh nĂ«se ruajmĂ« tĂ« gjitha informacionet pĂ«r lidhjen direkt nĂ« çelĂ«sin e rreshtit? Pra, tĂ« bĂ«jmĂ« çelĂ«sin me shumĂ« pjesĂ« si userID.friendID? NĂ« kĂ«tĂ« rast ne madje as qĂ« mund tĂ« lexojmĂ« kolonat e rreshtit (Opsioni 4(row)):
RowKey
Kolona
Vasja.Petja
Petja: 1
Vasja.Ola
Olja: 1
Vasja.Dasha
Dasha: 1
Petja.Masha
Masha: 1
Petja.Vasja
Vasja: 1
ĂshtĂ« e qartĂ« se vlerĂ«simi i tĂ« gjitha skenarĂ«ve tĂ« manipulimit tĂ« tĂ« dhĂ«nave nĂ« njĂ« strukturĂ« tĂ« tillĂ«, ashtu si edhe nĂ« variantin e mĂ«parshĂ«m, do tĂ« jetĂ« O(1). Diferenca me variantin 3 do tĂ« jetĂ« ekskluzivisht nĂ« efikasitetin e operacioneve tĂ« futjes dhe leximit nĂ« bazĂ«n e tĂ« dhĂ«nave.
Dhe njĂ« âfundiâ i fundit. ĂshtĂ« e lehtĂ« tĂ« vĂ«rehet se nĂ« variantin 4 vendi i çelĂ«sit tĂ« vargut do tĂ« ketĂ« njĂ« gjatĂ«si tĂ« ndryshueshme, e cila ndoshta mund tĂ« ketĂ« njĂ« ndikim nĂ« performancĂ« (kĂ«tĂ« e kujtojmĂ«, pasi HBase ruan tĂ« dhĂ«nat si njĂ« grup bajtoresh dhe vargjet nĂ« tabela janĂ« tĂ« renditura sipas çelĂ«sit). PĂ«rveç kĂ«saj, kemi njĂ« ndarĂ«s, i cili nĂ« disa skenarĂ« mund tĂ« kĂ«rkojĂ« pĂ«rpunim. PĂ«r tĂ« eleminuar kĂ«tĂ« ndikim, mund tĂ« pĂ«rdoren hash-e nga userID dhe friendID, dhe pasi tĂ« dy hash-et do tĂ« kenĂ« njĂ« gjatĂ«si tĂ« qĂ«ndrueshme, mund t'i konkatenuar ata pa ndarĂ«s. AtĂ«herĂ« tĂ« dhĂ«nat nĂ« tabelĂ« do tĂ« duken kĂ«shtu.Variant 5(hash)):
RowKey
Kolona
dc084ef00e94aef49be885f9b01f51c01918fa783851db0dc1f72f83d33a5994
Petja: 1
dc084ef00e94aef49be885f9b01f51c0f06b7714b5ba522c3cf51328b66fe28a
Olja: 1
dc084ef00e94aef49be885f9b01f51c00d2c2e5d69df6b238754f650d56c896a
Dasha: 1
1918fa783851db0dc1f72f83d33a59949ee3309645bd2c0775899fca14f311e1
Masha: 1
1918fa783851db0dc1f72f83d33a5994dc084ef00e94aef49be885f9b01f51c0
Vasja: 1
ĂshtĂ« e qartĂ« se kompleksiteti algoritmik i punĂ«s me kĂ«tĂ« strukturĂ« pĂ«r skenarĂ«t qĂ« po shqyrtojmĂ«, do tĂ« jetĂ« i njĂ«jtĂ« me variantin 4 â domethĂ«nĂ« O(1).
Pra, le të përmbledhim të gjitha vlerësimet tona të kompleksitetit të llogaritjes në një tabelë:
Shtimi i një shoku
Kontrolli i një shoku
Fshirja e një shoku
Varianti 1 (default)
O(n)
O(n)
O(n)
Varianti 2 (count)
O(1)
O(n)
O(n)
Varianti 3 (column)
O(1)
O(1)
O(1)
Varianti 4 (row)
O(1)
O(1)
O(1)
Varianti 5 (hash)
O(1)
O(1)
O(1)
Siç shihet, variantet 3-5 duken më të preferuarat dhe teorikisht sigurojnë realizimin e të gjithë skenarëve të nevojshëm për manipulimin e të dhënave brenda një kohe konstante. Në kushtet e detyrës tonë nuk ka një kërkesë të qartë për të marrë një listë të gjithë shokëve të përdoruesit, por në aktivitete të vërteta projektimi, ne, si analistë të mirë, është mirë të "parashikojmë" që një detyrë e tillë mund të shfaqet dhe të "vendosim kashtën". Prandaj, preferencat e mia janë në anën e variantit 3. Por ka shumë mundësi që në një projekt të vërtetë, kjo kërkesë mund të jetë zgjidhur me mjete të tjera, prandaj pa një vizion të përgjithshëm të detyrës, më mirë mos të nxjerrim përfundime përfundimtare.
Përgatitja e eksperimentit
DĂ«shirojmĂ« tĂ« verifikojmĂ« teoritĂ« e mĂ«sipĂ«rme nĂ« praktikĂ« â kjo ishte edhe qĂ«llimi i mendimit qĂ« lindi gjatĂ« fundjavĂ«s sĂ« gjatĂ«. PĂ«r kĂ«tĂ« Ă«shtĂ« e nevojshme tĂ« vlerĂ«sojmĂ« shpejtĂ«sinĂ« e funksionimit tĂ« "aplikacionit tonĂ« hipotetik" nĂ« tĂ« gjithĂ« skenarĂ«t e pĂ«rshkruar tĂ« pĂ«rdorimit tĂ« bazĂ«s, si dhe rritjen e kĂ«tij koha me rritjen e madhĂ«sisĂ« sĂ« rrjetit social (n). Parametri i synuar qĂ« na intereson dhe qĂ« do tĂ« masim gjatĂ« eksperimentit Ă«shtĂ« koha qĂ« "aplikacioni hipotetik" shpenzon pĂ«r tĂ« realizuar njĂ« "operacion biznesi". Me "operacion biznesi" kuptojmĂ« njĂ«rin nga sa vijon:
- Shtimi i një shoku të ri
- Kontrolli nëse përdoruesi A është shoku i përdoruesit B
- Hiqja e një shoku
Pra, duke pasur parasysh kërkesat e përcaktuara në premisën fillestare, skenari i kontrollit vizatohet si vijon:
- ShĂ«nimi i tĂ« dhĂ«nave. Generoni njĂ« rrjet tĂ« rastĂ«sishĂ«m me madhĂ«si n. PĂ«r njĂ« afĂ«rsi mĂ« tĂ« madhe me "botĂ«n reale", numri i miqve tĂ« çdo pĂ«rdoruesi Ă«shtĂ« gjithashtu njĂ« vlerĂ« e rastĂ«sishme. Matni kohĂ«n qĂ« do tĂ« marrĂ« "aplikacioni ynĂ« hipotetik" pĂ«r tĂ« regjistruar tĂ« dhĂ«nat e gjeneruara nĂ« HBase. MĂ« pas, ndani kohĂ«n e marrĂ« me numrin e pĂ«rgjithshĂ«m tĂ« miqve tĂ« shtuar â kĂ«shtu do tĂ« kemi kohĂ«n mesatare pĂ«r njĂ« "operacion biznesi".
- Leximi i të dhënave. Për çdo përdorues, përgatitni një listë 'identitetesh' për të cilat duhet të merrni përgjigje nëse përdoruesi i ndjek ato apo jo. Gjatësia e listës = afërsisht numri i miqve të përdoruesit, përveç që për gjysmën e miqve që kontrollohen, përgjigja duhet të jetë "Po", ndërsa për gjysmën tjetër "Jo". Kontrolli bëhet në këtë rend, në mënyrë që përgjigjet "Po" dhe "Jo" të alternohen (do të na duhet të kalojmë të gjitha kolonat e rreshtit për opsionet 1 dhe 2 në çdo rast të dytë). Koha totale e kontrolleve më pas ndahet me numrin e miqve të kontrolluar për të marrë kohën mesatare të kontrollit për një subjekt.
- Fshirja e të dhënave. Të hiqni të gjithë miqtë nga përdoruesi. Në përputhje me këtë, rendi i eliminimit është rastësor (do të 'shkrijmë' listën fillestare që është përdorur për regjistrimin e të dhënave). Koha e përgjithshme e verifikimit më pas ndahet me numrin e miqve që janë hequr për të marrë kohën mesatare për një kontroll.
Skenarët duhet të ekzekutohen për çdo nga 5 variantet e modeleve të të dhënave dhe për madhësi të ndryshme të rrjetit social, për të parë se si ndryshon koha me rritjen e tij. Brenda një n lidhjesh në rrjet, lista e përdoruesve për kontroll duhet të jetë, natyrisht, e njëjtë për të 5 variantet.
Për një kuptim më të mirë, më poshtë po sjell një shembull të të dhënave të gjeneruara për n= 5. 'Generatori' i shkruar jep tre fjalorë ID-sh:
- i pari â pĂ«r futje
- i dyti â pĂ«r verifikim
- i treti â pĂ«r eliminim
{0: [1], 1: [4, 5, 3, 2, 1], 2: [1, 2], 3: [2, 4, 1, 5, 3], 4: [2, 1]} # gjithsej 15 miq
{0: [1, 10800], 1: [5, 10800, 2, 10801, 4, 10802], 2: [1, 10800], 3: [3, 10800, 1, 10801, 5, 10802], 4: [2, 10800]} # gjithsej 18 subjekte të kontrolluara
{0: [1], 1: [1, 3, 2, 5, 4], 2: [1, 2], 3: [4, 1, 2, 3, 5], 4: [1, 2]} # gjithsej 15 miq
Siç mund të vëreni, të gjithë ID-të mbi 10,000 në fjalor për verifikim janë ato që me siguri do të japin përgjigje False. Shtimi, verifikimi dhe fshirja e "miqve" kryhen pikërisht në rendin e caktuar në fjalor.
Eksperimenti u realizua nĂ« njĂ« laptop qĂ« drejtonte Windows 10, ku njĂ« enĂ« Docker ishte e angazhuar nĂ« njĂ« bazĂ« tĂ« dhĂ«nash HBase, dhe njĂ« tjetĂ«r â Python me Jupyter Notebook. Docker-i kishte tĂ« rezervuara 2 bĂ«rthama CPU dhe 2 GB RAM. TĂ« gjitha logjikat, si emulimi i funksionimit tĂ« "aplikacionit tĂ« supozuar", ashtu edhe "mbĂ«shtetje" pĂ«r tĂ« gjeneruar tĂ« dhĂ«na testuese dhe pĂ«r tĂ« matur kohĂ«n, ishin shkruar nĂ« Python. PĂ«r punĂ«n me HBase u pĂ«rdor biblioteka , pĂ«r llogaritjen e hash-eve (MD5) pĂ«r variantin 5 â hashlib.
Duke marrĂ« parasysh fuqinĂ« llogaritĂ«se tĂ« laptopit tĂ« caktuar, u zgjodh eksperimenti me n = 10, 30, âŠ. 170 â kur koha totale e zbatimit tĂ« plotĂ« tĂ« testit (tĂ« gjithĂ« skenarĂ«t pĂ«r tĂ« gjitha variantet pĂ«r tĂ« gjitha n) ishte ende mĂ« shumĂ« e arsyeshme dhe pĂ«rshtatej brenda njĂ« kohe tĂ« çajit (nĂ« mesatare 15 minuta).
Këtu është e nevojshme të bëhet një sqarim, se në këtë eksperiment në radhë të parë po vlerësojmë jo numrat e absolutë të performancës. Edhe krahasimi relatif i dy varianteve të ndryshme mund të mos jetë plotësisht korrekt. Aktualisht na intereson natyra e ndryshimit të kohës në varësi të n, pasi duke marrë parasysh konfigurimin e përmendur më sipër të "stendës testuese", është shumë e vështirë të marrim vlerësime të kohës, "të pastra" nga ndikimi i faktorëve rastësorë dhe të tjerë (as nuk është bërë një detyrë e tillë).
Rezultati i eksperimenti
Testi i parĂ« â si ndryshon koha e nevojshme pĂ«r tĂ« mbushur listĂ«n e miqve. Rezultati â nĂ« grafikĂ«n mĂ« poshtĂ«.

Variantet 3-5, siç pritej, tregojnë një kohë praktikisht konstante për "operacionin e biznesit", e cila nuk varet nga rritja e madhësisë së rrjetit dhe ka një ndryshim të padallueshëm në performancë.
Opsioni 2 tregon gjithashtu njĂ« performancĂ« konstante, por disi mĂ« tĂ« dobĂ«t, pothuajse me 2 herĂ« mĂ« pak se opsionet 3-5. Dhe kjo nuk mund tĂ« mos na gĂ«zojĂ«, sepse Ă«shtĂ« nĂ« pĂ«rputhje me teorinĂ« â nĂ« kĂ«tĂ« opsion numri i operacioneve tĂ« hyrjes/daljes nĂ«/nga HBase Ă«shtĂ« pikĂ«risht 2 herĂ« mĂ« i madh. KĂ«to mund tĂ« shĂ«rbejnĂ« si dĂ«shmi indirekte qĂ« qĂ«ndrimi ynĂ« testues nĂ« parim ofron njĂ« saktĂ«si tĂ« mirĂ«.
Opsioni 1 gjithashtu natyrisht rezulton të jetë më i ngadalshëm dhe tregon një rritje lineare të kohës së kaluar për të shtuar njëri-tjetrin në përputhje me madhësinë e rrjetit.
Tani le të shikojmë rezultatet e testit të dytë.

Opsionet 3-5 përsëri veprojnë siç pritej - koha konstante, e pavarur nga madhësia e rrjetit. Opsionet 1 dhe 2 demonstrojnë rritje lineare të kohës me rritjen e madhësisë së rrjetit dhe një performancë të ngjashme. Megjithatë, opsioni 2 rezulton të jetë pak më i ngadaltë - duket se për shkak të nevojës për të lexuar dhe përpunuar kolonën shtesë "count", e cila bëhet më e dukshme me rritjen e n. Megjithatë, do të përmbahen nga çdo përfundim, pasi saktësia e këtij krahasimi është relativisht e ulët. Për më tepër, këto marrëdhënie (cilin opsion, 1 apo 2, është më i shpejtë) ndryshonin nga nisja në nisje (duke ruajtur karakterin e varshmërisë dhe "shkuar nën hundë me hundë").
Dhe grafiku përfundimtar - rezultati i testimit të fshirjes.

Këtu gjithashtu pa surpriza. Opsionet 3-5 e kryejnë fshirjen me kohë konstante.
ĂfarĂ« Ă«shtĂ« interesante, opsionet 4 dhe 5, ndryshe nga skenarĂ«t e mĂ«parshĂ«m, tregojnĂ« njĂ« performancĂ« pak mĂ« tĂ« dobĂ«t se opsioni 3. Duket se operacioni i fshirjes sĂ« rreshtit Ă«shtĂ« mĂ« i kushtueshĂ«m se operacioni i fshirjes sĂ« kolonĂ«s, gjĂ« qĂ« Ă«shtĂ« logjike nĂ« pĂ«rgjithĂ«si.
Opsionet 1 dhe 2, siç pritej, tregojnë rritje lineare të kohës. Megjithatë, opsioni 2 është konstant më i ngadalshëm se opsioni 1 - për shkak të operacionit shtesë të hyrjes/daljes për "mirëmbajtjen" e kolonës count.
Përfundimet e përgjithshme të eksperimenti:
- Opsionet 3-5 tregojnë më shumë efikasitet, pasi shfrytëzojnë avantazhet e HBase; performanca e tyre, për këtë arsye, ndryshon nga njëra-tjetra me një konstant dhe nuk varet nga madhësia e rrjetit.
- Dallimi ndĂ«rmjet opsioneve 4 dhe 5 nuk u regjistrua. Por kjo nuk do tĂ« thotĂ« se opsioni 5 nuk duhet pĂ«rdorur. ĂshtĂ« shumĂ« e mundur qĂ« skenari i eksperimenti, duke marrĂ« parasysh TTHK e murit testues, nuk e lejoi ta identifikonte atĂ«.
- Karakteri i rritjes së kohës së nevojshme për të kryer "operacionet e biznesit" me të dhëna, në përgjithësi konfirmoi teoria e mëparshme për të gjitha opsionet.
Epilogu
Eksperimentet e rënda të kryera nuk duhet marrë si të vërteta absolute. Ka shumë faktorë që nuk janë marrë parasysh dhe që kanë sjellë devijime në rezultatet (sidomos këto fluksione janë të dukshme në grafika me madhësi të vogël rrjeti). Për shembull, shpejtësia e punës së thrift, që përdoret nga happybase, vëllimi dhe mënyra e implementimit të logjikës që kam shkruar në Python (nuk pretendoj se kodi është shkruar optimalisht dhe përdor maksimalisht potencialet e të gjitha komponenteve), ndoshta karakteristikat e memorizimit të HBase, aktiviteti në background i Windows 10 në laptopin tim, etj. Në përgjithësi, mund të themi se të gjitha përllogaritjet teorike kanë treguar vlefshmërinë e tyre eksperimentalisht. Ose të paktën nuk mund të demantojmë ato në këtë mënyrë 'në mënyrë të drejtpërdrejtë'.
NĂ« pĂ«rfundim â rekomandime pĂ«r tĂ« gjithĂ« ata qĂ« sapo kanĂ« filluar tĂ« projektojnĂ« modele tĂ« dhĂ«nash nĂ« HBase: distancohuni nga pĂ«rvoja e mĂ«parshme me bazat e tĂ« dhĂ«nave relacional dhe mbani mend 'ura':
- Kur projektojmë, nisim nga problemi dhe nga modeli i manipulimit të të dhënave, dhe jo nga modeli i fushës së subjektit.
- Qasje efektive (pa skanimin e plotĂ« tĂ« tabelĂ«s) â vetĂ«m me çelĂ«sin.
- Denormalizimi
- Rreshta të ndryshme mund të përmbajnë kolona të ndryshme
- Formacioni dinamik i kolonnave
Burimi: habr.com

