HBase
 sql >> Baza danych >  >> NoSQL >> HBase

Wewnątrz architektury pozyskiwania danych w czasie zbliżonym do rzeczywistego Santander (część 2)

Podziękowania dla Pedro Boado i Abel Fernandez Alfonso z zespołu inżynierów Santander za współpracę przy tym poście o tym, jak Santander UK wykorzystuje Apache HBase jako silnik obsługujący swoją innowacyjną aplikację Spendlytics działający niemal w czasie rzeczywistym.

Aplikacja Spendlytics na iOS została zaprojektowana, aby pomóc klientom Santander korzystającym z osobistych kart debetowych i kredytowych utrzymać kontrolę nad wydatkami, w tym płatnościami dokonywanymi za pośrednictwem Apple Pay. Wykorzystuje dane o transakcjach w czasie rzeczywistym, aby umożliwić klientom analizowanie wydatków na karty w różnych okresach czasu (tygodniowe, miesięczne, roczne), według kategorii (podróże, supermarkety, gotówka itp.) i według sprzedawcy.

W naszym poprzednim poście opisaliśmy, w jaki sposób Apache Flume i Apache Kafka są używane do przekształcania, wzbogacania i przesyłania strumieniowego transakcji do Apache HBase. W dalszej części tego postu opisano, w jaki sposób transakcje są organizowane w Apache HBase w celu optymalizacji wydajności, oraz w jaki sposób wykorzystujemy koprocesory, aby zapewnić agregacje trendów zakupowych dla poszczególnych klientów. Santander i Cloudera wyruszyli (i nadal są) w podróż do HBase ze Spendlytics, która przeszła wiele iteracji i optymalizacji projektowania schematów i implementacji koprocesorów. Mamy nadzieję, że wyciągnięte wnioski są kluczowymi punktami odniesienia z tego posta.

Schemat 1.0

Dobry projekt schematu HBase polega na zrozumieniu zamierzonych wzorców dostępu. Zrób to dobrze, a HBase będzie latać; źle to zrobisz i możesz skończyć z nieoptymalną wydajnością z powodu kompromisów projektowych, takich jak hotspoty regionu lub konieczność wykonywania dużych skanów w wielu regionach. (Hotspot w tabeli HBase jest to miejsce, w którym nierówna dystrybucja klawiszy rowków może spowodować, że większość żądań jest kierowana do jednego regionu, przytłaczając RegionServer i powodując powolne czasy odpowiedzi).

Co wiedzieliśmy o zamierzonych przez Spendlytics wzorcach dostępu i jak wpłynęło to na początkowy projekt schematu:

  • Klienci analizują tylko transakcje na własnych kontach:
    • Aby uzyskać szybką wydajność skanowania liniowego, wszystkie transakcje klientów powinny być przechowywane sekwencyjnie.
  • Identyfikatory klientów rosną monotonicznie:
    • Sekwencyjne identyfikatory klientów zwiększają prawdopodobieństwo, że nowi klienci będą znajdować się w tym samym regionie, co potencjalnie może stworzyć region, w którym się znajduje. Aby uniknąć tego problemu, identyfikatory klientów powinny być solone (z prefiksem) lub odwrócone w celu równomiernego rozłożenia w regionach, gdy są używane na początku klawisza wiersza.
  • Klienci mają wiele kart
    • Aby zoptymalizować skany, transakcje klienta powinny być dalej pogrupowane i posortowane według umowy o kartę, tj. identyfikator umowy powinien stanowić część klawisza wiersza.
  • Transakcje będą dostępne w całości, tj. atrybuty takie jak sprzedawca, sprzedawca, lokalizacja, waluta i kwota nie muszą być odczytywane osobno
    • Przechowywanie atrybutów transakcji w oddzielnych komórkach skutkowałoby powstaniem szerszej, rzadszej tabeli, co wydłużyłoby czas wyszukiwania. Ponieważ atrybuty będą dostępne razem, sensowne jest ich serializowanie razem w rekordzie Apache Avro. Avro jest kompaktowy i zapewnia nam wydajną reprezentację z możliwością ewolucji schematu.
  • Dostęp do transakcji można uzyskać indywidualnie, w partiach (według czasu, kategorii i sprzedawcy) oraz zbiorczo (według czasu, kategorii i sprzedawcy).
    • Dodanie unikalnego identyfikatora transakcji jako kwalifikatora kolumny umożliwi pobieranie poszczególnych transakcji bez zwiększania złożoności klucza wiersza.
    • Aby umożliwić szybkie skanowanie transakcji w różnych okresach czasu, znacznik czasu transakcji powinien stanowić część klucza wiersza.
    • Dodanie kategorii i sprzedawcy do klucza wiersza może być zbyt szczegółowe i skutkowałoby bardzo wysoką i wąską tabelą ze złożonym kluczem wiersza. Wysokie i wąskie są w porządku, biorąc pod uwagę, że niepodzielność nie jest problemem, ale posiadanie ich jako kwalifikatorów kolumn poszerzyłoby tabelę, jednocześnie nadal obsługując agregacje wtórne.
  • Dane trendów powinny być obliczone wstępnie w jak największym stopniu, aby zoptymalizować wydajność odczytu.
    • Więcej o tym później, ale na razie wiedz, że dodaliśmy drugą rodzinę kolumn do przechowywania trendów.

    W oparciu o powyższe, wstępny projekt schematu jest zilustrowany w następujący sposób:

    Trendy komputerowe

    Aspektem pierwotnego projektu, z którego nauczyliśmy się najwięcej, były trendy komputerowe. Wymogiem było umożliwienie klientom analizy ich wydatków według kategorii i detalisty z dokładnością do godziny. Punkty danych obejmowały najmniejszą i największą wartość transakcji, całkowitą wartość transakcji oraz liczbę transakcji. Czasy odpowiedzi musiały wynosić 200 ms lub mniej.

    Trendy w zakresie obliczeń wstępnych zapewniłyby nam najszybsze czasy odpowiedzi, więc było to nasze pierwsze podejście. Trendy nie mogły opóźniać transakcji, więc musiały być obliczane na ścieżce zapisu. Byłoby to świetne dla wydajności odczytu, ale postawiło nas przed kilkoma wyzwaniami:jak najlepiej zorganizować trendy w HBase oraz jak je szybko i niezawodnie obliczać bez poważnego wpływu na wydajność zapisu.

    Eksperymentowaliśmy z różnymi projektami schematów i staraliśmy się wykorzystać niektóre dobrze znane projekty tam, gdzie to możliwe (takie jak schemat OpenTSDB). Po kilku iteracjach zdecydowaliśmy się na projekt schematu zilustrowany powyżej. Przechowywane w tabeli transakcji, w oddzielnej rodzinie kolumn, wartości trendów są zorganizowane razem w jednym wierszu, z jednym wierszem trendu na klienta. Nadając kluczowi wiersza ten sam prefiks, co transakcje klienta (na przykład <reverse_customer_id>::<contract_id> ) zapewniło to, że wiersz trendu zostanie posortowany obok odpowiednich rekordów transakcji klienta. Dzięki zdefiniowanym granicom regionów i niestandardowym zasadom podziału regionów możemy również zagwarantować, że wiersz trendu będzie zawsze łączony z rekordami transakcji klienta, umożliwiając agregację trendów, aby pozostać całkowicie po stronie serwera w koprocesorze.

    Aby wstępnie obliczyć trendy, wdrożyliśmy niestandardowy koprocesor obserwatora podpiąć się pod ścieżkę zapisu. (Koprocesory obserwatora są podobne do wyzwalaczy w RDBMS, ponieważ wykonują kod użytkownika przed lub po wystąpieniu określonego zdarzenia. Na przykład przed lub po Put lub Get .)

    W postPut koprocesor wykonuje następujące czynności:

    1. Sprawdza Put dla atrybutu trendu (flagi). Atrybut jest ustawiany tylko dla nowych rekordów transakcji, aby uniknąć rekurencyjnych wywołań podczas aktualizowania rekordu trendu. Pozwala także na pominięcie koprocesora dla Put s, które nie wymagają aktualizacji trendów (np. rozliczenia ).
    2. Uzyskaj rekord trendu dla klienta. Rekord trendu klienta jest kolokowany z jego transakcjami (na podstawie prefiksu klucza wiersza), dzięki czemu koprocesor może pobrać go bezpośrednio z bieżącego regionu. Wiersz trendu musi być zablokowany, aby wiele wątków obsługi RegionServer nie próbowało równolegle aktualizować trendów.
    3. Aktualizuj punkty danych:
    4. Zaktualizuj i odblokuj wiersz trendów.

    Rozwiązanie okazało się dokładne podczas testów i zgodnie z oczekiwaniami wydajność odczytu przekroczyła wymagania. Jednak pojawiły się pewne obawy związane z tym podejściem. Pierwszy dotyczył sposobu postępowania z awariami:trendy są przechowywane w oddzielnym wierszu, więc nie można zagwarantować niepodzielności. Druga dotyczyła sprawdzania poprawności trendów w czasie; oznacza to, że musielibyśmy wdrożyć mechanizm identyfikowania i korygowania wszelkich nieścisłości w trendach. Gdy rozważyliśmy również wymagania HA i fakt, że musielibyśmy uruchomić dwie, aktywne-aktywne instancje HBase w różnych centrach danych, może to być większy problem. Z czasem dokładność trendu może się nie tylko zmniejszać, ale również te dwa klastry mogą dryfować i muszą zostać uzgodnione w zależności od metody, którą zastosowaliśmy do ich synchronizacji. Wreszcie, naprawienie błędów lub dodanie nowych punktów danych byłoby trudne, ponieważ prawdopodobnie musielibyśmy cofnąć się i ponownie obliczyć wszystkie trendy.

    Potem była wydajność zapisu. Dla każdej nowej transakcji obserwator musiał pobrać rekord trendu, zaktualizować 32 punkty danych i umieścić rekord trendu z powrotem. Pomimo tego, że wszystko to dzieje się w granicach jednego regionu, stwierdziliśmy, że przepustowość została zmniejszona z ponad 20 000 zapisów na sekundę do 1000 zapisów na sekundę (na RegionServer). Ta wydajność była akceptowalna w krótkim okresie, ale nie byłaby skalowana w celu obsługi przewidywanego obciążenia długoterminowego.

    Wiedzieliśmy, że wydajność zapisu stanowi ryzyko, więc mieliśmy plan tworzenia kopii zapasowych, a był to koprocesor punktu końcowego . Koprocesory punktów końcowych są podobne do procedur przechowywanych w RDBMS, ponieważ umożliwiają wykonywanie obliczeń po stronie serwera — na serwerze RegionServer, gdzie znajdują się dane, a nie na kliencie. Punkty końcowe skutecznie rozszerzają interfejs API HBase.

    Zamiast wstępnie obliczać trendy, punkt końcowy oblicza je w locie, po stronie serwera. W rezultacie mogliśmy usunąć rodzinę kolumn trendów ze schematu i szło z tym ryzyko nieścisłości i rozbieżności. Odejście od obserwatora zaowocowało dobrą wydajnością zapisu, ale czy odczyty byłyby wystarczająco szybkie? Krótko mówiąc, tak. Ponieważ transakcje klienta są ograniczone do jednego regionu i posortowane według karty i znacznika czasu, punkt końcowy może szybko skanować i agregować, dobrze w ramach celu Spendlytics 200 ms. Oznacza to również, że żądanie klienta (w tym przypadku z interfejsu API Spendlytics) jest zawsze kierowane tylko do jednej instancji Endpoint (pojedynczego serwera RegionServer), a klient otrzyma z powrotem pojedynczą odpowiedź z pełnym wynikiem — to znaczy bez po stronie klienta przetwarzanie jest wymagane do agregowania częściowych wyników z wielu punktów końcowych, co miałoby miejsce, gdyby transakcje klienta obejmowały wiele regionów.

    Wyciągnięte wnioski

    Spendlytics działa od lipca 2015 roku. Od tego czasu dokładnie monitorowaliśmy wzorce dostępu i szukaliśmy sposobów na optymalizację wydajności. Chcemy stale poprawiać wygodę użytkowników i zapewniać klientom coraz lepszy wgląd w wydatki na karty. Pozostała część tego postu opisuje wnioski, których wyciągnęliśmy z uruchamiania Spendlytics w środowisku produkcyjnym, oraz niektóre z wprowadzonych optymalizacji.

    Po pierwszym wydaniu zidentyfikowaliśmy kilka problemów, na których poprawie chcieliśmy się skupić. Pierwszym był sposób filtrowania wyników według atrybutu transakcji. Jak wspomniano wcześniej, atrybuty transakcji są zakodowane w rekordach Avro, ale stwierdziliśmy, że coraz większa liczba wzorców dostępu chciała filtrować według atrybutów, a użytkownicy byli zmuszani do robienia tego po stronie klienta. Początkowym rozwiązaniem było zaimplementowanie niestandardowego ValueFilter HBase które zaakceptowały nasze własne złożone wyrażenia filtrujące, na przykład:

    category='SUPERMARKETS' AND amount > 100 AND 
    (brand LIKE 'foo%' OR brand = 'bar')

    Wyrażenie jest oceniane dla każdego rekordu Avro, co pozwala nam filtrować wyniki po stronie serwera i zmniejszać ilość danych zwracanych do klienta (oszczędzając przepustowość sieci i przetwarzanie po stronie klienta). Filtr ma wpływ na wydajność skanowania, ale czasy odpowiedzi pozostały w granicach celu 200 ms.

    Okazało się to rozwiązaniem tymczasowym ze względu na dalsze zmiany, które były wymagane do optymalizacji zapisów. Ze względu na sposób, w jaki działa proces rozliczania kart kredytowych, najpierw otrzymujemy autoryzowane transakcja od momentu sprzedaży (w czasie zbliżonym do rzeczywistego), a potem jakiś czas później rozliczona transakcja z sieci kart kredytowych (w partiach). Transakcje te muszą zostać uzgodnione, zasadniczo poprzez połączenie rozliczonych transakcje z autoryzowanymi transakcje już w HBase, dołączanie na ID transakcji. W ramach tego procesu atrybuty transakcji mogą ulec zmianie i mogą zostać dodane nowe atrybuty. Okazało się to bolesne ze względu na konieczność przepisywania całych rekordów Avro — nawet podczas aktualizacji pojedynczych atrybutów. Aby atrybuty były bardziej dostępne dla aktualizacji, zorganizowaliśmy je w kolumny, zastępując serializację Avro.

    Dbamy również tylko o niepodzielność na poziomie transakcji, więc grupowanie transakcji według godziny nie dało nam żadnej przewagi. Co więcej, ustabilizowali się transakcje, które teraz docierają w partiach, mają tylko szczegółowość na poziomie dnia, co utrudnia (kosztowne) uzgodnienie ich z istniejącymi autoryzowanymi transakcje przechowywane przez godzinę. Aby rozwiązać ten problem, przenieśliśmy identyfikator transakcji do klucza wiersza i zmniejszyliśmy ziarno sygnatury czasowej do dni, a nie godzin. Proces uzgadniania jest teraz znacznie łatwiejszy, ponieważ możemy po prostu zbiorczo załadować zmiany do HBase i pozwolić na rozliczenie wartości mają pierwszeństwo.

    Podsumowując:

    • Koprocesory obserwatorów mogą być cennym narzędziem, ale używaj ich mądrze.
    • W niektórych przypadkach użycia rozszerzenie interfejsu API HBase za pomocą punktów końcowych jest dobrą alternatywą.
    • Użyj niestandardowych filtrów, aby poprawić wydajność, przycinając wyniki po stronie serwera.
    • Serializowane wartości mają sens we właściwym przypadku użycia, ale wykorzystują mocne strony HBase, faworyzując natywną obsługę pól i kolumn.
    • Zarządzanie wstępnie obliczonymi wynikami jest trudne; dodatkowe opóźnienie związane z przetwarzaniem w locie może być warte zachodu.
    • Wzorce dostępu będą się zmieniać, więc bądź zwinny i otwarty na wprowadzanie zmian w schemacie HBase, aby dostosować się i wyprzedzić grę.

    Mapa drogowa

    Optymalizacją, którą obecnie oceniamy, są koprocesory hybrydowe. Rozumiemy przez to połączenie zarówno obserwatora, jak i koprocesora punktu końcowego, aby wstępnie obliczyć trendy. Jednak w przeciwieństwie do poprzednich nie zrobilibyśmy tego na ścieżce zapisu, ale w tle, podłączając się do operacji opróżniania i kompaktowania HBase. Obserwator obliczy trendy podczas zdarzeń spłukiwania i zagęszczania na podstawie ustalonych transakcje dostępne w tym momencie. Następnie użyjemy punktu końcowego, aby połączyć wstępnie obliczone trendy z agregacjami delty transakcji w locie. Dzięki wstępnemu obliczaniu trendów w ten sposób mamy nadzieję zwiększyć wydajność odczytów bez wpływu na wydajność zapisu.

    Innym podejściem, które oceniamy pod kątem agregacji trendów i ogólnie dostępu do HBase, jest Apache Phoenix. Phoenix to skórka SQL dla HBase, która umożliwia dostęp za pomocą standardowych interfejsów API JDBC. Mamy nadzieję, że użycie SQL i JDBC uprości dostęp do HBase i zmniejszy ilość kodu, który musimy napisać. Możemy również wykorzystać inteligentne wzorce wykonywania Phoenix oraz wbudowane koprocesory i filtry do szybkiej agregacji. Phoenix był uważany za zbyt niedojrzały do ​​użytku produkcyjnego na początku Spendlytics, ale podobne przypadki użycia zostały zgłoszone przez takie firmy jak eBay i Salesforce, teraz nadszedł czas na ponowną ocenę. (Pakiet Phoenix dla CDH jest dostępny do instalacji i oceny, ale bez wsparcia, za pośrednictwem Cloudera Labs.)

    Santander ogłosił niedawno, że jest pierwszym bankiem, który uruchomił technologię bankowości głosowej, która umożliwia klientom rozmawianie z aplikacją SmartBank i pytanie o wydatki na karty. Platformą stojącą za tą technologią jest Cloudera, a architektura Spendlytics — opisana w tym zestawie postów — służyła jako projekt planu.

    James Kinley jest głównym architektem rozwiązań w Cloudera.

    Ian Buss jest starszym architektem rozwiązań w Cloudera.

    Pedro Boado jest inżynierem Hadoop w Santander (Isban) w Wielkiej Brytanii.

    Abel Fernandez Alfonso jest inżynierem Hadoop w Santander (Isban) w Wielkiej Brytanii.


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Wewnątrz architektury pozyskiwania danych w czasie zbliżonym do rzeczywistego Santander (część 2)

  2. Wprowadzanie obsługi transakcji do Operacyjnej Bazy Danych Cloudera

  3. Kodowanie wymazywania HDFS w Big Data Hadoop

  4. Konwertowanie list ACL HBase na zasady Ranger

  5. Co to jest para klucz-wartość MapReduce w usłudze Hadoop?