Udostępnij za pośrednictwem


Najlepsze rozwiązania w zakresie wydajności dla rozwiązania Apache Phoenix

Najważniejszym aspektem wydajności usługi Apache Phoenix jest zoptymalizowanie bazowej bazy danych Apache HBase. Phoenix tworzy model danych relacyjnych na szczycie bazy danych HBase, który konwertuje zapytania SQL na operacje HBase, takie jak skanowania. Projekt schematu tabeli, wybór i kolejność pól w kluczu podstawowym oraz użycie wszystkich indeksów wpływa na wydajność rozwiązania Phoenix.

Projekt schematu tabeli

Podczas tworzenia tabeli w phoenix ta tabela jest przechowywana w tabeli HBase. Tabela HBase zawiera grupy kolumn (rodzin kolumn), do których uzyskuje się dostęp. Wiersz w tabeli Phoenix jest wierszem w tabeli HBase, w którym każdy wiersz składa się z wersji komórek skojarzonych z co najmniej jedną kolumną. Logicznie pojedynczy wiersz bazy danych HBase jest kolekcją par klucz-wartość, z których każda ma tę samą wartość klucza wiersza. Oznacza to, że każda para klucz-wartość ma atrybut rowkey, a wartość tego atrybutu rowkey jest taka sama dla określonego wiersza.

Projekt schematu tabeli Phoenix obejmuje projekt klucza podstawowego, projekt rodziny kolumn, projekt poszczególnych kolumn i sposób partycjonowania danych.

Projekt klucza podstawowego

Klucz podstawowy zdefiniowany w tabeli w phoenix określa sposób przechowywania danych w wierszu bazowej tabeli HBase. W bazie danych HBase jedynym sposobem uzyskania dostępu do określonego wiersza jest użycie klucza wiersza. Ponadto dane przechowywane w tabeli HBase są sortowane według klucza wiersza. Phoenix tworzy wartość klucza wiersza, łącząc wartości każdej kolumny w wierszu w kolejności, w której są zdefiniowane w kluczu podstawowym.

Na przykład tabela kontaktów zawiera imię, nazwisko, numer telefonu i adres w tej samej rodzinie kolumn. Klucz podstawowy można zdefiniować na podstawie rosnącej liczby sekwencji:

rowkey adres phone firstName lastName
1000 1111 San Gabriel Dr. 1-425-000-0002 Michał Dole
8396 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji

Jednak jeśli często wykonujesz zapytania według lastName, ten klucz podstawowy może nie działać prawidłowo, ponieważ każde zapytanie wymaga pełnego skanowania tabeli w celu odczytania wartości każdej lastName. Zamiast tego można zdefiniować klucz podstawowy w kolumnach lastName, firstName i social security number. Ta ostatnia kolumna polega na uściślaniu dwóch mieszkańców pod tym samym adresem o tej samej nazwie, takiej jak ojciec i syn.

rowkey adres phone firstName lastName socialSecurityNum
1000 1111 San Gabriel Dr. 1-425-000-0002 Michał Dole 111
8396 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji 222

W przypadku tego nowego klucza podstawowego klucze wierszy generowane przez firmę Phoenix będą następujące:

rowkey adres phone firstName lastName socialSecurityNum
Dole-John-111 1111 San Gabriel Dr. 1-425-000-0002 Michał Dole 111
Raji-Calvin-222 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji 222

W pierwszym wierszu danej tabeli dane dla klucza wiersza są reprezentowane w sposób pokazany:

rowkey key wartość
Dole-John-111 adres 1111 San Gabriel Dr.
Dole-John-111 phone 1-425-000-0002
Dole-John-111 firstName Michał
Dole-John-111 lastName Dole
Dole-John-111 socialSecurityNum 111

Ten klucz wiersza przechowuje teraz zduplikowaną kopię danych. Rozważ rozmiar i liczbę kolumn uwzględnionych w kluczu podstawowym, ponieważ ta wartość jest dołączona do każdej komórki w bazowej tabeli HBase.

Ponadto jeśli klucz podstawowy zawiera wartości monotonicznie rosnące, należy utworzyć tabelę z zasobnikami soli, aby uniknąć tworzenia hotspotów zapisu — zobacz Partycjonowanie danych.

Projekt rodziny kolumn

Jeśli niektóre kolumny są używane częściej niż inne, należy utworzyć wiele rodzin kolumn, aby oddzielić często używane kolumny od rzadko używanych kolumn.

Ponadto, jeśli niektóre kolumny są zwykle dostępne razem, umieść te kolumny w tej samej rodzinie kolumn.

Projekt kolumn

  • Zachowaj kolumny VARCHAR poniżej około 1 MB ze względu na koszty operacji we/wy dużych kolumn. Podczas przetwarzania zapytań baza HBase materializuje komórki w całości przed wysłaniem ich do klienta, a klient otrzyma je w całości przed przekazaniem ich do kodu aplikacji.
  • Przechowuj wartości kolumn przy użyciu formatu kompaktowego, takiego jak protobuf, Avro, msgpack lub BSON. Kod JSON nie jest zalecany, ponieważ jest większy.
  • Rozważ kompresowanie danych przed magazynem, aby zmniejszyć opóźnienia i koszty operacji we/wy.

Partycjonowanie danych

Phoenix umożliwia kontrolowanie liczby regionów, w których dane są dystrybuowane, co może znacznie zwiększyć wydajność odczytu/zapisu. Podczas tworzenia tabeli Phoenix można utworzyć sól lub wstępnie podzielić dane.

Aby zasiąć stół podczas tworzenia, określ liczbę zasobników soli:

CREATE TABLE CONTACTS (...) SALT_BUCKETS = 16

To łączenie dzieli tabelę na wartości kluczy podstawowych, automatycznie wybierając wartości.

Aby kontrolować, gdzie występują podziały tabeli, możesz wstępnie podzielić tabelę, podając wartości zakresu, wzdłuż których występuje dzielenie. Aby na przykład utworzyć tabelę podzieloną wzdłuż trzech regionów:

CREATE TABLE CONTACTS (...) SPLIT ON ('CS','EU','NA')

Projekt indeksu

Indeks Phoenix to tabela HBase, która przechowuje kopię niektórych lub wszystkich danych z indeksowanej tabeli. Indeks poprawia wydajność określonych typów zapytań.

Po zdefiniowaniu wielu indeksów, a następnie zapytaniu o tabelę, phoenix automatycznie wybiera najlepszy indeks dla zapytania. Indeks podstawowy jest tworzony automatycznie na podstawie wybranych kluczy podstawowych.

W przypadku przewidywanych zapytań można również tworzyć indeksy pomocnicze, określając ich kolumny.

Podczas projektowania indeksów:

  • Utwórz tylko potrzebne indeksy.
  • Ogranicz liczbę indeksów dla często aktualizowanych tabel. Aktualizacje tabeli są tłumaczone na zapisy zarówno w tabeli głównej, jak i w tabelach indeksów.

Tworzenie indeksów pomocniczych

Indeksy pomocnicze mogą zwiększyć wydajność odczytu, zamieniając to, co byłoby pełnym skanowaniem tabeli w wyszukiwanie punktów, kosztem miejsca do magazynowania i szybkości zapisu. Indeksy pomocnicze można dodawać lub usuwać po utworzeniu tabeli i nie wymagają zmian w istniejących zapytaniach — zapytania po prostu działają szybciej. W zależności od potrzeb rozważ utworzenie omówionych indeksów, indeksów funkcjonalnych lub obu tych elementów.

Korzystanie z indeksów objętych

Objęte indeksy to indeksy, które zawierają dane z wiersza oprócz indeksowanych wartości. Po znalezieniu żądanego wpisu indeksu nie ma potrzeby uzyskiwania dostępu do tabeli podstawowej.

Na przykład w przykładowej tabeli kontaktów można utworzyć indeks pomocniczy tylko w kolumnie socialSecurityNum. Ten indeks pomocniczy przyspieszy zapytania filtrujące według wartości socialSecurityNum, ale pobieranie innych wartości pól wymaga innego odczytu względem tabeli głównej.

rowkey adres phone firstName lastName socialSecurityNum
Dole-John-111 1111 San Gabriel Dr. 1-425-000-0002 Michał Dole 111
Raji-Calvin-222 5415 San Gabriel Dr. 1-230-555-0191 Calvin Raji 222

Jeśli jednak zazwyczaj chcesz wyszukać imię i nazwisko, biorąc pod uwagę wartość socialSecurityNum, możesz utworzyć indeks objęty nazwą firstName i lastName jako rzeczywiste dane w tabeli indeksów:

CREATE INDEX ssn_idx ON CONTACTS (socialSecurityNum) INCLUDE(firstName, lastName);

Ten indeks obejmuje następujące zapytanie, aby uzyskać wszystkie dane tylko przez odczyt z tabeli zawierającej indeks pomocniczy:

SELECT socialSecurityNum, firstName, lastName FROM CONTACTS WHERE socialSecurityNum > 100;

Korzystanie z indeksów funkcjonalnych

Indeksy funkcjonalne umożliwiają utworzenie indeksu w dowolnym wyrażeniu, które ma być używane w zapytaniach. Po utworzeniu indeksu funkcjonalnego i użyciu tego wyrażenia zapytanie może służyć do pobierania wyników, a nie tabeli danych.

Możesz na przykład utworzyć indeks, aby umożliwić wyszukiwanie bez uwzględniania wielkości liter w połączonym imieniu i nazwisku osoby:

CREATE INDEX FULLNAME_UPPER_IDX ON "Contacts" (UPPER("firstName"||' '||"lastName"));

Projekt zapytania

Główne zagadnienia dotyczące projektowania zapytań to:

  • Zapoznaj się z planem zapytania i zweryfikuj oczekiwane zachowanie.
  • Wydajnie przyłączaj.

Omówienie planu zapytania

W usłudze SQLLine użyj polecenia EXPLAIN, a następnie zapytania SQL, aby wyświetlić plan operacji wykonywanych przez phoenix. Sprawdź, czy plan:

  • W razie potrzeby używa klucza podstawowego.
  • Używa odpowiednich indeksów pomocniczych, a nie tabeli danych.
  • Używa SKANOWANIA ZAKRESU lub POMIJANIA SKANOWANIA, jeśli jest to możliwe, a nie SKANOWANIA TABELI.

Przykłady planowania

Załóżmy na przykład, że masz tabelę o nazwie FLIGHTS, która przechowuje informacje o opóźnieniu lotu.

Aby wybrać wszystkie loty z airlineid 19805wartością , gdzie airlineid jest polem, które nie znajduje się w kluczu podstawowym lub w żadnym indeksie:

select * from "FLIGHTS" where airlineid = '19805';

Uruchom objaśnione polecenie w następujący sposób:

explain select * from "FLIGHTS" where airlineid = '19805';

Plan zapytania wygląda następująco:

CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN FULL SCAN OVER FLIGHTS
   SERVER FILTER BY AIRLINEID = '19805'

W tym planie zanotuj frazę FULL SCAN OVER FLIGHTS (PEŁNE SKANOWANIE W RAMACH LOTÓW). Ta fraza wskazuje, że wykonanie wykonuje skanowanie TABELI we wszystkich wierszach w tabeli, zamiast korzystać z bardziej wydajnego skanowania ZAKRESU lub POMIJANIA SKANOWANIA.

Teraz załóżmy, że chcesz wykonać zapytanie o loty 2 stycznia 2014 r. dla przewoźnika AA , gdzie jego numer lotu był większy niż 1. Załóżmy, że kolumny year, month, dayofmonth, carrier i flightnum istnieją w przykładowej tabeli i są częścią złożonego klucza podstawowego. Zapytanie wygląda następująco:

select * from "FLIGHTS" where year = 2014 and month = 1 and dayofmonth = 2 and carrier = 'AA' and flightnum > 1;

Przyjrzyjmy się planowi dla tego zapytania:

explain select * from "FLIGHTS" where year = 2014 and month = 1 and dayofmonth = 2 and carrier = 'AA' and flightnum > 1;

Wynikowy plan to:

CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER FLIGHTS [2014,1,2,'AA',2] - [2014,1,2,'AA',*]

Wartości w nawiasach kwadratowych są zakresem wartości dla kluczy podstawowych. W tym przypadku wartości zakresu są stałe z rokiem 2014, miesiącem 1 i miesiącem 2, ale zezwalają na wartości flightnum rozpoczynające się od 2 i w górę (*). Ten plan zapytania potwierdza, że klucz podstawowy jest używany zgodnie z oczekiwaniami.

Następnie utwórz indeks w tabeli FLIGHTS o nazwie carrier2_idx , która znajduje się tylko w polu przewoźnika. Ten indeks zawiera flightdaterównież kolumny , tailnum, origini flightnum , których dane są również przechowywane w indeksie.

CREATE INDEX carrier2_idx ON FLIGHTS (carrier) INCLUDE(FLIGHTDATE,TAILNUM,ORIGIN,FLIGHTNUM);

Załóżmy, że chcesz uzyskać operatora wraz z elementami flightdate i tailnum, jak w następującym zapytaniu:

explain select carrier,flightdate,tailnum from "FLIGHTS" where carrier = 'AA';

Powinien zostać wyświetlony ten indeks, który jest używany:

CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER CARRIER2_IDX ['AA']

Aby uzyskać pełną listę elementów, które mogą być wyświetlane w wynikach planu wyjaśnień, zobacz sekcję Wyjaśnij plany w przewodniku dostrajania apache Phoenix.

Wydajne łączenie

Ogólnie rzecz biorąc, należy unikać sprzężeń, chyba że jedna strona jest mała, zwłaszcza w przypadku częstych zapytań.

W razie potrzeby można wykonać duże sprzężenia z /*+ USE_SORT_MERGE_JOIN */ wskazówką, ale duże sprzężenia to kosztowna operacja w przypadku ogromnej liczby wierszy. Jeśli całkowity rozmiar wszystkich tabel po prawej stronie przekroczy dostępną pamięć, użyj /*+ NO_STAR_JOIN */ wskazówki.

Scenariusze

W poniższych wytycznych opisano niektóre typowe wzorce.

Obciążenia z dużym obciążeniem odczytu

W przypadku przypadków użycia z dużą liczbą operacji odczytu upewnij się, że używasz indeksów. Ponadto, aby zaoszczędzić obciążenie związane z czasem odczytu, rozważ utworzenie omówionych indeksów.

Obciążenia z dużym obciążeniem zapisu

W przypadku obciążeń z dużym obciążeniem zapisu, w których klucz podstawowy jest monotonicznie zwiększany, utwórz zasobniki soli, aby uniknąć hotspotów zapisu, kosztem ogólnej przepływności odczytu ze względu na wymagane dodatkowe skanowania. Ponadto w przypadku zapisywania dużej liczby rekordów przy użyciu operacji UPSERT wyłącz automatyczne zatwierdzanie i wsaduj rekordy.

Zbiorcze usuwanie

Podczas usuwania dużego zestawu danych włącz autoPolecenia przed wysłaniem zapytania DELETE, aby klient nie musiał pamiętać kluczy wierszy dla wszystkich usuniętych wierszy. Funkcja AutoCommit uniemożliwia klientowi buforowanie wierszy, których dotyczy operacja DELETE, dzięki czemu phoenix może usunąć je bezpośrednio na serwerach regionów bez konieczności zwracania ich do klienta.

Niezmienne i tylko dołączanie

Jeśli twój scenariusz preferuje szybkość zapisu w ramach integralności danych, rozważ wyłączenie dziennika z wyprzedzeniem zapisu podczas tworzenia tabel:

CREATE TABLE CONTACTS (...) DISABLE_WAL=true;

Aby uzyskać szczegółowe informacje na temat tych i innych opcji, zobacz Apache Phoenix Grammar.

Następne kroki