1. Відновлення даних
Касандра підтримує три механізми відновлення даних:
читання з відновленням (read repair) — під час читання дані запитуються зі всіх реплік та порівнюються вже після завершення координації. Та колонка, яка має останню мітку часу, пошириться на вузли, де мітки застарілі.
спрямованого відправлення (hinted handoff) — дозволяє зберігати інформацію про операції запису на координаторі в тому випадку, якщо запис на якийсь із вузлів не вдався. Пізніше, коли це буде можливо, запис повториться. Дозволяє швидко проводити відновлення даних у разі короткострокової відсутності вузла в кластері. До того ж, за рівня узгодженості ANY дозволяє добитися повної доступності для запису (absolute write availability), коли навіть всі вузли реплік недоступні, операція запису підтверджується, а дані зберігаються на вузлі-координаторі.
анти-ентропійне відновлення вузла (anti-entropy node repair) — це певний процес відновлення всіх реплік, який має запускатися регулярно вручну за допомогою команди “nodetool repair” і дозволяє підтримувати кількість реплік усіх даних, які можливо не були відновлені першими двома способами, на потрібному рівні реплікації.
1. Запис на диск
Коли дані надходять на вузол після координації безпосередньо для запису, то вони потрапляють до двох структур даних: до таблицы в пам'яті (memtable) та до журналу закріплення (commit log). Таблиця в пам'яті існує для кожного колоночного сімейства і дозволяє запам'ятати значення моментально. Технічно це хеш-таблиця (hashmap) з можливістю одночасного доступу (concurrent access) на основі структури даних, яка називається “списками з пропусками” (skip list). Журнал закріплення один на весь простір ключів і зберігається на диску. Журнал являє собою послідовність операцій модифікації. Також він розбивається на частини, коли досягає певного розміру.
Така організація дозволяє зробити швидкість запису обмеженою швидкістю послідовного запису на жорсткий диск і водночас гарантувати довговвчність даних (data durability). Журнал закріплення в разі аварійної зупинки вузла читається при старті сервісу Касандри та відновлює всі таблиці в пам'яті. Виходить, що швидкість впирається під час послідовного запису на диск, а в сучасних жорстких дисків це близько 100МБ/с. З цієї причини журнал закріплення радять винести на окремий дисковий носій.
Зрозуміло, що рано чи пізно пам'ять може заповнитися. Тому таблицю в пам'яті також необхідно зберегти на диск. Для визначення моменту збереження існує обмеження обсягу, який зайнято таблицями в пам'яті (memtable_total_spacein_mb), за замовчуванням це ⅓ максимального розміру купи Java (Java heapspace). Під час заповнення таблицями в пам'яті обсягу більшого за це обмеження, Касандра створює нову таблицю та записує стару таблицю в пам'яті на диск у вигляді збереженої таблиці (SSTable). Збережена таблиця після створення більше ніколи не модифікується (is immutable). Коли відбувається збереження на диск, частини журналу закріплення позначаються як вільні, і таким чином вивільняють місце на диску, яке займає журнал. Варто врахувати, що журнал має переплетену структуру з даних різних колоночних сімейств у просторі ключів, і якісь частини можуть бути не вивільнені, оскільки деяким областям будуть відповідними інші дані, які досі знаходяться в таблицях у пам'яті.
У підсумку для кожного колоночного сімейства є одна відповідна таблиця в пам'яті та певне число збережених таблиць. Тепер, коли вузол опрацьовує запис читання, йому необхідно зробити запит на всі ці структури та обрати останнє за міткою часу значення. Для прискорення цього процесу існує три механізми: блум-фільтрація (bloom filter), кеш ключів (key cache) та кеш записів (record cache):
- блум-фільтр — це структура даних, яка займає небагато місця і дозволяє дати відповідь на питання: чи міститься елемент (а в нашому випадку це ключ) у множині чи ні. Водночас, якщо відповідь — “ні”, то це 100%, а якщо відповідь “так”, то це, можливо, хибно-позитивна відповідь. Це дозволяє зменшити кількість читань зі збережених таблиць;
- кеш ключів зберігає позицію на диску запису для кожного ключа, таким чином зменшуючи кількість операцій позиціонування (seek operations) під час пошуку за збереженою таблицею;
- кеш записів зберігає запис цілком, дозволяючи повністю позбутися операцій читання з диску.
3. Ущільнення
У певний момент часу дані в колоночному сімействі перезапишуться — прийдуть колонки, які будуть мати те ж ім'я та ключ. Тобто виникне ситуація, коли в більш старій збереженій таблиці і більш новій будуть міститися старі й нові дані.
Щоб гарантувати цілісність, Касандра зобов'язана читати всі ці збережені таблиці та обирати дані з останньою міткою часу. Виходить, що кількість операцій позиціонування жорсткого диску під час читання пропорційна кількості збережених таблиць.
Тому щоб звільнити перезаписані дані та зменшити кількість збережених таблиць, існує процес ущільнення (compaction). Він читає послідовно декілька збережених таблиць та записує нову збережену таблицю, в якій об'єднані дані за мітками часу. Коли таблиця повністю записана і введена в користування, Касандра може звільнити таблиці-джерела (таблицями, які її утворили).
Таким чином, якщо таблиці містили перезаписані дані, ця надлишковість усувається. Зрозуміло, що під час такої операції обсяг надлишковості збільшується — нова збережена таблиця існує на диску разом із таблицями-джерелами, а це означає, що обсяг місця на диску завжди має бути такий, щоб можна було виконати ущільнення.
Касандра дозволяє обрати одну з двох стратегій проведення ущільнення:
стратегія ущільнення збережених таблиць, пов'язаних розміром (size-tiered compaction) — ця стратегія ущільнює певним чином дві обрані таблиці. Застосовується автоматично у вигляді фонового ущільнення (minor compaction) і в ручному режимі, для повного ущільнення (major compaction). Допускає ситуацію надходження ключа у багатьох таблицях і, відповідно, вимагає виконати операцію пошуку для кожної такої таблиці.
стратегія ущільнення збережених таблиць рівнями (leveled compaction) — ущільнює збережені таблиці, які початково створюються невеликими — 5 МБ, за допомогою їх групування в рівні. Кожен рівень у 10 разів більший за попередній. Водночас існують такі гарантії: 90% запитів читання будуть відбуватися до одної збереженої таблиці, і лише 10% простору на диску буде використовуватися під застарілі дані. У цьому випадку для виконання ущільнення під часову таблицю достатньо лише 10-кратного розміру таблиці, тобто 50 Мб.
4. Операції видалення
З точки зору внутрішнього влаштування, операції видалення колонок — це операції запису спеціального значення — значення, що затирає (tombstone). Коли таке значення виходить в результаті читання, то воно пропускається, ніби такого значення ніколи й не існувало. В результаті ж ущільнення такі значення поступово витісняють застарілі реальні значення і, вірогідно, зовсім зникають. Якщо ж з'являться колонки з реальними даними з іще новішими мітками часу, то вони перетруть, врешті-решт, і ці значення, які затирають.
5. Транзакційність
Касандра підтримує транзакційність на рівні одного запису, тобто для набору колонок з одним ключем. Ось як виконуються чотири вимоги ACID:
- атомарність (atomicity) — усі колонки в одному записі за одну операцію будуть або записані, або ні;
- узгодженість (consistency) — як вже зазначено вище, є можливість використовувати запити зі строгою узгодженістю замість доступності, і тим самим виконувати цю вимогу;
- ізольованість (isolation) — починаючи з Касандри версії 1.1, з'явилася підтримка ізольованості, коли під час запису колонок одного запису інший користувач, який читає цей самий запис, побачить або повністю стару версію запису або, вже після завершення операції, нову версію, а не частину колонок з одної і частину з другої;
- довговічність (durability) забезпечується наявністю журналу закріплення, який відтвориться та відновить вузол до потрібного стану в разі якої-небудь відмови.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ