6.1 Хто і навіщо вигадав HBase

У цій лекції ми поговоримо про такий чудовий інструмент як Hbase, який останнім часом завоював велику популярність: наприклад, Facebook використовує його як основу своєї системи обміну повідомлень, а це вже багато про що говорить.

У лекції буде розказано про концепцію Big Table та її вільну реалізацію, особливості роботи та відмінність як від класичних реляційних баз даних (таких як MySQL та Oracle), так і key-value сховищ, таких як Redis, Aerospike та memcached. Як завжди - почнемо з історії питання. Як і багато інших проектів з області BigData, Hbase зародилася з концепції, розробленої в Google. Принципи, що лежать в основі Hbase, були описані у статті Bigtable: A Distributed Storage System for Structured Data .

Як ми розглядали в минулих лекціях - звичайні файли досить непогано підходять для пакетної обробки даних з використанням парадигми MapReduce. З іншого боку, інформацію, що зберігається у файлух, досить незручно оновлювати; Файли також не мають можливості довільного доступу. Для швидкої та зручної роботи з довільним доступом є клас nosql-систем типу key-value storage, таких як Aerospike, Redis, Couchbase, Memcached. Однак зазвичай у цих системах дуже незручна пакетна обробка даних. Hbase є спробою об'єднання зручності пакетної обробки і зручності оновлення і довільного доступу.

6.2 Модель даних

HBase - це розподілена, колоночно-орієнтована, мультиверсійна база типу "ключ-значення".

  • Дані організовані в таблиці, проіндексовані первинним ключем, який Hbase називається RowKey.
  • Для кожного RowKey ключа може зберігатися необмежений набір атрибутів (або колонок).
  • Колонки організовані групи колонок, звані Column Family. Як правило, в одну Column Family об'єднують колонки, для яких однакові патерн використання та зберігання.
  • Для кожного атрибута може зберігатись кілька різних версій. Різні версії мають різний timestamp.

Записи фізично зберігаються у відсортованому по RowKey порядку. При цьому дані, що відповідають різним Column Family, зберігаються окремо, що дозволяє при необхідності читати дані тільки з потрібного сімейства колонок.

При видаленні певного атрибуту фізично він одразу не видаляється, а лише маркується спеціальним прапорцем тебстона. Фізичне видалення даних відбудеться пізніше при виконанні операції Major Compaction.

Атрибути, що належать до однієї групи колонок і відповідні одному ключу фізично зберігаються як відсортований список. Будь-який атрибут може бути відсутнім або бути присутнім для кожного ключа, при цьому якщо атрибут відсутній, це не викликає накладних витрат на зберігання порожніх значень.

Список та назви груп колонок фіксований та має чітку схему. На рівні групи колонок задаються такі параметри як time to live (TTL) і максимальна кількість версій, що зберігаються. Якщо різниця між timestamp для певної версії і поточним часом більше TTL — запис позначається для видалення. Якщо кількість версій для певного атрибуту перевищила максимальну кількість версій, запис також позначається для видалення.

Модель даних Hbase можна запам'ятати, як відповідність ключ значення:

<table, RowKey, Column Family, Column, timestamp> -> Value

6.3 Підтримувані операції

Список підтримуваних операцій у hbase дуже простий. Підтримуються 4 основні операції:

  • Put : додати новий запис до hbase. Timestamp цього запису може бути заданий руками, інакше він буде встановлений автоматично як поточний час.
  • Get : отримати дані щодо певного RowKey. Можна вказати Column Family, з якої будемо брати дані та кількість версій, які хочемо прочитати.
  • Scan : читати записи по черзі. Можна вказати запис з яким починаємо читати, запис до якого читати, кількість записів, які необхідно вважати, Column Family з якої буде проводитися читання та максимальна кількість версій для кожного запису.
  • Delete : позначити певну версію для видалення. Фізичного видалення не відбудеться, воно буде відкладено до наступного Major Compaction (див. нижче).

6.4 Архітектура

HBase є розподіленою базою даних, яка може працювати на десятках та сотнях фізичних серверів, забезпечуючи безперебійну роботу навіть при виході з експлуатації деяких з них. Тому архітектура HBase досить складна в порівнянні з класичними реляційними базами даних.

HBase для своєї роботи використовує два основні процеси:

1. Region Server – обслуговує один або кілька регіонів. Регіон — це діапазон записів, що відповідають певному діапазону RowKey, що йдуть поспіль. Кожен регіон містить:

  • Persistent Storage – основне сховище даних у HBase. Дані фізично зберігаються на HDFS у спеціальному форматі HFile. Дані HFile зберігаються в відсортованому за RowKey порядку. Одній парі (регіон, column family) відповідає щонайменше один HFIle.
  • MemStore – буфер на запис. Так як дані зберігаються в HFile d відсортованому порядку - оновлювати HFile на кожен запис досить дорого. Натомість дані під час запису потрапляють у спеціальну область пам'яті MemStore, де накопичуються деякий час. При заповненні MemStore до деякого критичного значення дані записуються в новий HFile.
  • BlockCache – кеш на читання. Дозволяє суттєво економити час на даних, які читаються часто.
  • Write Ahead Log (WAL) . Оскільки дані під час запису потрапляють у memstore, існує певний ризик втрати даних через збій. Для того щоб цього не відбулося всі операції, перед власне здійснення маніпуляцій потрапляють у спеціальний лог-файл. Це дозволяє відновити дані після будь-якого збою.

2. Master Server - головний сервер у кластері HBase. Master керує розподілом регіонів по Region Server'ам, веде реєстр регіонів, керує запусками регулярних завдань та робить іншу корисну роботу.

Для координації дій між сервісами HBase використовує Apache ZooKeeper, спеціальний сервіс, призначений керувати конфігураціями та синхронізацією сервісів.

При збільшенні кількості даних у регіоні та досягненні ним певного розміру Hbase запускає split, операцію, що розбиває регіон на 2. Для того щоб уникнути постійних поділів регіонів, можна заздалегідь задати межі регіонів і збільшити їх максимальний розмір.

Так як дані по одному регіону можуть зберігатися в кількох HFile, для прискорення роботи Hbase періодично зливає їх воєдино. Ця операція в Hbase називається compaction. Compaction'и бувають двох видів:

  • Minor Compaction . Запускається автоматично, виконується у фоновому режимі. Має низький пріоритет у порівнянні з іншими операціями Hbase.
  • Major Compaction . Запускається руками або після спрацьовування певних тригерів (наприклад по таймеру). Має високий пріоритет та може суттєво уповільнити роботу кластера. Major Compaction'и краще робити під час коли навантаження на кластер невелике. Під час Major Compaction також відбувається фізичне видалення даних, рано помічених міткою tombstone.

6.5 Способи роботи з HBase

HBase Shell

Найпростіший спосіб розпочати роботу з Hbase – скористатися утилітою hbase shell. Вона доступна відразу після встановлення hbase на будь-якій ноді кластера hbase.

Hbase shell являє собою jruby-консоль з вбудованою підтримкою всіх основних операцій з роботи з Hbase. Нижче наведено приклад створення таблиці users із двома column family, виконання деяких маніпуляцій із нею та видалення таблиці в кінці на мові hbase shell:

create 'users', {NAME => 'user_profile', VERSIONS => 5}, {NAME => 'user_posts', VERSIONS => 1231231231} 
put 'users', 'id1', 'user_profile:name', 'alexander' 
put 'users', 'id1', 'user_profile:second_name', 'alexander' 
get 'users', 'id1' 
put 'users', 'id1', 'user_profile:second_name', 'kuznetsov' 
get 'users', 'id1' 
get 'users', 'id1', {COLUMN => 'user_profile:second_name', VERSIONS => 5} 
put 'users', 'id2', 'user_profile:name', 'vasiliy' 
put 'users', 'id2', 'user_profile:second_name', 'ivanov' 
scan 'users', {COLUMN => 'user_profile:second_name', VERSIONS => 5} 
delete 'users', 'id1', 'user_profile:second_name' 
get 'users', 'id1' 
disable 'users' 
drop 'users'

Native API

Як і більшість інших hadoop-related проектів hbase реалізований мовою java, тому й нативний api доступний мовою Java. Native API досить непогано документований на офіційному сайті. Ось приклад використання Hbase API взятий звідти:

import java.io.IOException;

import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

public class MyLittleHBaseClient {
  public static void main(String[] args) throws IOException {
	Configuration config = HBaseConfiguration.create();
	Connection connection = ConnectionFactory.createConnection(config);
	try {
  	Table table = connection.getTable(TableName.valueOf("myLittleHBaseTable"));
  	try {
    	Put p = new Put(Bytes.toBytes("myLittleRow"));
    	p.add(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"),
    	Bytes.toBytes("Some Value"));
    	table.put(p);

    	Get g = new Get(Bytes.toBytes("myLittleRow"));
    	Result r = table.get(g);
    	byte [] value = r.getValue(Bytes.toBytes("myLittleFamily"),
      	Bytes.toBytes("someQualifier"));

    	String valueStr = Bytes.toString(value);
    	System.out.println("GET: " + valueStr);

    	Scan s = new Scan();
    	s.addColumn(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"));
    	ResultScanner scanner = table.getScanner(s);
    	try {
       	for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
         	System.out.println("Found row: " + rr);
       	}
     	} finally {
       	scanner.close();
     	}
   	} finally {
     	if (table != null) table.close();
   	}
 	} finally {
   	connection.close();
 	}
  }
}

Thrift, REST та підтримка інших мов програмування

Для роботи з іншими мовами програмування Hbase надає Thrift API та Rest API. На базі них побудовані клієнти для всіх основних мов програмування: Python, PHP, Java Script і т.д.

6.6 Деякі особливості роботи з HBase

  1. Hbase «з коробки» інтегрується з MapReduce, і може бути використана як вхідні та вихідні дані за допомогою спеціальних TableInputFormat і TableOutputFormat.

  2. Дуже важливо правильно вибрати RowKey. RowKey повинен забезпечувати хороший рівномірний розподіл по регіонах, інакше є ризик виникнення так званих «гарячих регіонів» — регіонів, які використовуються набагато частіше за інших, що призводить до неефективного використання ресурсів системи.

  3. Якщо дані заливаються не одинично, а відразу великими пачками — Hbase підтримує спеціальний механізм BulkLoad, який дозволяє заливати дані набагато швидше, ніж використовуючи одиничні Put'и. BulkLoad по суті є двокроковою операцією:

    • Формування HFile без участі put'ів за допомогою спеціального MapReduce job'a
    • Підкладання цих файликів безпосередньо в Hbase
  4. Hbase підтримує виведення своїх метрик на сервер моніторингу Ganglia. Це може бути дуже корисно при адмініструванні Hbase для розуміння суті проблем, що відбуваються з hbase.

RowKey

В якості RowKey використовується ідентифікатор користувача, в якості якого використовується GUUID, рядок, що спеціально генерується таким чином, щоб бути унікальним у всьому світі. GUUID'и розподілені рівномірно, що дає хороший розподіл даних по серверам.

Column Family

У нашому сховищі використовуються дві column family:

  • Data. У цій групі колонок зберігаються дані, які втрачають актуальність для рекламних цілей, такі як факти відвідування користувачем певних URL. TTL на цю Column Family встановлено у розмірі 2 місяці, обмеження за кількістю версій – 2000.
  • LongData. У цій групі колонок зберігаються дані, які не втрачають своєї актуальності протягом тривалого часу, такі як стать, дата народження та інші «вічні» характеристики користувача.

Колонки

Кожен тип фактів про користувача зберігається окремій колонці. Наприклад, у колонці Data:_v зберігаються URL, відвідані користувачем, а колонці LongData:gender — підлогу користувача.

Як timestamp зберігається час реєстрації цього факту. Наприклад, у колонці Data:_v — як timestamp використовується час заходу користувачем певний URL.

Така структура зберігання даних дуже добре лягає на наш патерн використання і дозволяє швидко оновлювати дані про користувачів, швидко діставати всю необхідну інформацію про користувачів, і, використовуючи MapReduce, швидко обробляти дані про всіх користувачів відразу.

6.7 Альтернативи

HBase досить складна в адмініструванні та використанні, тому перш ніж використовувати HBase є сенс звернути увагу на альтернативи:

  • Реляційні бази даних . Дуже непогана альтернатива, особливо якщо дані влазять на одну машину. Також насамперед про реляційні бази даних варто подумати у разі, коли важливі транзакції індекси відмінні від первинного.

  • Key-Value сховища . Такі сховища, як Redis і Aerospike, краще підходять, коли необхідна мінімізація latency і менш важлива пакетна обробка даних.

  • Файли та їх обробка за допомогою MapReduce . Якщо дані лише додаються, і рідко оновлюються/змінюються, краще не використовувати HBase, а просто зберігати дані у файлух. Для спрощення роботи з файлуми можна скористатися такими інструментами як Hive, Pig та Impala.

Використання HBase виправдане коли:

  • Даних багато, і вони не влазять на один комп'ютер/сервер
  • Дані часто оновлюються та видаляються
  • У даних присутній явний «ключ» за яким зручно прив'язувати все інше
  • Потрібна пакетна обробка даних
  • Потрібен довільний доступ до даних за певними ключами