HBase
 sql >> Database >  >> NoSQL >> HBase

Suddivisione e unione di regioni Apache HBase

Questo post sul blog è stato pubblicato su Hortonworks.com prima della fusione con Cloudera. Alcuni collegamenti, risorse o riferimenti potrebbero non essere più accurati.

Per questo post, facciamo un tuffo tecnico in una delle aree principali di HBase. In particolare, esamineremo come Apache HBase distribuisce il carico attraverso le regioni e gestisce la suddivisione delle regioni. HBase memorizza le righe di dati nelle tabelle. Le tabelle sono suddivise in blocchi di righe chiamati "regioni". Tali regioni sono distribuite nel cluster, ospitate e rese disponibili ai processi client dal processo RegionServer. Una regione è un intervallo continuo all'interno dello spazio chiave, il che significa che tutte le righe della tabella che ordinano tra la chiave di inizio e la chiave di fine della regione sono archiviate nella stessa regione. Le regioni non sono sovrapposte, ovvero una chiave di riga singola appartiene esattamente a una regione in qualsiasi momento. Una regione è servita solo da un server di una singola regione in qualsiasi momento, ed è così che HBase garantisce una forte coerenza all'interno di una singola riga#. Insieme a -ROOT- e .META. regioni, le regioni di una tabella formano effettivamente un B-Tree di 3 livelli allo scopo di individuare una riga all'interno di una tabella.

Una Regione, a sua volta, è composta da tanti “Negozi”, che corrispondono a famiglie di colonne. Un negozio contiene un memstore e zero o più file di archivio. I dati per ciascuna famiglia di colonne vengono archiviati e vi si accede separatamente.

Una tabella in genere è costituita da molte regioni, che a loro volta sono ospitate da molti server di regioni. Pertanto, le regioni sono il meccanismo fisico utilizzato per distribuire il carico di scrittura e query tra i server delle regioni. Quando una tabella viene creata per la prima volta, HBase, per impostazione predefinita, allocherà solo una regione per la tabella. Ciò significa che inizialmente tutte le richieste andranno a un unico server della regione, indipendentemente dal numero di server della regione. Questo è il motivo principale per cui le fasi iniziali di caricamento dei dati in una tabella vuota non possono utilizzare l'intera capacità del cluster.

Pre-divisione

Il motivo per cui HBase crea solo una regione per la tabella è che non può sapere come creare i punti di divisione all'interno dello spazio chiave della riga. Prendere tali decisioni si basa principalmente sulla distribuzione delle chiavi nei tuoi dati. Invece di tirare a indovinare e lasciarti affrontare le conseguenze, HBase ti fornisce gli strumenti per gestirlo dal cliente. Con un processo chiamato pre-splitting, puoi creare una tabella con molte regioni fornendo i punti di divisione al momento della creazione della tabella. Poiché la pre-splitting assicurerà che il carico iniziale sia distribuito in modo più uniforme in tutto il cluster, dovresti sempre considerare di utilizzarlo se conosci in anticipo la tua distribuzione delle chiavi. Tuttavia, la pre-splitting comporta anche il rischio di creare regioni che non distribuiscono veramente il carico in modo uniforme a causa dell'inclinazione dei dati o in presenza di righe molto calde o di grandi dimensioni. Se l'insieme iniziale di punti di divisione della regione viene scelto male, potresti ritrovarti con una distribuzione del carico eterogenea, che a sua volta limiterà le prestazioni dei tuoi cluster.

Non esiste una risposta breve per il numero ottimale di regioni per un determinato carico, ma puoi iniziare con un multiplo inferiore del numero di server delle regioni come numero di suddivisioni, quindi lasciare che la suddivisione automatica si occupi del resto.

Un problema con il pre-splitting è il calcolo dei punti di divisione per la tabella. È possibile utilizzare l'utilità RegionSplitter. RegionSplitter crea i punti di divisione, utilizzando un SplitAlgorithm collegabile. HexStringSplit e UniformSplit sono due algoritmi predefiniti. Il primo può essere utilizzato se le chiavi di riga hanno un prefisso per le stringhe esadecimali (come se si utilizzano gli hash come prefissi). Quest'ultimo divide lo spazio delle chiavi in ​​modo uniforme supponendo che siano array di byte casuali. Puoi anche implementare il tuo SplitAlgorithm personalizzato e usarlo dall'utilità RegionSplitter.

$ hbase org.apache.hadoop.hbase.util.RegionSplitter test_table HexStringSplit -c 10 -f f1

dove -c 10, specifica il numero di regioni richiesto come 10, e -f specifica le famiglie di colonne desiderate nella tabella, separate da “:”. Lo strumento creerà una tabella denominata "test_table" con 10 regioni:

13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,,1358563771069.acc1ad1b7962564fc3a43e5907e8db33.', STARTKEY => '', ENDKEY => '19999999', ENCODED => acc1ad1b7962564fc3a43e5907e8db33,}
13/01/18 18:49:32 DEBUG hbase.HRegionInfo: Current INFO from scan results = {NAME => 'test_table,19999999,1358563771096.37ec12df6bd0078f5573565af415c91b.', STARTKEY => '19999999', ENDKEY => '33333332', ENCODED => 37ec12df6bd0078f5573565af415c91b,}
...

Se hai punti di divisione a portata di mano, puoi anche utilizzare la shell HBase, per creare la tabella con i punti di divisione desiderati.

hbase(main):015:0> create 'test_table', 'f1', SPLITS=> ['a', 'b', 'c']

o

$ echo -e  "anbnc" >/tmp/splits
hbase(main):015:0> create 'test_table', 'f1', SPLITSFILE=>'/tmp/splits'

Per una distribuzione ottimale del carico, dovresti pensare al tuo modello di dati e alla distribuzione delle chiavi per scegliere l'algoritmo di divisione o i punti di divisione corretti. Indipendentemente dal metodo scelto per creare la tabella con un numero predeterminato di regioni, ora puoi iniziare a caricare i dati nella tabella e vedere che il carico è distribuito nel tuo cluster. Puoi lasciare che la suddivisione automatizzata prenda il sopravvento all'avvio dell'acquisizione dei dati e monitorare continuamente il numero totale di regioni per la tabella.

Divisione automatica

Indipendentemente dal fatto che venga utilizzata o meno la suddivisione preliminare, una volta che una regione raggiunge un determinato limite, viene automaticamente suddivisa in due regioni. Se utilizzi HBase 0.94 (fornito con HDP-1.2), puoi configurare quando HBase decide di dividere una regione e come calcola i punti di divisione tramite l'API RegionSplitPolicy collegabile. Esistono un paio di criteri di suddivisione dell'area predefiniti:ConstantSizeRegionSplitPolicy, IncreasingToUpperBoundRegionSplitPolicy e KeyPrefixRegionSplitPolicy.

Il primo è il criterio di divisione predefinito e unico per le versioni HBase precedenti a 0.94. Divide le regioni quando la dimensione totale dei dati per uno dei negozi (corrispondenti a una famiglia di colonne) nella regione diventa maggiore di "hbase.hregion.max.filesize" configurato, che ha un valore predefinito di 10 GB. Questa norma di suddivisione è l'ideale nei casi in cui hai terminato la pre-divisione e sei interessato a ridurre il numero di regioni per server di regione.

La politica di suddivisione predefinita per HBase 0.94 e trunk è IncreasingToUpperBoundRegionSplitPolicy, che esegue una suddivisione più aggressiva in base al numero di regioni ospitate nello stesso server di regione. Il criterio di suddivisione utilizza la dimensione massima del file di archiviazione basata su Min (R^2 * "hbase.hregion.memstore.flush.size", "hbase.hregion.max.filesize"), dove R è il numero di regioni della stessa tabella ospitata sullo stesso regionserver. Ad esempio, con la dimensione di svuotamento predefinita del memstore di 128 MB e la dimensione massima predefinita dell'archivio di 10 GB, la prima regione sul server della regione verrà suddivisa subito dopo il primo svuotamento a 128 MB. Con l'aumento del numero di regioni ospitate nel server della regione, verranno utilizzate dimensioni divise crescenti:512 MB, 1152 MB, 2 GB, 3,2 GB, 4,6 GB, 6,2 GB, ecc. Dopo aver raggiunto 9 regioni, le dimensioni divise andranno oltre la "hbase" configurata .hregion.max.filesize", a quel punto, da quel momento in poi verrà utilizzata la dimensione divisa di 10 GB. Per entrambi questi algoritmi, indipendentemente da quando si verifica la divisione, il punto di divisione utilizzato è la chiave di riga che corrisponde al punto medio nell'"indice di blocco" per il file di archivio più grande nell'archivio più grande.

KeyPrefixRegionSplitPolicy è una curiosa aggiunta all'arsenale HBase. È possibile configurare la lunghezza del prefisso per le chiavi di riga per raggrupparle e questo criterio di divisione garantisce che le regioni non siano suddivise nel mezzo di un gruppo di righe con lo stesso prefisso. Se hai impostato i prefissi per le tue chiavi, puoi utilizzare questo criterio di divisione per assicurarti che le righe con lo stesso prefisso della chiave di riga finiscano sempre nella stessa regione. Questo raggruppamento di record viene talvolta denominato "Gruppi di entità" o "Gruppi di righe". Questa è una caratteristica fondamentale quando si considera l'uso della funzione "transazioni locali" (collegamento alternativo) nella progettazione dell'applicazione.

È possibile configurare la politica di suddivisione predefinita da utilizzare impostando la configurazione "hbase.regionserver.region.split.policy" o configurando il descrittore di tabella. Per le anime coraggiose, puoi anche implementare la tua politica di divisione personalizzata e collegarla al momento della creazione della tabella o modificando una tabella esistente:

HTableDescriptor tableDesc = new HTableDescriptor("example-table");
tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, AwesomeSplitPolicy.class.getName());
//add columns etc
admin.createTable(tableDesc);

Se si esegue la pre-splitting e si desidera gestire manualmente le suddivisioni delle regioni, è anche possibile disabilitare le suddivisioni delle regioni impostando "hbase.hregion.max.filesize" su un numero elevato e impostando il criterio di suddivisione su ConstantSizeRegionSplitPolicy. Tuttavia, dovresti utilizzare un valore di protezione di circa 100 GB, in modo che le regioni non crescano oltre le capacità di un server regionale. Puoi prendere in considerazione la disabilitazione della suddivisione automatica e fare affidamento sul set iniziale di regioni dalla pre-divisione, ad esempio, se stai utilizzando hash uniformi per i prefissi delle chiavi e puoi assicurarti che la lettura/scrittura venga caricata in ciascuna regione così come le sue dimensioni è uniforme tra le regioni nella tabella.

Separazioni forzate

HBase consente inoltre ai clienti di forzare la divisione di una tabella online dal lato client. Ad esempio, la shell HBase può essere utilizzata per dividere tutte le regioni della tabella o dividere una regione, opzionalmente fornendo un punto di divisione.

hbase(main):024:0> split 'b07d0034cbe72cb040ae9cf66300a10c', 'b'
0 row(s) in 0.1620 seconds

Con un attento monitoraggio della distribuzione del carico HBase, se vedi che alcune regioni stanno ricevendo carichi irregolari, potresti considerare di dividere manualmente tali regioni per uniformare il carico e migliorare la velocità effettiva. Un altro motivo per cui potresti voler eseguire le divisioni manuali è quando vedi che le divisioni iniziali per la regione risultano non ottimali e hai disabilitato le divisioni automatiche. Ciò potrebbe accadere, ad esempio, se la distribuzione dei dati cambia nel tempo.

Come vengono implementate le suddivisioni regionali

Poiché le richieste di scrittura vengono gestite dal server della regione, si accumulano in un sistema di archiviazione in memoria chiamato "memstore". Una volta riempito il memstore, il suo contenuto viene scritto su disco come file di archivio aggiuntivi. Questo evento è chiamato "memstore flush". Man mano che i file del negozio si accumulano, RegionServer li "compatta" in file combinati e più grandi. Al termine di ogni svuotamento o compattazione, una richiesta di suddivisione dell'area viene accodata se RegionSplitPolicy decide che l'area deve essere divisa in due. Poiché tutti i file di dati in HBase sono immutabili, quando si verifica una divisione, le regioni figlie appena create non riscriveranno tutti i dati in nuovi file. Invece, creeranno piccoli file simili a collegamenti simbolici, denominati File di riferimento, che puntano alla parte superiore o inferiore del file del negozio principale in base al punto di divisione. Il file di riferimento verrà utilizzato proprio come un normale file di dati, ma solo la metà dei record. La regione può essere divisa solo se non ci sono più riferimenti ai file di dati immutabili della regione padre. Questi file di riferimento vengono puliti gradualmente dalle compattazioni, in modo che la regione smetta di fare riferimento ai suoi file padre e possa essere ulteriormente suddivisa.

Sebbene la divisione della regione sia una decisione locale presa da RegionServer, il processo di divisione stesso deve coordinarsi con molti attori. Il RegionServer notifica al Master prima e dopo lo split, aggiorna il .META. tabella in modo che i client possano scoprire le nuove regioni figlie e riorganizzare la struttura delle directory e i file di dati in HDFS. La divisione è un processo multi-attività. Per abilitare il rollback in caso di errore, RegionServer mantiene un diario in memoria sullo stato di esecuzione. I passaggi eseguiti da RegionServer per eseguire la divisione sono illustrati nella Figura 1. Ciascun passaggio è etichettato con il relativo numero di passaggio. Le azioni da RegionServers o Master sono visualizzate in rosso, mentre le azioni dai client sono mostrate in verde.

1. RegionServer decide in locale di dividere la regione e prepara la divisione. Come primo passo, crea uno znode in zookeeper sotto /hbase/region-in-transition/region-name nello stato SPLITTING.
2. Il Master viene a conoscenza di questo znode, poiché dispone di un watcher per lo znode di transizione della regione padre.
3. RegionServer crea una sottodirectory denominata ".splits" nella directory della regione principale in HDFS.
4. RegionServer chiude la regione padre, forza uno svuotamento della cache e contrassegna la regione come offline nelle sue strutture dati locali. A questo punto, le richieste dei client che arrivano all'area padre genereranno NotServingRegionException. Il client riproverà con alcuni backoff.
5. RegionServer crea le directory della regione nella directory .splits, per le regioni secondarie A e B, e crea le strutture dati necessarie. Quindi divide i file del negozio, nel senso che crea due file di riferimento per file del negozio nella regione padre. Tali file di riferimento punteranno ai file delle regioni principali.
6. RegionServer crea la directory della regione effettiva in HDFS e sposta i file di riferimento per ciascuna figlia.
7. RegionServer invia una richiesta Put al .META. tabella e imposta il genitore come offline nel file .META. tabella e aggiunge informazioni sulle regioni figlie. A questo punto, non ci saranno voci singole in .META. per le figlie. I clienti vedranno che la regione madre è divisa se scansionano .META., ma non sapranno delle figlie fino a quando non appaiono in .META.. Inoltre, se questo Inserisci in .META. riesce, il genitore sarà effettivamente diviso. Se il RegionServer ha esito negativo prima che questo RPC abbia esito positivo, il master e il server della regione successiva che apre la regione ripuliranno lo stato sporco relativo alla divisione della regione. Dopo il .META. aggiornamento, tuttavia, la divisione della regione verrà portata avanti dal Master.
8. RegionServer apre le figlie in parallelo per accettare le scritture.
9. RegionServer aggiunge le figlie A e B a .META. insieme alle informazioni che ospita le regioni. Dopo questo punto, i clienti possono scoprire le nuove regioni ed inviare richieste alla nuova regione. I client memorizzano nella cache .META. voci in locale, ma quando effettuano richieste al server della regione o .META., le loro cache verranno invalidate e impareranno a conoscere le nuove regioni da .META..
10. RegionServer aggiorna znode /hbase/region-in-transition/region-name in zookeeper per indicare SPLIT, in modo che il master possa conoscerlo. Il bilanciatore può riassegnare liberamente le regioni figlie ad altri server di regioni, se lo desidera.
11. Dopo la divisione, meta e HDFS conterranno ancora i riferimenti alla regione padre. Tali riferimenti verranno rimossi quando le compattazioni nelle regioni figlie riscrivono i file di dati. Le attività di raccolta dei rifiuti nel master controllano periodicamente se le regioni figlie fanno ancora riferimento ai file dei genitori. In caso contrario, la regione principale verrà rimossa.

Fusioni di regioni

A differenza della divisione delle regioni, HBase a questo punto non fornisce strumenti utilizzabili per unire le regioni. Sebbene esistano strumenti HMerge e Unisci, non sono molto adatti per un uso generale. Al momento non è disponibile alcun supporto per le tabelle online e la funzionalità di unione automatica. Tuttavia, con problemi come OnlineMerge, master ha avviato le unioni di regioni automatiche, blocchi di lettura/scrittura basati su ZK per le operazioni di tabella, stiamo lavorando per stabilizzare le suddivisioni delle regioni e consentire un migliore supporto per le unioni di regioni. Resta sintonizzato!

Conclusione

Come puoi vedere, HBase sotto il cofano fa molte pulizie per gestire le suddivisioni delle regioni ed eseguire lo sharding automatizzato attraverso le regioni. Tuttavia, HBase fornisce anche gli strumenti necessari per la gestione delle regioni, in modo da poter gestire il processo di suddivisione. Puoi anche controllare con precisione quando e come avvengono le suddivisioni delle regioni tramite RegionSplitPolicy.

Il numero di regioni in una tabella e il modo in cui tali regioni sono suddivise sono fattori cruciali per comprendere e ottimizzare il carico del cluster HBase. Se è possibile stimare la distribuzione delle chiavi, è necessario creare la tabella con pre-splitting per ottenere prestazioni di carico iniziale ottimali. Puoi iniziare con un multiplo inferiore del numero di server delle regioni come punto di partenza per il numero iniziale di regioni e lasciare che la suddivisione automatizzata prenda il sopravvento. Se non è possibile stimare correttamente i punti di divisione iniziali, è meglio creare semplicemente la tabella con un'area e avviare un caricamento iniziale con la divisione automatizzata e utilizzare IncreasingToUpperBoundRegionSplitPolicy. Tuttavia, tieni presente che il numero totale di regioni si stabilizzerà nel tempo e l'attuale set di punti di divisione delle regioni sarà determinato dai dati che la tabella ha ricevuto finora. Potresti voler monitorare la distribuzione del carico tra le regioni in ogni momento e, se la distribuzione del carico cambia nel tempo, utilizzare la suddivisione manuale o impostare dimensioni di suddivisione delle regioni più aggressive. Infine, puoi provare la prossima funzione di unione online e contribuire con il tuo caso d'uso.