Teoria i praktyka wykorzystania HBase

Dzień dobry! Nazywam się Danil Lipowoj, nasz zespół w Sbertechu zaczął korzystać z HBase jako magazyn danych operacyjnych. W trakcie jego badania zgromadzono doświadczenia, które chciałem usystematyzować i opisać (mamy nadzieję, że będzie to przydatne dla wielu osób). Wszystkie poniżej przedstawione eksperymenty przeprowadzono na wersjach HBase 1.2.0-cdh5.14.2 i 2.0.0-cdh6.0.0-beta1.

  1. Ogólna architektura
  2. Zapisywanie danych w HBASE
  3. Odczytywanie danych z HBASE
  4. Buforowanie danych
  5. Przetwarzanie wsadowe danych MultiGet/MultiPut
  6. Strategia podziału tabel na regiony (sharding)
  7. Odporność na awarie, kompresja i lokalność danych
  8. Ustawienia i wydajność
  9. Testowanie obciążenia
  10. Wnioski

1. Ogólna architektura

Teoria i praktyka wykorzystania HBase
Zapasowy Master nasłuchuje heartbeat aktywnego na węźle ZooKeeper i w przypadku jego zniknięcia przejmuje funkcje mastera.

2. Zapisywanie danych w HBASE

Najpierw rozważymy najprostszy przypadek – zapis obiektu klucz-wartość w pewnej tabeli przy użyciu put(rowkey). Klient najpierw musi ustalić, gdzie znajduje się serwer główny regionu (Root Region Server — RRS), który przechowuje tabelę hbase:meta. Informacje tę uzyskuje od ZooKeeper. Następnie zwraca się do RRS i odczytuje tabelę hbase:meta, z której wyciąga informację, który RegionServer (RS) odpowiada za przechowywanie danych dla podanego klucza rowkey w interesującej go tabeli. W celu dalszego wykorzystania tabela meta jest buforowana przez klienta, dlatego kolejne zapytania są szybsze, bezpośrednio do RS.

Następnie RS, po otrzymaniu zapytania, najpierw zapisuje je w WriteAheadLog (WAL), co jest niezbędne do odtworzenia w przypadku awarii. Następnie zapisuje dane w MemStore. To bufor w pamięci, który zawiera posortowany zestaw kluczy danego regionu. Tabela może być podzielona na regiony (partyacje), z których każdy zawiera nieprzecinający się zbiór kluczy. Pozwala to, umieszczając regiony na różnych serwerach, uzyskać wyższą wydajność. Jednak mimo oczywistości tego stwierdzenia, później zobaczymy, że to nie działa we wszystkich przypadkach.

Po umieszczeniu wpisu w MemStore klient otrzymuje odpowiedź, że wpis został pomyślnie zapisany. W rzeczywistości jest on przechowywany tylko w buforze i trafi na dysk dopiero po upływie pewnego czasu lub po zapełnieniu go nowymi danymi.

Teoria i praktyka wykorzystania HBase
W trakcie wykonywania operacji „Delete” fizyczne usunięcie danych nie następuje. Zostają one po prostu oznaczone jako usunięte, a samo zniszczenie ma miejsce w momencie wywołania funkcji major compact, o której więcej napisano w pkt 7.

Pliki w formacie HFile gromadzą się w HDFS, a od czasu do czasu uruchamiany jest proces minor compact, który po prostu łączy małe pliki w większe, nie usuwając niczego. Z biegiem czasu przekształca się to w problem, który objawia się tylko przy odczycie danych (do tego wrócimy nieco później).

Oprócz opisanego powyżej procesu ładowania istnieje znacznie efektywniejsza procedura, w której zawarta jest chyba najsilniejsza strona tej bazy danych – BulkLoad. Polega ona na tym, że samodzielnie tworzymy HFiles i umieszczamy je na dysku, co pozwala na doskonałą skalowalność i osiąganie całkiem przyzwoitych prędkości. W istocie ograniczeniem jest tu nie HBase, lecz możliwości sprzętowe. Poniżej przedstawione są wyniki ładowania na klastrze składającym się z 16 RegionServers i 16 NodeManager YARN (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 wątki), wersja HBase 1.2.0-cdh5.14.2.

Teoria i praktyka wykorzystania HBase

Widać, że zwiększając liczbę partycji (regionów) w tabeli oraz liczby executorów Spark, otrzymujemy przyrost prędkości ładowania. Prędkość zależy również od objętości zapisu. Duże bloki dają przyrost w miarze MB/s, małe w liczbie wstawionych rekordów w jednostce czasu, przy innych równych warunkach.

Można również uruchomić ładowanie w dwóch tabelach jednocześnie i uzyskać podwojone prędkości. Poniżej widać, że zapis bloków 10 KB od razu do dwóch tabel odbywa się z prędkością około 600 MB/s w każdej (łącznie 1275 MB/s), co odpowiada prędkości zapisu do jednej tabeli 623 MB/s (zob. nr 11 powyżej).

Teoria i praktyka wykorzystania HBase
Natomiast drugi uruchomienie z zapisami 50 KB pokazuje, że prędkość ładowania rośnie już nieznacznie, co wskazuje na zbliżanie się do wartości granicznych. Należy przy tym mieć na uwadze, że same mu HBASE praktycznie nie stawia się obciążeń, wszystko co od niego wymagane, to najpierw oddanie danych z hbase:meta, a następnie po umieszczeniu HFiles, zrzucenie danych BlockCache i zapisanie bufora MemStore na dysk, jeśli nie jest pusty.

3. Odczyt danych z HBASE

Jeśli zakładamy, że wszystkie informacje z hbase:meta są już dostępne dla klienta (patrz pkt 2), zapytanie jest kierowane bezpośrednio do tego RS, w którym przechowywany jest poszukiwany klucz. Najpierw wyszukiwanie odbywa się w MemCache. Niezależnie od tego, czy dane tam istnieją, wyszukiwanie odbywa się również w buforze BlockCache i w razie potrzeby w HFiles. Jeśli dane zostały znalezione w pliku, są one umieszczane w BlockCache i przy następnym zapytaniu będą zwracane szybciej. Wyszukiwanie w HFile odbywa się stosunkowo szybko dzięki zastosowaniu filtru Blooma, tzn. po odczytaniu małej ilości danych od razu ustala, czy ten plik zawiera poszukiwany klucz, a jeśli nie, przechodzi do następnego.

Teoria i praktyka wykorzystania HBase
Po uzyskaniu danych z tych trzech źródeł RS formułuje odpowiedź. W szczególności może przekazać od razu kilka znalezionych wersji obiektu, jeśli klient zażądał wersjonowania.

4. Cache danych

Bufory MemStore i BlockCache zajmują do 80% przydzielonej pamięci on-heap RS (pozostałe przeznaczone są na zadania serwisowe RS). Jeśli typowy sposób użytkowania polega na tym, że procesy zapisują i natychmiast odczytują te same dane, warto zmniejszyć BlockCache i zwiększyć MemStore, ponieważ przy zapisie dane do pamięci cache na odczyt nie trafiają, co powoduje rzadsze wykorzystanie BlockCache. Bufor BlockCache składa się z dwóch części: LruBlockCache (zawsze on-heap) i BucketCache (zwykle off-heap lub na SSD). BucketCache warto zastosować, gdy zapytań o odczyt jest bardzo dużo i nie mieszczą się one w LruBlockCache, co prowadzi do intensywnej pracy Garbage Collector. Przy tym nie należy oczekiwać radykalnego wzrostu wydajności z wykorzystania cache'u do odczytu, jednak do tego jeszcze wrócimy w pkt 8.

Teoria i praktyka wykorzystania HBase
BlockCache jest jeden dla całego RS, a MemStore jest osobny dla każdej tabeli (po jednym na każdy Column Family).

Jak opisano w teorii, przy zapisie dane do pamięci cache nie trafiają i rzeczywiście, takie parametry CACHE_DATA_ON_WRITE dla tabeli oraz „Cache DATA on Write” dla RS są ustawione na false. Jednak w praktyce, jeśli zapiszesz dane w MemStore, a następnie zrzucisz je na dysk (w ten sposób je czyszcząc), a następnie usuniesz powstały plik, to wykonując zapytanie get, pomyślnie uzyskasz dane. Co więcej, nawet jeśli całkowicie wyłączysz BlockCache i wypełnisz tabelę nowymi danymi, następnie wymusisz zrzut MemStore na dysk, usuniesz je i zapytasz z innej sesji, to i tak zostaną one skądś wydobyte. Tak więc HBase przechowuje w sobie nie tylko dane, ale i tajemnicze zagadki.

hbase(main):001:0> create 'ns:magic', 'cf'
Stworzono tabelę ns:magic
Zajęło 1.1533 sekundy
hbase(main):002:0> put 'ns:magic', 'key1', 'cf:c', 'spróbuj_usunąć_mnie'
Zajęło 0.2610 sekundy
hbase(main):003:0> flush 'ns:magic'
Zajęło 0.6161 sekundy
hdfs dfs -mv /data/hbase/data/ns/magic/* /tmp/trash
hbase(main):002:0> get 'ns:magic', 'key1'
 cf:c      znacznik_czasu=1534440690218, wartość=spróbuj_usunąć_mnie

Parametr „Cache DATA on Read” jest ustawiony na false. Jeśli masz pomysły, zapraszam do omówienia ich w komentarzach.

5. Przetwarzanie wsadowe danych MultiGet/MultiPut

Przetwarzanie pojedynczych żądań (Get/Put/Delete) jest dość kosztowną operacją, dlatego zaleca się łączenie ich w List lub List, co pozwala na znaczną poprawę wydajności. Dotyczy to w szczególności operacji zapisu, a przy odczycie pojawia się kolejna pułapka. Na poniższym wykresie pokazano czas odczytu 50 000 zapisów z MemStore. Odczyt był realizowany w jednym wątku, a na osi poziomej pokazano liczbę kluczy w żądaniu. Widać, że wraz z zwiększeniem liczby kluczy w jednym żądaniu do tysiąca czas wykonania maleje, tzn. szybkość rośnie. Jednakże przy włączonym domyślnym trybie MSLAB po tym progu zaczyna się radykalny spadek wydajności, przy czym im większa objętość danych w zapisie, tym dłuższy czas operacji.

Teoria i praktyka wykorzystania HBase

Testy przeprowadzono na wirtualnej maszynie, 8 rdzeni, wersja HBase 2.0.0-cdh6.0.0-beta1.

Tryb MSLAB ma na celu zmniejszenie fragmentacji pamięci heap, która powstaje w wyniku mieszania danych młodszych i starszych pokoleń. Jako rozwiązanie problemu, przy włączeniu MSLAB dane są umieszczane w stosunkowo małych komórkach (chunk) i przetwarzane partiami. W rezultacie, kiedy objętość w żądanym pakiecie danych przekracza przydzielony rozmiar, wydajność nagle spada. Z drugiej strony, wyłączenie tego trybu również nie jest pożądane, ponieważ prowadzi do zatrzymań z powodu GC w momentach intensywnej pracy z danymi. Dobrym rozwiązaniem jest zwiększenie objętości komórki, w przypadku aktywnego zapisu przez put jednocześnie z odczytem. Warto zauważyć, że problem nie występuje, jeśli po zapisaniu wykonasz polecenie flush, które zrzuca MemStore na dysk, lub jeśli następuje załadunek za pomocą BulkLoad. W poniższej tabeli pokazano, że żądania z MemStore większej objętości (i tej samej liczby) prowadzą do opóźnień. Jednak zwiększając chunksize, przywracamy czas przetwarzania do normy.

Teoria i praktyka wykorzystania HBase
Oprócz zwiększenia chunksize pomocne jest podziałanie danych według regionów, tj. splitowanie tabel. Prowadzi to do tego, że na każdy region przypada mniejsza liczba zapytań, a jeśli mieszczą się one w komórce, odpowiedź pozostaje dobra.

6. Strategia podziału tabel według regionów (splitowanie)

Ponieważ HBase jest przechowalnią key-value, a partycjonowanie odbywa się według klucza, niezwykle ważne jest, aby równomiernie rozdzielać dane wśród wszystkich regionów. Na przykład, partycjonowanie takiej tabeli na trzy części prowadzi do tego, że dane będą podzielone na trzy regiony:

Teoria i praktyka wykorzystania HBase
Czasami prowadzi to do znacznego spowolnienia, jeśli w dalszej kolejności wczytywane dane będą miały postać np. wartości long, które w większości zaczynają się od tej samej cyfry, na przykład:

1000001
1000002

1100003

Ponieważ klucze są przechowywane w postaci tablicy bajtów, wszystkie one będą zaczynały się jednakowo i odnosiły się do jednego regionu #1 przechowującego ten zakres kluczy. Istnieje kilka strategii podziału:

HexStringSplit – przekształca klucz w ciąg z szesnastkowym kodowaniem w zakresie „00000000” => „FFFFFFFF”, a na lewo wypełnia zerami.

UniformSplit – przekształca klucz w tablicę bajtów z szesnastkowym kodowaniem w zakresie „00” => „FF”, a na prawo wypełnia zerami.

Ponadto można wskazać dowolny zakres lub zestaw kluczy do podziału oraz skonfigurować automatyczne splitowanie. Jednak jednym z najprostszych i najskuteczniejszych podejść jest UniformSplit i wykorzystanie złączenia hash, na przykład wyższej pary bajtów uzyskanej z przepuszczenia klucza przez funkcję CRC32(rowkey) oraz samego rowkey:

hash + rowkey

Wówczas wszystkie dane będą równomiernie rozprowadzane wśród regionów. Podczas odczytu pierwsze dwa bajty są po prostu zrzucane, a pozostaje oryginalny klucz. RS kontroluje również liczbę danych i kluczy w regionie i w przypadku przekroczenia limitów automatycznie dzieli go na części.

7. Odporność na awarie i lokalność danych

Ponieważ za każdy zestaw kluczy odpowiada tylko jeden region, rozwiązaniem problemów związanych z awariami RS lub wyłączeniem z eksploatacji jest przechowywanie wszystkich niezbędnych danych w HDFS. W przypadku awarii RS, master wykrywa to poprzez brak heartbeat na węźle ZooKeeper. Wówczas przypisuje obsługiwany region innemu RS, a ponieważ HFiles są przechowywane w rozproszonej systemie plików, nowy właściciel odczytuje je i kontynuuje obsługę danych. Jednakże, ponieważ część danych może znajdować się w MemStore i nie została jeszcze zapisana w HFiles, do przywracania historii operacji wykorzystuje się WAL, który również jest przechowywany w HDFS. Po zatwierdzeniu zmian, RS jest w stanie odpowiadać na żądania, jednak przeprowadzka prowadzi do sytuacji, w której część danych oraz procesy ich obsługi znajdują się na różnych węzłach, tj. lokalność ulega zmniejszeniu.

Rozwiązaniem problemu jest major compaction – ta procedura przenosi pliki na te węzły, które za nie odpowiadają (tam, gdzie znajdują się ich regiony), w wyniku czego podczas tej procedury gwałtownie wzrasta obciążenie sieci i dysków. Jednak w dalszej perspektywie dostęp do danych staje się zauważalnie szybszy. Ponadto, major_compaction łączy wszystkie HFiles w jeden plik w obrębie regionu, a także oczyszcza dane w zależności od ustawień tabeli. Na przykład, można określić liczbę wersji obiektu, które należy zachować lub czas jego życia, po którego upływie obiekt jest fizycznie usuwany.

Ta procedura może mieć bardzo pozytywny wpływ na działanie HBase. Na poniższym obrazku widać, jak degradowała się wydajność w wyniku intensywnego zapisu danych. Można zauważyć, jak do jednej tabeli 40 wątków zapisywało i 40 wątków równocześnie odczytywało dane. Wątki zapisujące tworzą coraz więcej HFiles, które są odczytywane przez inne wątki. W wyniku tego coraz więcej danych musi zostać usuniętych z pamięci, a w końcu zaczyna działać GC, który praktycznie paraliżuje całą pracę. Uruchomienie major compaction doprowadziło do oczyszczenia powstałych zatorów i przywrócenia wydajności.

Teoria i praktyka wykorzystania HBase
Test przeprowadzono na 3 DataNode i 4 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 wątki). Wersja HBase 1.2.0-cdh5.14.2

Warto zauważyć, że uruchomienie major compaction odbyło się na „żywej” tabeli, do której aktywnie zapisywano i odczytywano dane. W sieci pojawiały się twierdzenia, że może to prowadzić do niepoprawnych odpowiedzi przy odczycie danych. W celu sprawdzenia uruchomiono proces, który generował nowe dane i zapisywał je w tabeli. Następnie od razu je odczytywano i porównywano, czy uzyskana wartość zgadza się z tym, co zapisano. W trakcie pracy tego procesu major compaction uruchamiano około 200 razy i nie zarejestrowano żadnych awarii. Możliwe, że problem pojawia się rzadko i tylko podczas wysokiego obciążenia, dlatego bezpieczniej jest jednak zaplanować zatrzymanie procesów zapisu i odczytu oraz wykonać czyszczenie, aby nie dopuszczać do takich spadków GC.

Również major compaction nie wpływa na stan MemStore, aby zrzucić go na dysk i przeprowadzić kompaktację, należy użyć flush (connection.getAdmin().flush(TableName.valueOf(tblName))).

8. Ustawienia i wydajność

Jak już wspomniano, HBase odnosi największy sukces tam, gdzie nie wymaga wiele działań, przy wykonywaniu BulkLoad. To dotyczy jednak większości systemów i ludzi. Niemniej jednak to narzędzie nadaje się raczej do masowego układania danych dużymi blokami, natomiast jeśli proces wymaga wypełnienia wielu konkurencyjnych zapytań o odczyt i zapis, używane są opisane powyżej polecenia Get i Put. Aby określić optymalne parametry, przeprowadzono uruchomienia przy różnych kombinacjach parametrów tabeli i ustawień:

  • Uruchomiono 10 wątków jednocześnie 3 razy z rzędu (nazwijmy to blokiem wątków).
  • Czas pracy wszystkich wątków w bloku był uśredniany i stanowił końcowy wynik pracy bloku.
  • Wszystkie wątki pracowały z tą samą tabelą.
  • Przed każdym uruchomieniem bloku wątków wykonywano major compaction.
  • Każdy blok wykonywał tylko jedną z następujących operacji:

— Put
— Get
— Get+Put

  • Każdy blok wykonywał 50 000 powtórzeń swojej operacji.
  • Rozmiar rekordu w bloku wynosił 100 bajtów, 1000 bajtów lub 10000 bajtów (losowo).
  • Bloki uruchamiano z różną liczbą żądanych kluczy (lub jeden klucz, lub 10).
  • Bloki uruchamiano przy różnych ustawieniach tabeli. Zmieniano parametry:

— BlockCache = włączany lub wyłączany
— BlockSize = 65 KB lub 16 KB
— Partycje = 1, 5 lub 30
— MSLAB = włączony lub wyłączony

W ten sposób blok wygląda tak:

a. Włączano/wyłączano tryb MSLAB.
b. Tworzenie tabeli, dla której ustalono następujące parametry: BlockCache = true/none, BlockSize = 65/16 Kb, Partycji = 1/5/30.
c. Ustalono kompresję GZ.
d. Uruchomiono 10 wątków jednocześnie wykonujących 1/10 operacji put/get/get+put w tej tabeli z zapisami po 100/1000/10000 bajtów, wykonując 50 000 zapytań z rzędu (klucze losowe).
e. Punkt d powtórzono trzy razy.
f. Czas pracy wszystkich wątków uśredniono.

Sprawdzono wszystkie możliwe kombinacje. Przewidywalnie, przy zwiększeniu rozmiaru zapisu prędkość będzie malała, a wyłączenie cache'owania spowoduje spowolnienie. Jednak celem było zrozumienie stopnia i znaczenia wpływu każdego parametru, dlatego zebrane dane zostały poddane liniowej regresji, co pozwala ocenić wiarygodność za pomocą statystyki t-studenta. Poniżej przedstawione są wyniki działania bloków wykonujących operacje Put. Pełny zestaw kombinacji 2*2*3*2*3 = 144 warianty + 72, ponieważ niektóre zostały wykonane dwukrotnie. W sumie 216 uruchomień:

Teoria i praktyka wykorzystania HBase
Testowanie przeprowadzono na mini-klastrze składającym się z 3 DataNode i 4 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 wątki). Wersja HBase 1.2.0-cdh5.14.2.

Najwyższa prędkość wstawiania 3.7 sekundy została osiągnięta przy wyłączonym trybie MSLAB, na tabeli z jedną partycją, z włączonym BlockCache, BlockSize = 16, zapisach po 100 bajtów w 10 sztukach w paczce.
Najniższa prędkość wstawiania 82.8 sekundy została osiągnięta przy włączonym trybie MSLAB, na tabeli z jedną partycją, z włączonym BlockCache, BlockSize = 16, zapisach po 10000 bajtów w 1 sztuce.

Teraz przyjrzyjmy się modelowi. Widoczna jest dobra jakość modelu według R2, ale całkowicie jasne jest, że ekstrapolacja w tym przypadku jest niewskazana. Rzeczywiste zachowanie systemu przy zmianie parametrów będzie nieliniowe, ten model jest potrzebny nie do prognozowania, a do zrozumienia, co się wydarzyło w obrębie zadanych parametrów. Na przykład tutaj widzimy, według kryterium Studenta, że dla operacji Put nie mają znaczenia parametry BlockSize i BlockCache (co w sumie jest całkiem przewidywalne):

Teoria i praktyka wykorzystania HBase
A to, że zwiększenie liczby partycji prowadzi do spadku wydajności, jest nieco zaskakujące (już widzieliśmy pozytywny wpływ zwiększenia liczby partycji przy BulkLoad), choć jest to zrozumiałe. Po pierwsze, aby przetworzyć dane, trzeba formułować zapytania do 30 regionów zamiast jednego, a objętość danych nie jest na tyle duża, aby to przyniosło korzyść. Po drugie, całkowity czas pracy determinuje najwolniejszy RS, a ponieważ liczba DataNode jest mniejsza od liczby RS, część regionów ma zerową lokalność. No i spójrzmy na pierwszą piątkę liderów:

Teoria i praktyka wykorzystania HBase
Teraz oceńmy wyniki wykonania bloków Get:

Teoria i praktyka wykorzystania HBase
Liczba partycji straciła na znaczeniu, co prawdopodobnie tłumaczy się tym, że dane są dobrze buforowane, a pamięć podręczna do odczytu jest najważniejszym (statystycznie) parametrem. Oczywiście zwiększenie liczby wiadomości w zapytaniu jest również bardzo korzystne dla wydajności. Najlepsze wyniki:

Teoria i praktyka wykorzystania HBase
No i w końcu spójrzmy na model bloku, który najpierw realizował get, a potem put:

Teoria i praktyka wykorzystania HBase
Tutaj wszystkie parametry są znaczące. A oto wyniki liderów:

Teoria i praktyka wykorzystania HBase

9. Testy obciążeniowe

No i w końcu uruchomimy w miarę rozsądne obciążenie, ale zawsze ciekawiej jest, gdy jest z czym porównywać. Na stronie DataStax – kluczowego dewelopera Cassandy jest wyniki NT kilku baz NoSQL, w tym HBase wersji 0.98.6-1. Ładowanie odbywało się 40 wątkami, rozmiar danych wynosił 100 bajtów, dyski SSD. Wynik testowania operacji Read-Modify-Write wykazał takie rezultaty.

Teoria i praktyka wykorzystania HBase
Z tego co zrozumiałem, odczyt realizowany był partiami po 100 rekordów, a dla 16 nodów HBase test DataStax wykazał wydajność 10 tys. operacji na sekundę.

Dobrze, że w naszym klastrze również jest 16 nodów, ale nie bardzo "dobrze", że na każdym są 64 rdzenie (wątki), podczas gdy w teście DataStax tylko po 4. Z drugiej strony mają dyski SSD, a u nas HDD oraz nowsza wersja HBase, a wykorzystanie CPU podczas obciążenia praktycznie nie wzrastało (wizualnie o 5-10 procent). Niemniej spróbujemy uruchomić się na tej konfiguracji. Ustawienia tabel są domyślne, odczyty odbywają się w zakresie kluczy od 0 do 50 mln losowo (tzn. w zasadzie za każdym razem nowe). W tabeli znajduje się 50 milionów rekordów, podzielonych na 64 partycje. Klucze są haszowane z użyciem crc32. Ustawienia tabel są domyślne, MSLAB jest włączony. Uruchomienie 40 wątków, każdy wątek odczytuje zestaw 100 losowych kluczy i natychmiast zapisuje wygenerowane 100 bajtów z powrotem pod tymi kluczami.

Teoria i praktyka wykorzystania HBase
Stojak: 16 DataNode i 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 wątki). Wersja HBase 1.2.0-cdh5.14.2.

Średni wynik bliżej 40 tys. operacji na sekundę, co jest znacznie lepsze niż w teście DataStax. Jednak w celach eksperymentalnych można nieco zmienić warunki. Mało prawdopodobne jest, aby cała praca odbywała się wyłącznie na jednej tabeli oraz tylko z unikalnymi kluczy. Załóżmy, że istnieje pewien „gorący” zestaw kluczy, który generuje główne obciążenie. Dlatego spróbujemy stworzyć obciążenie większymi rekordami (10 KB), również porcjami po 100, w 4 różnych tabelach i ograniczając zakres żądanych kluczy do 50 tys. Na poniższym wykresie przedstawiono uruchomienie 40 wątków, każdy wątek odczytuje zestaw 100 kluczy i natychmiast zapisuje losowe 10 KB pod tymi kluczami z powrotem.

Teoria i praktyka wykorzystania HBase
Stojak: 16 DataNode i 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 wątki). Wersja HBase 1.2.0-cdh5.14.2.

W trakcie obciążenia kilka razy uruchamiano major compaction, jak pokazano powyżej, bez tej procedury wydajność stopniowo by spadała, jednak w trakcie wykonania pojawia się również dodatkowe obciążenie. Spadki spowodowane są różnymi przyczynami. Czasami wątki kończyły pracę i podczas ich ponownego uruchamiania pojawiała się przerwa, czasami zewnętrzne aplikacje generowały obciążenie na klastrze.

Czytanie i jednoczesne zapisywanie to jeden z najtrudniejszych scenariuszy pracy dla HBase. Jeśli wykonujemy tylko zapytania put o małej wielkości, na przykład po 100 bajtów, łącząc je w pakiety po 10-50 tys. sztuk, można uzyskać setki tysięcy operacji na sekundę, a podobnie sytuacja wygląda w przypadku zapytań tylko do odczytu. Warto zauważyć, że wyniki są radykalnie lepsze niż te uzyskane przez DataStax, w dużej mierze dzięki zapytaniom blokowym po 50 tys.

Teoria i praktyka wykorzystania HBase
Stojak: 16 DataNode i 16 RS (CPU Xeon E5-2680 v4 @ 2.40GHz * 64 wątki). Wersja HBase 1.2.0-cdh5.14.2.

10. Wnioski

System ten jest wystarczająco elastyczny, jednak wpływ dużej liczby parametrów wciąż pozostaje nieznany. Część z nich została przetestowana, ale nie weszła do ostatecznego zbioru testów. Na przykład wstępne eksperymenty wykazały niewielkie znaczenie takiego parametru jak DATA_BLOCK_ENCODING, który koduje informacje, korzystając z wartości z sąsiednich komórek, co jest całkowicie zrozumiałe dla danych generowanych losowo. W przypadku użycia dużej liczby powtarzających się obiektów zysk może być znaczący. Ogólnie można powiedzieć, że HBase sprawia wrażenie dość poważnej i przemyślanej bazy danych, która przy operacjach z dużymi blokami danych może być wystarczająco wydajna. Szczególnie jeśli istnieje możliwość rozłożenia w czasie procesów odczytu i zapisu.

Jeśli coś według Ciebie nie zostało wystarczająco omówione, chętnie opowiem więcej. Zachęcamy do dzielenia się swoimi doświadczeniami lub dyskusji, jeśli z czymś się nie zgadzasz.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster