Osvědčené postupy pro Apache Phoenix z hlediska výkonuApache Phoenix performance best practices

Nejdůležitější aspektem Apache Phoenix výkonu je optimalizace základních HBA pro Apache.The most important aspect of Apache Phoenix performance is to optimize the underlying Apache HBase. Phoenix vytvoří model relačních dat základem HBA, který převede dotazy SQL na operace HBA, jako jsou například kontroly.Phoenix creates a relational data model atop HBase that converts SQL queries into HBase operations, such as scans. Návrh schématu tabulky, výběr a řazení polí v primárním klíči a vaše používání indexů má vliv na výkon v Phoenixu.The design of your table schema, the selection and ordering of the fields in your primary key, and your use of indexes all affect Phoenix performance.

Návrh schématu tabulkyTable schema design

Když vytvoříte tabulku v Phoenixu, tato tabulka je uložená v tabulce HBA.When you create a table in Phoenix, that table is stored in an HBase table. Tabulka HBA obsahuje skupiny sloupců (rodin sloupců), ke kterým se přistupuje dohromady.The HBase table contains groups of columns (column families) that are accessed together. Řádek v tabulce Phoenixu je řádek v tabulce HBA, kde každý řádek obsahuje buňky s verzemi, které jsou přidružené k jednomu nebo více sloupcům.A row in the Phoenix table is a row in the HBase table, where each row consists of versioned cells associated with one or more columns. Logicky, jeden řádek HBA je kolekcí párů klíč-hodnota, z nichž každá má stejnou hodnotu rowkey.Logically, a single HBase row is a collection of key-value pairs, each having the same rowkey value. To znamená, že každá dvojice klíč-hodnota má atribut rowkey a hodnota tohoto atributu rowkey je stejná pro určitý řádek.That is, each key-value pair has a rowkey attribute, and the value of that rowkey attribute is the same for a particular row.

Návrh schématu tabulky Phoenix zahrnuje návrh primárního klíče, návrh rodiny sloupců, jednotlivý návrh sloupce a způsob rozdělení dat do oddílů.The schema design of a Phoenix table includes the primary key design, column family design, individual column design, and how the data is partitioned.

Návrh primárního klíčePrimary key design

Primární klíč definovaný v tabulce v Phoenixu určuje, jak jsou data uložená v rowkey podkladové tabulky HBA.The primary key defined on a table in Phoenix determines how data is stored within the rowkey of the underlying HBase table. V rámci adaptérů HBA je jediným způsobem, jak získat přístup k určitému řádku, rowkey.In HBase, the only way to access a particular row is with the rowkey. Kromě toho jsou data uložená v tabulce HBA řazena podle rowkey.In addition, data stored in an HBase table is sorted by the rowkey. Phoenix sestaví hodnotu rowkey zřetězením hodnot každého sloupce v řádku v pořadí, v jakém jsou definovány v primárním klíči.Phoenix builds the rowkey value by concatenating the values of each of the columns in the row, in the order they're defined in the primary key.

Například tabulka pro kontakty má jméno, příjmení, telefonní číslo a adresu, a to vše ve stejné rodině sloupců.For example, a table for contacts has the first name, last name, phone number, and address, all in the same column family. Můžete definovat primární klíč na základě rostoucího pořadového čísla:You could define a primary key based on an increasing sequence number:

rowkeyrowkey adresaaddress Androidphone firstNamefirstName lastNamelastName
10001000 1111 síť San Gabrielem Dr.1111 San Gabriel Dr. 1-425-000-00021-425-000-0002 JohnJohn DoleDole
83968396 5415 síť San Gabrielem Dr.5415 San Gabriel Dr. 1-230-555-01911-230-555-0191 CalvinCalvin RajiRaji

Pokud se ale často dotazuje podle oprávnění lastName, nemusí tento primární klíč správně fungovat, protože každý dotaz vyžaduje, aby všechny dotazy načetly hodnotu každé příjmení.However, if you frequently query by lastName this primary key may not perform well, because each query requires a full table scan to read the value of every lastName. Místo toho můžete definovat primární klíč ve sloupcích lastName, firstName a číslo rodného čísla.Instead, you can define a primary key on the lastName, firstName, and social security number columns. Posledním sloupcem je nejednoznačnost dvou rezidentů na stejné adrese, jako je například otců a syn.This last column is to disambiguate two residents at the same address with the same name, such as a father and son.

rowkeyrowkey adresaaddress Androidphone firstNamefirstName lastNamelastName socialSecurityNumsocialSecurityNum
10001000 1111 síť San Gabrielem Dr.1111 San Gabriel Dr. 1-425-000-00021-425-000-0002 JohnJohn DoleDole 111111
83968396 5415 síť San Gabrielem Dr.5415 San Gabriel Dr. 1-230-555-01911-230-555-0191 CalvinCalvin RajiRaji 222222

S tímto novým primárním klíčem klíče řádků generované v Phoenixu budou:With this new primary key the row keys generated by Phoenix would be:

rowkeyrowkey adresaaddress Androidphone firstNamefirstName lastNamelastName socialSecurityNumsocialSecurityNum
Dole – Jan až 111Dole-John-111 1111 síť San Gabrielem Dr.1111 San Gabriel Dr. 1-425-000-00021-425-000-0002 JohnJohn DoleDole 111111
Raji-Calvin-222Raji-Calvin-222 5415 síť San Gabrielem Dr.5415 San Gabriel Dr. 1-230-555-01911-230-555-0191 CalvinCalvin RajiRaji 222222

V prvním řádku výše jsou data pro rowkey reprezentovaná, jak je znázorněno níže:In the first row above, the data for the rowkey is represented as shown:

rowkeyrowkey keykey valuevalue
Dole – Jan až 111Dole-John-111 adresaaddress 1111 síť San Gabrielem Dr.1111 San Gabriel Dr.
Dole – Jan až 111Dole-John-111 Androidphone 1-425-000-00021-425-000-0002
Dole – Jan až 111Dole-John-111 firstNamefirstName JohnJohn
Dole – Jan až 111Dole-John-111 lastNamelastName DoleDole
Dole – Jan až 111Dole-John-111 socialSecurityNumsocialSecurityNum 111111

Tato rowkey nyní ukládá duplicitní kopii dat.This rowkey now stores a duplicate copy of the data. Vezměte v úvahu velikost a počet sloupců, které zahrnete do primárního klíče, protože tato hodnota je zahrnutá do každé buňky v podkladové tabulce HBA.Consider the size and number of columns you include in your primary key, because this value is included with every cell in the underlying HBase table.

Také pokud má primární klíč hodnoty, které se rovnoměrně zvětšující, měli byste vytvořit tabulku s kontejnery Salt , abyste se vyhnuli vytváření hotspotů pro zápis – viz data oddílu.Also, if the primary key has values that are monotonically increasing, you should create the table with salt buckets to help avoid creating write hotspots - see Partition data.

Návrh rodiny sloupcůColumn family design

Pokud jsou některé sloupce dostupné častěji než jiné, měli byste vytvořit více rodin sloupců, které oddělují často používané sloupce ze sloupců s zřídka použitou výjimkou.If some columns are accessed more frequently than others, you should create multiple column families to separate the frequently accessed columns from rarely accessed columns.

Také pokud jsou k určitým sloupcům přicházet společně, umístěte tyto sloupce do stejné rodiny sloupců.Also, if certain columns tend to be accessed together, put those columns in the same column family.

Návrh sloupceColumn design

  • Ponechte sloupce VARCHAR pod přibližně 1 MB z důvodu nákladů na vstupně-výstupní operace velkých sloupců.Keep VARCHAR columns under about 1 MB because of the I/O costs of large columns. Při zpracování dotazů vyplní materializuje buňky v plném rozsahu před jejich odesláním klientovi a klient je zaplní před tím, než ho dostanou do kódu aplikace.When processing queries, HBase materializes cells in full before sending them over to the client, and the client receives them in full before handing them off to the application code.
  • Uložte hodnoty sloupce pomocí kompaktního formátu, jako je například protobuf, Avro, msgpack nebo BSON.Store column values using a compact format such as protobuf, Avro, msgpack, or BSON. JSON se nedoporučuje, protože je větší.JSON isn't recommended, as it's larger.
  • Zvažte komprimaci dat před úložištěm za účelem snížení latence a vstupně-výstupních nákladů.Consider compressing data before storage to cut latency and I/O costs.

Dělení datPartition data

Phoenix vám umožňuje řídit počet oblastí, ve kterých jsou vaše data distribuována, což může významně zvýšit výkon čtení a zápisu.Phoenix enables you to control the number of regions where your data is distributed, which can significantly increase read/write performance. Když vytváříte tabulku v Phoenixu, můžete data buď nasoleit, nebo předem rozdělit.When creating a Phoenix table, you can either salt or pre-split your data.

Chcete-li během vytváření nasoleit tabulku, zadejte počet sad Salt:To salt a table during creation, specify the number of salt buckets:

CREATE TABLE CONTACTS (...) SALT_BUCKETS = 16

Toto nasolení rozdělí tabulku podél hodnot primárních klíčů a automaticky zvolí hodnoty.This salting splits the table along the values of primary keys, choosing the values automatically.

Chcete-li určit, kde dojde k rozdělení tabulky, můžete tabulku před rozrozdělením zadáním hodnot rozsahu, podél kterých se rozdělení provádí.To control where the table splits occur, you can pre-split the table by providing the range values along which the splitting occurs. Chcete-li například vytvořit tabulku rozdělenou podél tří oblastí:For example, to create a table split along three regions:

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

Návrh indexuIndex design

Index Phoenix je tabulka HBA, která ukládá kopii některých nebo všech dat z indexované tabulky.A Phoenix index is an HBase table that stores a copy of some or all of the data from the indexed table. Index zvyšuje výkon pro konkrétní typy dotazů.An index improves performance for specific types of queries.

Pokud máte definováno více indexů a pak dotaz na tabulku, aplikace Phoenix automaticky vybere nejlepší index pro dotaz.When you have multiple indexes defined and then query a table, Phoenix automatically selects the best index for the query. Primární index se vytvoří automaticky v závislosti na vybraných primárních klíčích.The primary index is created automatically based on the primary keys you select.

U předpokládaných dotazů můžete také vytvořit sekundární indexy zadáním jejich sloupců.For anticipated queries, you can also create secondary indexes by specifying their columns.

Při navrhování indexů:When designing your indexes:

  • Vytvářejte pouze indexy, které potřebujete.Only create the indexes you need.
  • Omezte počet indexů na často aktualizovaných tabulkách.Limit the number of indexes on frequently updated tables. Aktualizace tabulky se přeloží na zápisy do hlavní tabulky i tabulek indexu.Updates to a table translate into writes to both the main table and the index tables.

Vytváření sekundárních indexůCreate secondary indexes

Sekundární indexy můžou zlepšit výkon při čtení tím, že se na základě toho, co by představovalo úplné prohledávání tabulky, provedou vyhledávání bodů, a to za cenu úložného prostoru a rychlosti zápisu.Secondary indexes can improve read performance by turning what would be a full table scan into a point lookup, at the cost of storage space and write speed. Sekundární indexy je možné přidat nebo odebrat po vytvoření tabulky a nevyžadují změny existujících dotazů – dotazy stačí spustit rychleji.Secondary indexes can be added or removed after table creation and don’t require changes to existing queries – queries just run faster. V závislosti na vašich potřebách zvažte vytvoření zahrnutých indexů, funkčních indexů nebo obojího.Depending on your needs, consider creating covered indexes, functional indexes, or both.

Použití zahrnutých indexůUse covered indexes

Zahrnuté indexy jsou indexy, které zahrnují data z řádku kromě indexovaných hodnot.Covered indexes are indexes that include data from the row in addition to the values that are indexed. Po nalezení požadované položky indexu není nutné přístup k primární tabulce.After finding the desired index entry, there's no need to access the primary table.

Například v tabulce příklad kontaktu byste mohli vytvořit sekundární index pouze v socialSecurityNum sloupci.For example, in the example contact table you could create a secondary index on just the socialSecurityNum column. Tento sekundární index by urychlil dotazy, které filtrují podle hodnot socialSecurityNum, ale načítání ostatních hodnot polí bude vyžadovat další čtení v hlavní tabulce.This secondary index would speed up queries that filter by socialSecurityNum values, but retrieving other field values will require another read against the main table.

rowkeyrowkey adresaaddress Androidphone firstNamefirstName lastNamelastName socialSecurityNumsocialSecurityNum
Dole – Jan až 111Dole-John-111 1111 síť San Gabrielem Dr.1111 San Gabriel Dr. 1-425-000-00021-425-000-0002 JohnJohn DoleDole 111111
Raji-Calvin-222Raji-Calvin-222 5415 síť San Gabrielem Dr.5415 San Gabriel Dr. 1-230-555-01911-230-555-0191 CalvinCalvin RajiRaji 222222

Pokud ale obvykle chcete vyhledat pole firstName a lastName s daným socialSecurityNum, mohli byste vytvořit zahrnutý index, který obsahuje pole firstName a lastName jako skutečná data v tabulce index:However, if you typically want to look up the firstName and lastName given the socialSecurityNum, you could create a covered index that includes the firstName and lastName as actual data in the index table:

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

Tento zahrnutý index umožňuje, aby následující dotaz získal všechna data pouhým čtením z tabulky obsahující sekundární index:This covered index enables the following query to acquire all data just by reading from the table containing the secondary index:

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

Použití funkčních indexůUse functional indexes

Funkční indexy umožňují vytvořit index pro libovolný výraz, který očekáváte, že se bude používat v dotazech.Functional indexes allow you to create an index on an arbitrary expression that you expect to be used in queries. Jakmile budete mít funkční index a dotaz použije tento výraz, může se index použít k načtení výsledků, nikoli k tabulce dat.Once you have a functional index in place and a query uses that expression, the index may be used to retrieve the results rather than the data table.

Můžete například vytvořit index, který vám umožní provádět hledání bez rozlišování velkých a malých písmen na základě kombinovaného křestního jména a příjmení osoby:For example, you could create an index to allow you to do case-insensitive searches on the combined first name and last name of a person:

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

Návrh dotazuQuery design

Hlavními hledisky při návrhu dotazů jsou:The main considerations in query design are:

  • Pochopení plánu dotazů a ověření očekávaného chování.Understand the query plan and verify its expected behavior.
  • Připojte se efektivně.Join efficiently.

Pochopení plánu dotazuUnderstand the query plan

V SQLLinepoužijte vysvětlení následovaný vaším dotazem SQL k zobrazení plánu operací, které bude provádět Phoenix.In SQLLine, use EXPLAIN followed by your SQL query to view the plan of operations that Phoenix will perform. Ověřte, že plán:Check that the plan:

  • V případě potřeby použije primární klíč.Uses your primary key when appropriate.
  • Používá vhodné sekundární indexy místo tabulky dat.Uses appropriate secondary indexes, rather than the data table.
  • Nástroj používá kontrolu rozsahu nebo PŘESKOČENí kontroly, kdykoli je to možné, nikoli při prohledávání tabulky.Uses RANGE SCAN or SKIP SCAN whenever possible, rather than TABLE SCAN.

Příklady plánůPlan examples

Řekněme například, že máte tabulku s názvem lety, která ukládá informace o zpoždění letu.As an example, say you have a table called FLIGHTS that stores flight delay information.

Pokud chcete vybrat všechny lety s airlineid 19805 , kde airlineid je pole, které není v primárním klíči nebo v žádném indexu:To select all the flights with an airlineid of 19805, where airlineid is a field that isn't in the primary key or in any index:

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

Spusťte příkaz vysvětlit následujícím způsobem:Run the explain command as follows:

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

Plán dotazu vypadá takto:The query plan looks like this:

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

V tomto plánu si poznamenejte frázi úplná kontrola nad lety.In this plan, note the phrase FULL SCAN OVER FLIGHTS. Tato fráze indikuje, že při provádění se v tabulce prohledává všechny řádky v tabulce místo použití možnosti zefektivnit kontrolu rozsahu nebo přeskočit kontrolu.This phrase indicates the execution does a TABLE SCAN over all rows in the table, rather than using the more efficient RANGE SCAN or SKIP SCAN option.

Nyní řekněme, že chcete zadat dotaz na lety 2. ledna 2014 pro přepravce, AA kde jeho flightnum bylo větší než 1.Now, say you want to query for flights on January 2, 2014 for the carrier AA where its flightnum was greater than 1. Řekněme, že sloupce year, month, DayOfMonth, přepravce a flightnum existují v tabulce příkladů a jsou všechny součástí složeného primárního klíče.Let's assume that the columns year, month, dayofmonth, carrier, and flightnum exist in the example table, and are all part of the composite primary key. Dotaz by vypadal takto:The query would look as follows:

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

Podívejme se na plán tohoto dotazu:Let's examine the plan for this query with:

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

Výsledný plán:The resulting plan is:

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 pro primární klíče.The values in square brackets are the range of values for the primary keys. V tomto případě jsou hodnoty rozsahu opraveny s rokem 2014, měsíc 1 a DayOfMonth 2, ale umožňují hodnoty pro flightnum počínaje 2 a na začátku ( * ).In this case, the range values are fixed with year 2014, month 1, and dayofmonth 2, but allow values for flightnum starting with 2 and on up (*). Tento plán dotazu potvrdí, že se primární klíč používá podle očekávání.This query plan confirms that the primary key is being used as expected.

Dále v tabulce lety vytvořte index s názvem carrier2_idx , který je pouze v poli přepravce.Next, create an index on the FLIGHTS table named carrier2_idx that is on the carrier field only. Tento index zahrnuje také flightdate, tailnum, Origin a flightnum jako zahrnuté sloupce, jejichž data jsou také uložená v indexu.This index also includes flightdate, tailnum, origin, and flightnum as covered columns whose data is also stored in the index.

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

Řekněme, že chcete získat přepravce spolu s flightdate a tailnum, jak je znázorněno v následujícím dotazu:Say you want to get the carrier along with the flightdate and tailnum, as in the following query:

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

Měli byste vidět, že se používá tento index:You should see this index being used:

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

Úplný seznam položek, které se mohou objevit v tématu Vysvětlení výsledků plánu, najdete v části vysvětlující plány v příručce pro ladění Apache Phoenix.For a complete listing of the items that can appear in explain plan results, see the Explain Plans section in the Apache Phoenix Tuning Guide.

Efektivní připojeníJoin efficiently

Obecně platí, že chcete vyhnout spojení, pokud jedna strana není malá, zejména u častých dotazů.Generally, you want to avoid joins unless one side is small, especially on frequent queries.

V případě potřeby můžete s pomocným nástrojem provádět velké spojení /*+ USE_SORT_MERGE_JOIN */ , ale velký počet spojení je náročná operace nad velkým počtem řádků.If necessary, you can do large joins with the /*+ USE_SORT_MERGE_JOIN */ hint, but a large join is an expensive operation over huge numbers of rows. Pokud celková velikost všech tabulek na pravé straně by překročila dostupnou paměť, použijte /*+ NO_STAR_JOIN */ pomocný parametr.If the overall size of all right-hand-side tables would exceed the available memory, use the /*+ NO_STAR_JOIN */ hint.

ScénářeScenarios

Následující pokyny popisují některé běžné vzory.The following guidelines describe some common patterns.

Čtení – těžké úlohyRead-heavy workloads

Pro případy použití s vysokým využitím se ujistěte, že používáte indexy.For read-heavy use cases, make sure you're using indexes. Kromě toho, pokud chcete ušetřit režijní náklady při čtení, zvažte vytvoření zahrnutých indexů.Additionally, to save read-time overhead, consider creating covered indexes.

Zátěžové úlohy náročné na zápisWrite-heavy workloads

Pro úlohy náročné na zápis, u kterých je primární klíč rovnoměrně zvětšující, vytvořte bloky Salt, které vám pomůžou vyhnout se psaní hotspotů, a to za cenu celkové propustnosti čtení z důvodu dalších potřebných kontrol.For write-heavy workloads where the primary key is monotonically increasing, create salt buckets to help avoid write hotspots, at the expense of overall read throughput because of the additional scans needed. Také při použití UPSERT k zápisu velkého počtu záznamů vypněte automatické potvrzení a dávkujte záznamy.Also, when using UPSERT to write a large number of records, turn off autoCommit and batch up the records.

Hromadné odstraněníBulk deletes

Při odstraňování velkých datových sad zapněte funkci autocommit před vydáním dotazu DELETE, aby klient nemusel pamatovat klíče řádků pro všechny odstraněné řádky.When deleting a large data set, turn on autoCommit before issuing the DELETE query, so that the client doesn't need to remember the row keys for all deleted rows. Automatický zápis znemožňuje klientovi ukládat do vyrovnávací paměti řádky ovlivněné ODSTRANĚNÍm, aby je mohl v Phoenixu odstranit přímo na serverech oblastí bez nutnosti jejich vrácení klientovi.AutoCommit prevents the client from buffering the rows affected by the DELETE, so that Phoenix can delete them directly on the region servers without the expense of returning them to the client.

Unmutable a pouze připojeníImmutable and Append-only

Pokud váš scénář přinese rychlost zápisu přes integritu dat, zvažte možnost zakázat protokol zápisu při vytváření tabulek:If your scenario favors write speed over data integrity, consider disabling the write-ahead log when creating your tables:

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

Podrobnosti o této a dalších možnostech najdete v tématu Apache Phoenix gramatiky.For details on this and other options, see Apache Phoenix Grammar.

Další krokyNext steps