Wprowadzenie
Apache HBase to open-source Hadoop, rozproszony, wersjonowany menedżer pamięci masowej, który dobrze nadaje się do losowych , odczyt/zapis w czasie rzeczywistym dostęp.
Poczekaj poczekaj? losowy dostęp do odczytu/zapisu w czasie rzeczywistym?
Jak to możliwe? Czy Hadoop nie jest tylko sekwencyjnym systemem odczytu/zapisu przetwarzania wsadowego?
Tak, mówimy o tym samym, aw następnych kilku akapitach wyjaśnię, jak HBase osiąga losowe I/O, jak przechowuje dane i ewolucję formatu HFile HBase.
Formaty plików we/wy Apache Hadoop
Hadoop jest dostarczany z formatem pliku SequenceFile[1], którego można użyć do dołączenia par klucz/wartość, ale ze względu na funkcję hdfs tylko do dołączania, format pliku nie pozwala na modyfikację lub usunięcie wstawionej wartości. Jedyną dozwoloną operacją jest dołączanie, a jeśli chcesz wyszukać określony klucz, musisz przeczytać plik, aż znajdziesz swój klucz.
Jak widać, jesteś zmuszony podążać za sekwencyjnym wzorcem odczytu/zapisu… ale jak na tym tle zbudować losowy system dostępu do odczytu/zapisu o małych opóźnieniach, taki jak HBase?
Aby pomóc Ci rozwiązać ten problem, Hadoop ma inny format pliku, zwany MapFile[1], rozszerzenie SequenceFile. W rzeczywistości MapFile jest katalogiem zawierającym dwa SequenceFile:plik danych „/data” i plik indeksu „/index”. MapFile umożliwia dołączanie posortowanych par klucz/wartość, a każde N kluczy (gdzie N jest konfigurowalnym interwałem) przechowuje klucz i przesunięcie w indeksie. Pozwala to na dość szybkie wyszukiwanie, ponieważ zamiast skanować wszystkie rekordy, skanujesz indeks, który ma mniej wpisów. Po znalezieniu swojego bloku możesz przejść do prawdziwego pliku danych.
MapFile jest fajny, ponieważ możesz szybko wyszukać pary klucz/wartość, ale nadal istnieją dwa problemy:
- Jak mogę usunąć lub zamienić klucz/wartość?
- Gdy moje dane wejściowe nie są posortowane, nie mogę używać MapFile.
HBase i MapFile
Klucz HBase składa się z:klucza wiersza, rodziny kolumn, kwalifikatora kolumny, znacznika czasu i typu.
Aby rozwiązać problem usuwania par klucz/wartość, pomysł polega na użyciu pola „typ” do oznaczenia klucza jako usuniętego (znaczniki nagrobne). Rozwiązanie problemu zamiany par klucz/wartość to tylko kwestia wybrania późniejszego znacznika czasu (prawidłowa wartość znajduje się blisko końca pliku, append oznacza tylko, że ostatnie wstawione jest blisko końca).
Aby rozwiązać problem „nieuporządkowanego” klucza, przechowujemy w pamięci ostatnie dodane pary klucz-wartość. Po osiągnięciu progu HBase opróżnia go do pliku MapFile. W ten sposób dodajesz posortowane klucze/wartości do pliku MapFile.
HBase robi dokładnie to [2]:kiedy dodajesz wartość za pomocą table.put(), twój klucz/wartość jest dodawany do MemStore (pod maską MemStore jest posortowaną ConcurrentSkipListMap). Gdy zostanie osiągnięty próg dla pamięci (hbase.hregion.memstore.flush.size) lub RegionServer używa zbyt dużo pamięci dla pamięci (hbase.regionserver.global.memstore.upperLimit), dane są opróżniane na dysku jako nowy plik MapFile .
Wynikiem każdego opróżnienia jest nowy plik MapFile, a to oznacza, że aby znaleźć klucz, musisz przeszukać więcej niż jeden plik. Zabiera to więcej zasobów i jest potencjalnie wolniejsze.
Za każdym razem, gdy wydawane jest pobieranie lub skanowanie, HBase skanuje każdy plik, aby znaleźć wynik, aby uniknąć przeskakiwania zbyt wielu plików, istnieje wątek, który wykryje, kiedy osiągniesz określoną liczbę plików (hbase.hstore.compaction .maks.). Następnie próbuje scalić je razem w procesie zwanym kompaktowaniem, który zasadniczo tworzy nowy duży plik w wyniku scalenia plików.
HBase ma dwa rodzaje kompaktowania:jeden zwany „drobnym kompaktowaniem”, który po prostu łączy dwa lub więcej małych plików w jeden, oraz drugi zwany „głównym kompaktowaniem”, który zbiera wszystkie pliki w regionie, łączy je i wykonuje pewne czyszczenie. W dużym zagęszczeniu usunięte klucze/wartości są usuwane, ten nowy plik nie zawiera znaczników nagrobków, a wszystkie zduplikowane klucze/wartości (operacje zastępowania wartości) są usuwane.
Do wersji 0.20 HBase używał formatu MapFile do przechowywania danych, ale w 0.20 wprowadzono nowy MapFile specyficzny dla HBase (HBASE-61).
File v1
W HBase 0.20 MapFile jest zastępowany przez HFile:konkretną implementację pliku mapy dla HBase. Pomysł jest dość podobny do MapFile, ale dodaje więcej funkcji niż zwykły plik klucz/wartość. Funkcje, takie jak obsługa metadanych i indeksu, są teraz przechowywane w tym samym pliku.
Bloki danych zawierają rzeczywiste klucze/wartości jako plik MapFile. Dla każdej „operacji zamknięcia bloku” pierwszy klucz jest dodawany do indeksu, a indeks jest zapisywany na zamknięciu HFile.
Format HFile dodaje również dwa dodatkowe typy bloków „metadanych”:Meta i FileInfo. Te dwa bloki klucz/wartość są zapisywane po zamknięciu pliku.
Blok Meta został zaprojektowany do przechowywania dużej ilości danych z kluczem jako ciągiem, podczas gdy FileInfo jest prostą mapą preferowaną dla małych informacji z kluczami i wartościami, które są zarówno tablicą bajtów. StoreFile Regionserver używa Meta-Blocks do przechowywania filtra Bloom oraz FileInfo dla Max SequenceId, klucza głównego zagęszczenia i informacji o zakresie czasu. Ta informacja jest przydatna, aby uniknąć czytania pliku, jeśli nie ma szansy, że klucz jest obecny (Filtr Bloom), jeśli plik jest zbyt stary (Max SequenceId) lub jeśli plik jest zbyt nowy (Timerange), aby zawierał to, czego szukamy za.
File v2
W HBase 0.92 format HFile został nieco zmieniony (HBASE-3857), aby poprawić wydajność podczas przechowywania dużych ilości danych. Jednym z głównych problemów z HFile v1 jest to, że musisz załadować do pamięci wszystkie monolityczne indeksy i duże filtry kwitnienia, a aby rozwiązać ten problem, v2 wprowadza indeksy wielopoziomowe i filtr kwitnienia na poziomie bloków. W rezultacie HFile v2 oferuje lepszą szybkość, pamięć i wykorzystanie pamięci podręcznej.
Główną cechą tej wersji v2 są „bloki inline”, chodzi o to, aby łamać indeks i filtr Blooma na blok, zamiast przechowywać w pamięci cały indeks i filtr Blooma całego pliku. W ten sposób możesz trzymać w pamięci tylko to, czego potrzebujesz.
Ponieważ indeks jest przenoszony na poziom bloku, masz indeks wielopoziomowy, co oznacza, że każdy blok ma swój własny indeks (indeks liścia). Ostatni klucz każdego bloku jest zachowywany w celu utworzenia pośredniego/indeksu, który upodabnia wielopoziomowy indeks b+drzewo.
Nagłówek bloku zawiera teraz pewne informacje:Pole „Magia bloku” zostało zastąpione polem „Typ bloku”, które opisuje zawartość bloku „Dane”, indeks liścia, rozkwit, metadane, indeks korzeniowy itp. Również trzy pola (rozmiar skompresowany/nieskompresowany i blok przesunięcia prev) zostały dodane, aby umożliwić szybkie wyszukiwanie wstecz i do przodu.
Kodowanie bloków danych
Ponieważ klucze są posortowane i zwykle bardzo podobne, możliwe jest zaprojektowanie lepszej kompresji niż to, co może zrobić algorytm ogólnego przeznaczenia.
HBASE-4218 próbował rozwiązać ten problem, aw HBase 0.94 możesz wybierać między kilkoma różnymi algorytmami:kodowaniem przedrostkowym i różnicowym.
Główną ideą kodowania prefiksów jest przechowywanie wspólnego prefiksu tylko raz, ponieważ wiersze są sortowane, a początek jest zwykle taki sam.
Kodowanie różnicowe posuwa tę koncepcję dalej. Zamiast uważać klucz za nieprzejrzystą sekwencję bajtów, koder różnicowy dzieli każde pole klucza, aby lepiej skompresować każdą część. Oznacza to, że rodzina kolumn jest przechowywana raz. Jeśli długość klucza, długość wartości i typ są takie same jak w poprzednim wierszu, pole jest pomijane. Ponadto, w celu zwiększenia kompresji, znacznik czasu jest przechowywany jako różnica z poprzedniego.
Pamiętaj, że ta funkcja jest domyślnie wyłączona, ponieważ zapis i skanowanie są wolniejsze, ale więcej danych jest buforowanych. Aby włączyć tę funkcję, możesz ustawić DATA_BLOCK_ENCODING =PREFIX | RÓŻNICA | FAST_DIFF w informacjach o tabeli.
File v3
HBASE-5313 zawiera propozycję restrukturyzacji układu HFile w celu poprawy kompresji:
- Spakuj wszystkie klucze razem na początku bloku i wszystkie wartości razem na końcu bloku. W ten sposób możesz użyć dwóch różnych algorytmów do kompresji klucza i wartości.
- Skompresuj znaczniki czasu za pomocą XOR z pierwszą wartością i użyj VInt zamiast long.
Ponadto badany jest format kolumnowy lub kodowanie kolumnowe, spójrz na AVRO-806, aby znaleźć kolumnowy format pliku Doug Cutting.
Jak widać, trendem ewolucji jest bycie bardziej świadomym tego, co zawiera plik, aby uzyskać lepszą kompresję lub lepszą świadomość lokalizacji, co przekłada się na mniej danych do zapisu/odczytu z dysku. Mniej I/O oznacza większą szybkość!
[1] https://clouderatemp.wpengine.com/blog/2011/01/hadoop-io-sequence-map-set-array-bloommap-files/
[2] https://clouderatemp.wpengine. com/blog/2012/06/hbase-write-path/