JavaRush /Курси /All lectures for UA purposes /Apache Cassandra: зберігання даних у кластері

Apache Cassandra: зберігання даних у кластері

All lectures for UA purposes
Рівень 1 , Лекція 638
Відкрита

1. Розподіл даних

Розглянемо, як дані розподіляються залежно від ключа вузлами кластера (cluster nodes). Кассандра дозволяє ставити стратегію розподілу даних. Перша така стратегія розподіляє дані залежно від md5 значення ключа — випадковий розмітник (random partitioner). Друга враховує саме бітове представлення ключа — порядковий розмітник (byte-ordered partitioner).

Перша стратегія здебільшого дає більше переваг, оскільки тобі не потрібно дбати про рівномірний розподіл даних між серверами та подібні проблеми. Другу стратегію використовують у поодиноких випадках, наприклад, якщо необхідні інтервальні запити (range scan). Важливо зауважити, що вибір цієї стратегії проводиться перед створенням кластера і фактично не може бути змінено без перезавантаження даних.

Для розподілу даних Кассандра використовує техніку, відому як узгоджене хешування (consistent hashing). Цей підхід дозволяє розподілити дані між вузлами і зробити так, що під додавання та видалення нового вузла кількість даних, що пересилаються, була невеликою. Для цього кожному вузлу ставиться у відповідність мітка (token), яка розбиває на частини безліч всіх md5 значень ключів. Оскільки в більшості випадків використовується RandomPartitioner, розглянемо його.

Як я вже казав, RandomPartitioner обчислює 128-бітний md5 для кожного ключа. Для визначення, в яких вузлах будуть зберігатися дані, просто перебираються всі мітки вузлів від меншого до більшого, і коли значення мітки стає більше, ніж значення md5 ключа, цей вузол разом з деякою кількістю наступних вузлів (в порядку міток) вибирається для збереження. Загальна кількість обраних вузлів має дорівнювати рівню реплікації (replication factor). Рівень реплікації встановлюється для кожного простору ключів і дозволяє регулювати надмірність даних (data redundancy).

Перед тим, як додати вузол до кластера, необхідно встановити для нього мітку. Від того, який відсоток ключів покриває проміжок між цією міткою та наступною, залежить, скільки даних буде зберігатися на вузлі. Весь набір міток кластера називається кільцем (ring).

Ось ілюстрація, що відображає за допомогою вбудованої утиліти nodetool кільце кластера з 6 вузлів з рівномірно розподіленими мітками.

2. Узгодженість даних під час запису

Вузли кластера Кассандри рівноцінні, і клієнти можуть з'єднуватися з будь-яким із них, як для запису, так і для читання. Запити проходять стадію координації, під час якої, з'ясувавши за допомогою ключа та розмітника, на яких вузлах повинні розташовуватись дані, сервер надсилає запити до цих вузлів. Будемо називати вузол, який виконує координацію, координатором (coordinator), а вузли, які вибрані для збереження запису з цим ключем — вузлами-реплік (replica nodes). Фізично координатором може бути один із вузлів-реплік – це залежить тільки від ключа, розмітника та міток.

Для кожного запиту як на читання, так і на запис є можливість встановити рівень узгодженості даних.

Для запису цей рівень впливатиме на кількість вузлів-реплік, з яких очікується підтвердження вдалого закінчення операції (дані записалися) перед тим, як повернути користувачеві управління. Для запису є такі рівні узгодженості:

  • ONE — координатор шле запити всім вузлам-реплікам, але, дочекавшись підтвердження від першого ж вузла, повертає керування користувачеві;
  • TWO — те саме, але координатор чекає на підтвердження від двох перших вузлів, перш ніж повернути управління;
  • THREE — аналогічно, але координатор чекає на підтвердження від трьох перших вузлів, перш ніж повернути управління;
  • QUORUM — збирається кворум: координатор чекає на підтвердження запису від більш ніж половини вузлів-реплік, а саме round(N / 2) + 1, де N — рівень реплікації;
  • LOCAL_QUORUM — координатор чекає підтвердження від більш ніж половини вузлів-реплік у тому самому центрі обробки даних, де розташований координатор (для кожного запиту потенційно свій). Дозволяє позбутися затримок, пов'язаних з пересиланням даних до інших центрів обробки даних. Питання роботи з багатьма центрами обробки даних розглядаються у цій статті побіжно;
  • EACH_QUORUM — кооринатор чекає підтвердження від більш ніж половини вузлів-реплік у кожному центрі обробки даних незалежно;
  • ALL — координатор чекає на підтвердження від усіх вузлів-реплік;
  • ANY — дозволяє записати дані, навіть якщо всі вузли-репліки не відповідають. Координатор чекає або першої відповіді від одного з вузлів-реплік, або коли дані збережуться за допомогою направленої відправки (hinted handoff) координатору.

3. Узгодженість даних під час читання

Для читання рівень узгодженості впливатиме на кількість вузлів-реплік, з яких читатиметься. Для читання є такі рівні узгодженості:

  • ONE — координатор надсилає запити до найближчого вузла-репліки. Інші репліки також читаються з метою читання з виправленням (read repair) із заданою конфігурації кассандри ймовірністю;
  • TWO — те саме, але координатор шле запити до двох найближчих вузлів. Вибирається значення, яке має велику мітку часу;
  • THREE — аналогічно до попереднього варіанту, але з трьома вузлами;
  • QUORUM — збирається кворум, тобто координатор шле запити до більш ніж половини вузлів-реплік, а саме round(N/2) + 1, де N — рівень реплікації;
  • LOCAL_QUORUM — збирається кворум у тому центрі обробки даних, у якому відбувається координація, та повертаються дані з останньою міткою часу;
  • EACH_QUORUM — координатор повертає дані після збору кворуму в кожному центрі обробки даних;
  • ALL — координатор повертає дані після прочитання з усіх вузлів-реплік.

Таким чином, можна регулювати тимчасові затримки операцій читання, запису та налаштовувати узгодженість (tune consistency), а також доступність (availability) кожної з видів операцій. По суті, доступність безпосередньо залежить від рівня узгодженості операцій читання та запису, оскільки він визначає, скільки вузлів-реплік може вийти з ладу, і при цьому ці операції все ще будуть підтверджені.

Якщо кількість вузлів, з яких приходить підтвердження про запис, у сумі з числом вузлів, з яких відбувається читання, більша, ніж рівень реплікації, то у нас є гарантія, що після запису нове значення завжди буде прочитане, і це називається строгою узгодженістю (strong consistency). За відсутності строгої узгодженості існує можливість, що операція читання поверне застарілі дані.

У будь-якому випадку, значення зрештою пошириться між репліками, але вже після того, як закінчиться координаційне очікування. Таке поширення називається підсумковою узгодженістю (eventual consistency). Якщо не всі вузли-репліки будуть доступні під час запису, то рано чи пізно будуть задіяні засоби відновлення, такі як читання з виправленням та антиентропійне відновлення вузла (anti-entropy node repair). Про це трохи згодом.

Таким чином, при рівні узгодженості QUORUM на читання та на запис завжди підтримуватиметься строга узгодженість, і це буде певний баланс між затримкою операції читання та запису. При записі ALL, а читанні ONE буде сувора узгодженість, і операції читання будуть виконуватися швидше і будуть мати більшу доступність, тобто кількість вузлів, що вийшли з ладу, при якому читання все ще буде виконано, може бути більшим, ніж при QUORUM.

Для операцій запису потрібні всі робочі вузли-реплік. При записі ONE, читанні ALL теж буде сувора узгодженість, і операції запису виконуватимуться швидше і доступність запису буде великою, адже буде достатньо підтвердити лише, що операція запису пройшла хоча б на одному із серверів, а читання — повільніше і вимагатиме всіх вузлів-реплік . Якщо ж до програми немає вимоги про сувору узгодженість, то з'являється можливість прискорити операції читання та операції запису, а також поліпшити доступність за рахунок виставлення менших рівнів узгодженості.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ