Тази публикация в блога е публикувана на Hortonworks.com преди сливането с Cloudera. Някои връзки, ресурси или препратки може вече да не са точни.
Тази публикация в блога първоначално се появи тук и е възпроизведена изцяло тук.
HBase е разпределена база данни, изградена около основните концепции на подреден журнал за запис и дърво за сливане, структурирано от лог. Както при всяка база данни, оптимизираният I/O е критична грижа за HBase. Когато е възможно, приоритетът е изобщо да не се извършва I/O. Това означава, че използването на паметта и структурите за кеширане са от изключително значение. За тази цел HBase поддържа две кеш структури:„паметно хранилище“ и „блоков кеш“. Съхранение на паметта, реализирано като MemStore
, натрупва редакции на данни при получаването им, като ги буферира в паметта (1). Блоковият кеш, реализация на BlockCache
интерфейс, запазва блоковете данни резидентни в паметта, след като бъдат прочетени.
MemStore
е важно за достъп до последните редакции. Без MemStore
, достъпът до тези данни, както са записани в регистъра за запис, ще изисква четене и десериализиране на записи обратно от този файл, поне O(n)
операция. Вместо това MemStore
поддържа структура на skiplist, която се радва на O(log n)
цена за достъп и не изисква дисков вход/изход. MemStore
съдържа само малка част от данните, съхранявани в HBase, обаче.
Обслужването чете от BlockCache
е основният механизъм, чрез който HBase може да обслужва произволни четения с латентност от милисекунди. Когато блок от данни се чете от HDFS, той се кешира в BlockCache
. Последващите четения на съседни данни – данни от същия блок – не понасят I/O санкцията за повторно извличане на тези данни от диск (2). Това е BlockCache
това ще бъде оставащият фокус на тази публикация.
Блокиране към кеша
Преди да разберете BlockCache
, помага да се разбере какво точно представлява „блокът“ на HBase. В контекста на HBase блокът е единична I/O единица. Когато записвате данни в HFile, блокът е най-малката записана единица данни. По същия начин, един блок е най-малкото количество данни, което HBase може да прочете обратно от HFile. Внимавайте да не объркате HBase блок с HDFS блок или с блоковете на основната файлова система – всички те са различни (3).
HBase блоковете се предлагат в 4 разновидности: DATA
, META
, INDEX
и BLOOM
.
DATA
блокове съхраняват потребителски данни. Когато BLOCKSIZE
е указан за семейство колони, това е намек за този вид блок. Имайте предвид, това е само намек. Докато промивате MemStore
, HBase ще направи всичко възможно, за да спази тази насока. След всяка Cell
е написано, записващият проверява дали записаната сума е>=целевата BLOCKSIZE
. Ако е така, ще затвори текущия блок и ще стартира следващия (4).
INDEX
и BLOOM
блоковете служат на същата цел; и двете се използват за ускоряване на пътя за четене. INDEX
блоковете предоставят индекс върху Cell
се съдържат в DATA
блокове. BLOOM
блоковете съдържат филтър за разцвет над едни и същи данни. Индексът позволява на читателя бързо да разбере къде е Cell
трябва да се съхранява. Филтърът казва на четеца кога е Cell
определено липсва в данните.
И накрая META
блокове съхраняват информация за самия HFile и друга различна информация – метаданни, както може да очаквате. По-изчерпателен преглед на форматите HFile и ролите на различните типове блокове е предоставен в Apache HBase I/O – HFile.
HBase BlockCache и неговите реализации
Има един BlockCache
екземпляр в сървър на региона, което означава, че всички данни от всички региони, хоствани от този сървър, споделят един и същ кеш пул (5). BlockCache
се инстанцира при стартиране на сървъра на региона и се запазва за целия живот на процеса. Традиционно HBase предоставя само един BlockCache
реализация: LruBlockCache
. Изданието 0.92 въведе първата алтернатива в HBASE-4027: SlabCache
. HBase 0.96 въведе друга опция чрез HBASE-7404, наречена BucketCache
.
Основната разлика между изпитания LruBlockCache
и тези алтернативи са начинът, по който управляват паметта. По-конкретно LruBlockCache
е структура от данни, която се намира изцяло в JVM купчината, докато другите две могат да се възползват от паметта извън JVM купчината. Това е важно разграничение, тъй като паметта на JVM heap се управлява от JVM Garbage Collector, докато другите не са. В случаите на SlabCache
и BucketCache
, идеята е да се намали напрежението на GC, изпитвано от процеса на сървъра на региона, като се намали броят на обектите, задържани в хийпа.
LruBlockCache
Това е реализацията по подразбиране. Блоковете данни се кешират в JVM heap, използвайки тази реализация. Той е разделен на три области:с единичен достъп, с множество достъп и в паметта. Областите са с размери 25%, 50%, 25% от общия BlockCache
размер, съответно (6). Блок, първоначално прочетен от HDFS, се попълва в областта за единичен достъп. Последователните достъпи насърчават този блок в зоната за множествен достъп. Зоната в паметта е запазена за блокове, заредени от семейства колони, обозначени като IN_MEMORY
. Независимо от площта, старите блокове се изхвърлят, за да се освободи място за нови блокове с помощта на най-малко използван алгоритъм, оттук и „Lru“ в „LruBlockCache“.
SlabCache
Тази реализация разпределя области от памет извън JVM купчината с помощта на DirectByteBuffer
с. Тези области предоставят тялото на този BlockCache
. Точната област, в която ще бъде поставен конкретен блок, се основава на размера на блока. По подразбиране се разпределят две области, които консумират съответно 80% и 20% от общия конфигуриран размер на кеша извън паметта. Първият се използва за кеширане на блокове, които са приблизително размера на целевия блок (7). Последният съдържа блокове, които са приблизително 2 пъти размера на целевия блок. Блок се поставя в най-малката зона, където може да се побере. Ако кешът срещне блок, по-голям, отколкото може да се побере в която и да е област, този блок няма да бъде кеширан. Като LruBlockCache
, блоковото изгонване се управлява с помощта на алгоритъм на LRU.
BucketCache
Тази реализация може да бъде конфигурирана да работи в един от трите различни режима: heap
, offheap
и file
. Независимо от режима на работа, BucketCache
управлява области от паметта, наречени „кофи“ за задържане на кеширани блокове. Всяка кофа се създава с размер на целеви блок. heap
реализацията създава тези кофи в JVM heap; offheap
реализацията използва DirectByteByffers
да управлявате кофи извън JVM heap; file
режимът очаква път до файл във файловата система, в която са създадени кофите. file
режимът е предназначен за използване с резервно хранилище с ниска латентност – файлова система в паметта или може би файл, намиращ се на SSD съхранение (8). Независимо от режима, BucketCache
създава 14 кофи с различни размери. Той използва честотата на блокиране на достъпа, за да информира използването, точно както LruBlockCache
, и има същата разбивка на единичен достъп, многократен достъп и в паметта от 25%, 50%, 25%. Също като кеша по подразбиране, изгонването на блокове се управлява с помощта на LRU алгоритъм.
Кеширане на много нива
И двете SlabCache
и BucketCache
са проектирани да се използват като част от стратегия за кеширане на много нива. По този начин, част от общия BlockCache
размерът е разпределен на LruBlockCache
екземпляр. Този екземпляр действа като кеш от първо ниво, „L1“, докато другият екземпляр на кеша се третира като кеш от второ ниво, „L2“. Въпреки това, взаимодействието между LruBlockCache
и SlabCache
е различно от начина на LruBlockCache
и BucketCache
взаимодействат.
SlabCache
стратегия, наречена DoubleBlockCache
, е винаги да кеширате блокове както в L1, така и в L2 кеша. Двете нива на кеша работят независимо:и двете се проверяват при извличане на блок и всяко изхвърля блоковете, без да се съобразява с другото. BucketCache
стратегия, наречена CombinedBlockCache
, използва L1 кеша изключително за Bloom и Index блокове. Блоковете данни се изпращат директно в L2 кеша. В случай на изваждане на L1 блок, вместо да бъде изхвърлен изцяло, този блок се понижава до L2 кеша.
Кое да избера?
Има две причини да помислите за активиране на една от алтернативните BlockCache
реализации. Първото е просто количеството RAM, което можете да посветите на сървъра на региона. Мъдростта на общността признава, че горната граница на JVM heap, що се отнася до сървъра на региона, е някъде между 14GB и 31GB (9). Точната граница обикновено зависи от комбинация от хардуерен профил, конфигурация на клъстера, формата на таблиците с данни и моделите за достъп до приложения. Ще разберете, че сте влезли в опасната зона, когато GC спре и RegionTooBusyException
и започнете да наводнявате вашите регистрационни файлове.
Другият път, когато трябва да обмислите алтернативен кеш, е когато забавянето на отговора действително въпроси. Поддържането на купчината около 8-12GB позволява на CMS колектора да работи много гладко (10), което има измеримо влияние върху 99-ия персентил от времената за реакция. Като се има предвид това ограничение, единственият избор е да се проучи алтернативен колектор за боклук или да се завърти една от тези реализации извън купчина.
Тази втора опция е точно това, което направих. В следващата си публикация ще споделя някои ненаучни, но информативни резултати от експеримента, където сравнявам времената за отговор за различни BlockCache
реализации.
Както винаги, бъдете на линия и продължавайте с HBase!
1: MemStore
натрупва редакции на данни при получаването им, буферирайки ги в паметта. Това служи за две цели:увеличава общото количество данни, записани на диска с една операция, и запазва последните промени в паметта за последващ достъп под формата на четене с ниска латентност. Първият е важен, тъй като поддържа блоковете за запис в HBase приблизително в синхрон с размерите на HDFS блокове, подравнявайки моделите за достъп до HBase с подлежащото HDFS хранилище. Последното се разбира от само себе си, като улеснява заявките за четене на наскоро написани данни. Струва си да се отбележи, че тази структура не участва в трайността на данните. Редакциите също се записват в подредения регистър на запис, HLog
, което включва операция за добавяне на HDFS на конфигурируем интервал, обикновено незабавен.
2:Препрочитането на данни от локалната файлова система е най-добрият сценарий. В крайна сметка HDFS е разпределена файлова система, така че в най-лошия случай е необходимо четене на този блок през мрежата. HBase прави всичко възможно да поддържа локализиране на данните. Тези две статии предоставят задълбочен поглед върху това какво означава локализиране на данни за HBase и как се управлява.
3:Файловата система, HDFS и HBase блоковете са различни, но свързани. Съвременната I/O подсистема представлява много слоеве на абстракция върху абстракцията. Ядрото на тази абстракция е концепцията за единична единица данни, наричана „блок“. Следователно и трите слоя за съхранение дефинират свой собствен блок, всеки със собствен размер. Като цяло, по-големият размер на блока означава повишена пропускателна способност за последователен достъп. По-малкият размер на блока улеснява по-бърз произволен достъп.
4:Поставяне на BLOCKSIZE
проверката след запис на данните има две разклонения. Една Cell
е най-малката единица данни, записана в DATA
блок. Означава също Cell
не може да обхваща няколко блока.
5:Това е различно от MemStore
, за който има отделен екземпляр за всеки регион, хостван от регионалния сървър.
6:До съвсем скоро тези дялове на паметта бяха статично дефинирани; нямаше начин да се отмени разделението 25/50/25. Даден сегмент, например зоната за мулти достъп, може да нарасне по-голям от 50% разпределението, стига другите зони да са недостатъчно използвани. Повишеното използване в другите области ще изхвърли вписванията от зоната с много достъп до достигане на баланса 25/50/25. Операторът не може да промени тези размери по подразбиране. HBASE-10263, доставян в HBase 0.98.0, въвежда конфигурационни параметри за тези размери. Гъвкавото поведение се запазва.
7:„Приблизително“ бизнесът е да се позволи малко място за раздвижване в размери на блокове. Размерът на блока HBase е груба цел или намек, а не строго наложено ограничение. Точният размер на всеки конкретен блок от данни ще зависи от размера на целевия блок и размера на Cell
стойности, съдържащи се в него. Подсказката за размера на блока е посочена като размер на блока по подразбиране от 64 kb.
8:Използване на BucketCache
в file
режимът с постоянен резервен магазин има още едно предимство:постоянство. При стартиране той ще търси съществуващи данни в кеша и ще провери валидността им.
9:Както разбирам, има два компонента, които съветват горната граница на този диапазон. Първо е ограничение на адресируемостта на JVM обект. JVM може да препраща към обект в хийпа с 32-битов относителен адрес вместо пълния 64-битов собствен адрес. Тази оптимизация е възможна само ако общият размер на хепа е по-малък от 32GB. Вижте Компресиран Ами сега за повече подробности. Втората е способността на събирача на боклук да бъде в крак с количеството изхвърляне на обекти в системата. От това, което мога да кажа, трите източника на отмяна на обекти са MemStore
, BlockCache
и мрежови операции. Първият е смекчен от MemSlab
функция, активирана по подразбиране. Вторият е повлиян от размера на вашия набор от данни спрямо размера на кеша. Третото не може да се помогне, стига HBase да използва мрежов стек, който разчита на копиране на данни.
10:Точно както при 8, това предполага „модерен хардуер“. Взаимодействията тук са доста сложни и далеч извън обхвата на една публикация в блог.