Osvědčené postupy pro Apache Phoenix z hlediska výkonu
Nejdůležitějším aspektem výkonu Apache Phoenixu je optimalizace základního Apache HBase. Phoenix vytvoří relační datový model atop HBase, který převádí dotazy SQL na operace HBase, jako jsou kontroly. Návrh schématu tabulky, výběr a řazení polí v primárním klíči a použití indexů ovlivní výkon Phoenixu.
Návrh schématu tabulky
Při vytváření tabulky v Phoenixu je tato tabulka uložena v tabulce HBase. Tabulka HBase obsahuje skupiny sloupců (rodin sloupců), ke kterým se přistupuje společně. Řádek v tabulce Phoenix je řádek v tabulce HBase, kde se každý řádek skládá z buněk s verzí přidružených k jednomu nebo více sloupcům. Jeden řádek HBase je logicky kolekce párů klíč-hodnota, z nichž každá má stejnou hodnotu klíče řádku. To znamená, že každý pár klíč-hodnota má atribut rowkey a hodnota tohoto atributu rowkey je stejná pro konkrétní řádek.
Návrh schématu tabulky Phoenix zahrnuje návrh primárního klíče, návrh rodiny sloupců, návrh jednotlivých sloupců a způsob rozdělení dat.
Návrh primárního klíče
Primární klíč definovaný v tabulce v Phoenixu určuje, jak se data ukládají v klíči řádku podkladové tabulky HBase. Jediným způsobem přístupu k určitému řádku v HBase je klíč řádku. Kromě toho se data uložená v tabulce HBase seřadí podle klíče řádku. Phoenix vytvoří hodnotu rowkey tím, že zřetězením hodnot jednotlivých sloupců v řádku v pořadí, v jakém jsou definovány v primárním klíči.
Tabulka kontaktů má například křestní jméno, příjmení, telefonní číslo a adresu ve stejné rodině sloupců. Primární klíč můžete definovat na základě rostoucího pořadového čísla:
rowkey | adresa | telefon | firstName | lastName |
---|---|---|---|---|
1000 | 1111 San Gabriel Dr. | 1-425-000-0002 | John | Dole |
8396 | 5415 San Gabriel Dr. | 1-230-555-0191 | Calvin | Raji |
Pokud se však často dotazujete podle lastName, nemusí tento primární klíč dobře fungovat, protože každý dotaz vyžaduje úplnou kontrolu tabulky ke čtení hodnoty každého lastName. Místo toho můžete definovat primární klíč ve sloupcích lastName, firstName a social security number. Tento poslední sloupec spočívá v nejednoznačnosti dvou obyvatel na stejné adrese se stejným jménem, jako je otec a syn.
rowkey | adresa | telefon | firstName | lastName | socialSecurityNum |
---|---|---|---|---|---|
1000 | 1111 San Gabriel Dr. | 1-425-000-0002 | John | Dole | 111 |
8396 | 5415 San Gabriel Dr. | 1-230-555-0191 | Calvin | Raji | 222 |
S tímto novým primárním klíčem budou klíče řádků vygenerované Phoenixem:
rowkey | adresa | telefon | firstName | lastName | socialSecurityNum |
---|---|---|---|---|---|
Dole-John-111 | 1111 San Gabriel Dr. | 1-425-000-0002 | John | Dole | 111 |
Raji-Calvin-222 | 5415 San Gabriel Dr. | 1-230-555-0191 | Calvin | Raji | 222 |
V prvním řádku dané tabulky jsou data pro klíč řádku reprezentována takto:
rowkey | key | hodnota |
---|---|---|
Dole-John-111 | adresa | 1111 San Gabriel Dr. |
Dole-John-111 | telefon | 1-425-000-0002 |
Dole-John-111 | firstName | John |
Dole-John-111 | lastName | Dole |
Dole-John-111 | socialSecurityNum | 111 |
Tento klíč řádku teď ukládá duplicitní kopii dat. Vezměte v úvahu velikost a počet sloupců, které zahrnete do primárního klíče, protože tato hodnota je zahrnuta do každé buňky v podkladové tabulce HBase.
Pokud má primární klíč také hodnoty, které se monotonicky zvyšují, měli byste vytvořit tabulku se solnými kbelíky , abyste se vyhnuli vytváření hotspotů zápisu – viz data oddílu.
Návrh rodiny sloupců
Pokud se některé sloupce používají častěji než jiné, měli byste vytvořit více rodin sloupců, abyste rozdělili často používané sloupce od zřídka používaných sloupců.
Pokud mají určité sloupce tendenci být přístupné dohromady, vložte tyto sloupce do stejné rodiny sloupců.
Návrh sloupce
- Udržujte sloupce VARCHAR pod 1 MB kvůli nákladům na vstupně-výstupní operace velkých sloupců. Při zpracování dotazů HBase materializuje buňky v plném rozsahu před jejich odesláním klientovi a klient je obdrží v plném rozsahu před předáním kódu aplikace.
- Hodnoty sloupců můžete ukládat pomocí kompaktního formátu, jako je protobuf, Avro, msgpack nebo BSON. Json se nedoporučuje, protože je větší.
- Zvažte kompresi dat před úložištěm, abyste snížili latenci a vstupně-výstupní náklady.
Dělení dat
Phoenix umožňuje řídit počet oblastí, ve kterých se data distribuují, což může výrazně zvýšit výkon čtení a zápisu. Při vytváření phoenixové tabulky můžete data rozdělit buď solí, nebo předem rozdělit.
Chcete-li při vytváření tabulky solit, zadejte počet solných kbelíků:
CREATE TABLE CONTACTS (...) SALT_BUCKETS = 16
Toto solení rozdělí tabulku podle hodnot primárních klíčů a automaticky zvolí hodnoty.
Pokud chcete určit, kde dochází k rozdělení tabulky, můžete tabulku předem rozdělit zadáním hodnot rozsahu, ve kterých dochází k rozdělení. Pokud například chcete vytvořit tabulku rozdělenou mezi tři oblasti:
CREATE TABLE CONTACTS (...) SPLIT ON ('CS','EU','NA')
Návrh indexu
Phoenix index je tabulka HBase, která ukládá kopii některých nebo všech dat z indexované tabulky. Index zlepšuje výkon pro konkrétní typy dotazů.
Pokud máte definovaných více indexů a potom dotazujete tabulku, Phoenix automaticky vybere nejlepší index dotazu. Primární index se vytvoří automaticky na základě vybraných primárních klíčů.
U očekávaných dotazů můžete také vytvořit sekundární indexy zadáním jejich sloupců.
Při navrhování indexů:
- Vytvořte jenom indexy, které potřebujete.
- Omezte počet indexů u často aktualizovaných tabulek. Aktualizace do tabulky se přeloží na zápisy do hlavní i indexové tabulky.
Vytvoření sekundárních indexů
Sekundární indexy můžou zlepšit výkon čtení tím, že změní, co by bylo úplné prohledávání tabulky na bodové vyhledávání, a to za cenu úložného prostoru a rychlosti zápisu. Sekundární indexy je možné přidat nebo odebrat po vytvoření tabulky a nevyžadují změny stávajících dotazů – dotazy se spustí rychleji. V závislosti na vašich potřebách zvažte vytvoření pokrytých indexů, funkčních indexů nebo obojího.
Použití pokrytých indexů
Zahrnuté indexy jsou indexy, které kromě indexovaných hodnot obsahují data z řádku. Po nalezení požadované položky indexu není nutné přistupovat k primární tabulce.
Například v ukázkové tabulce kontaktů můžete vytvořit sekundární index pouze ve sloupci socialSecurityNum. Tento sekundární index by urychlil dotazy, které filtrují hodnoty socialSecurityNum, ale načtení jiných hodnot polí vyžaduje další čtení proti hlavní tabulce.
rowkey | adresa | telefon | firstName | lastName | socialSecurityNum |
---|---|---|---|---|---|
Dole-John-111 | 1111 San Gabriel Dr. | 1-425-000-0002 | John | Dole | 111 |
Raji-Calvin-222 | 5415 San Gabriel Dr. | 1-230-555-0191 | Calvin | Raji | 222 |
Pokud ale obvykle chcete vyhledat jméno a příjmení s názvem socialSecurityNum, můžete vytvořit pokrytý index, který obsahuje jméno a příjmení jako skutečná data v tabulce indexu:
CREATE INDEX ssn_idx ON CONTACTS (socialSecurityNum) INCLUDE(firstName, lastName);
Tento pokrytý index umožňuje následujícímu dotazu získat všechna data pouze čtením z tabulky obsahující sekundární index:
SELECT socialSecurityNum, firstName, lastName FROM CONTACTS WHERE socialSecurityNum > 100;
Použití funkčních indexů
Funkční indexy umožňují vytvořit index libovolného výrazu, který očekáváte v dotazech. Jakmile použijete funkční index a dotaz tento výraz použije, může být index použit k načtení výsledků místo tabulky dat.
Můžete například vytvořit index, který vám umožní provádět vyhledávání bez rozlišování velkých a malých písmen na kombinované křestní jméno a příjmení osoby:
CREATE INDEX FULLNAME_UPPER_IDX ON "Contacts" (UPPER("firstName"||' '||"lastName"));
Návrh dotazu
Hlavními aspekty návrhu dotazů jsou:
- Seznamte se s plánem dotazu a ověřte jeho očekávané chování.
- Efektivně se připojte.
Vysvětlení plánu dotazů
V SQLLine použijte příkaz EXPLAIN následovaný dotazem SQL k zobrazení plánu operací, které Phoenix provádí. Zkontrolujte, že plán:
- Pokud je to vhodné, použije váš primární klíč.
- Používá vhodné sekundární indexy místo tabulky dat.
- Používá funkci RANGE SCAN nebo SKIP SCAN, kdykoli je to možné, místo funkce PROHLEDÁVÁNÍ TABULEK.
Příklady plánů
Řekněme například, že máte tabulku s názvem LETY, která ukládá informace o zpoždění letů.
Pokud chcete vybrat všechny lety s polem airlineid
19805
, kde airlineid
je pole, které není v primárním klíči nebo v žádném indexu:
select * from "FLIGHTS" where airlineid = '19805';
Spusťte vysvětlený příkaz následujícím způsobem:
explain select * from "FLIGHTS" where airlineid = '19805';
Plán dotazu vypadá takto:
CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN FULL SCAN OVER FLIGHTS
SERVER FILTER BY AIRLINEID = '19805'
V tomto plánu si všimněte fráze ÚPLNÉ PROHLEDÁVÁNÍ PŘES LETY. Tato fráze označuje, že provádění provede prohledávání tabulek přes všechny řádky v tabulce místo použití efektivnější možnosti OBLAST SCAN nebo SKIP SCAN.
Řekněme, že chcete zadat dotaz na lety 2. ledna 2014 pro dopravce AA
, kde jeho let byl větší než 1. Předpokládejme, že v ukázkové tabulce existují sloupce year, month, dayofmonth, carrier a flightnum a jsou součástí složeného primárního klíče. Dotaz by vypadal takto:
select * from "FLIGHTS" where year = 2014 and month = 1 and dayofmonth = 2 and carrier = 'AA' and flightnum > 1;
Pojďme se podívat na plán pro tento dotaz:
explain select * from "FLIGHTS" where year = 2014 and month = 1 and dayofmonth = 2 and carrier = 'AA' and flightnum > 1;
Výsledný plán je:
CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER FLIGHTS [2014,1,2,'AA',2] - [2014,1,2,'AA',*]
Hodnoty v hranatých závorkách jsou rozsahem hodnot primárních klíčů. V tomto případě jsou hodnoty rozsahu pevné s rokem 2014, měsícem 1 a dnem 2, ale umožňují hodnoty letového čísla počínaje 2 a nahoru (*
). Tento plán dotazu potvrzuje, že se primární klíč používá podle očekávání.
Dále vytvořte index v tabulce FLIGHTS s názvem carrier2_idx
, která je pouze v poli dopravce. Tento index také zahrnuje flightdate
sloupce , origin
tailnum
a flightnum
jako zahrnuté sloupce, jejichž data jsou také uložena v indexu.
CREATE INDEX carrier2_idx ON FLIGHTS (carrier) INCLUDE(FLIGHTDATE,TAILNUM,ORIGIN,FLIGHTNUM);
Řekněme, že chcete operátora získat spolu s operátorem flightdate
a tailnum
, jako v následujícím dotazu:
explain select carrier,flightdate,tailnum from "FLIGHTS" where carrier = 'AA';
Měli byste vidět, že se používá tento index:
CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER CARRIER2_IDX ['AA']
Úplný seznam položek, které se dají zobrazit v vysvětlení výsledků plánu, najdete v části Vysvětlit plány v Průvodci laděním Apache Phoenix.
Efektivní spojení
Obecně se chcete vyhnout spojením, pokud není jedna strana malá, zejména u častých dotazů.
V případě potřeby můžete provádět velké spojení s nápovědou /*+ USE_SORT_MERGE_JOIN */
, ale velké spojení je nákladná operace nad obrovským počtem řádků. Pokud by celková velikost všech pravých tabulek překročila dostupnou paměť, použijte nápovědu /*+ NO_STAR_JOIN */
.
Scénáře
Následující pokyny popisují některé běžné vzory.
Úlohy náročné na čtení
V případě případů použití náročných na čtení se ujistěte, že používáte indexy. Pokud chcete ušetřit režijní náklady na čtení, zvažte také vytvoření pokrytých indexů.
Úlohy náročné na zápis
Pro úlohy náročné na zápis, u kterých se primární klíč monotonicky zvyšuje, vytvořte kontejnery soli, které pomáhají vyhnout se hotspotům zápisu, a to na úkor celkové propustnosti čtení kvůli potřebným dalším kontrolám. Při použití funkce UPSERT k zápisu velkého počtu záznamů vypněte funkci autoCommit a zasadíte záznamy do dávek.
Hromadné odstranění
Při odstraňování velké datové sady zapněte funkci autoCommit před vydáním dotazu DELETE, aby si klient nemusel pamatovat klíče řádků pro všechny odstraněné řádky. AutoCommit zabrání klientovi ukládat řádky ovlivněné funkcí DELETE do vyrovnávací paměti, aby je Phoenix mohl odstranit přímo na serverech oblastí bez nákladů na jejich vrácení klientovi.
Neměnné a pouze připojení
Pokud váš scénář upřednostňuje rychlost zápisu před integritou dat, zvažte zakázání protokolu s předstihem při vytváření tabulek:
CREATE TABLE CONTACTS (...) DISABLE_WAL=true;
Podrobnosti o této a dalších možnostech najdete v tématu Apache Phoenix Grammar.