Як налаштувати Dragonfly Cluster: покроковий гайд
Всім привіт! Мене звати Борис і я Principal Engineer у компанії DragonflyDB. У цій статті я хочу показати вам, як запустити Dragonfly Cluster, і надати короткий огляд внутрішніх процесів Dragonfly Cluster.
Dragonfly відзначається високою продуктивністю та вертикальним масштабуванням, що робить його чудовим вибором для вимогливих сучасних робочих навантажень з даними. Незабаром Dragonfly Cluster також запропонує горизонтальне масштабування, що ще більше розширить його можливості.
Примітка: На момент написання Dragonfly Cluster офіційно ще не опублікований. Однак всі функції і команди, які описані в цій статті, вже доступні в основній гілці Dragonfly. Ми активно тестуємо та покращуємо цей функціонал.
Огляд Dragonfly Cluster
Як і Redis Cluster, Dragonfly Cluster досягає горизонтального масштабування за допомогою шардінгу. Кластер складається з одного чи більше шардів, кожен з яких містить мастер вузол і нуль або більше реплік. Дані розподіляються між шардами за допомогою підходу на основі слотів.
- Простір хешування в кластері ділиться на 16384 слоти.
- Кожен ключ хешується в слот. Хеш-слот обчислюється з назви ключа за допомогою алгоритму CRC16 з подальшим взяттям модуля 16384.
- Кожен вузол у кластері відповідає за певну частину цих слотів.
Dragonfly Cluster підтримує динамічне балансування без простоїв. Слоти можуть безперешкодно мігрувати з одного вузла на інший, що дозволяє додавати, видаляти або змінювати розмір вузлів без переривання їх роботи.
Dragonfly Cluster підтримує операції з декількома ключами, за умови, що всі задіяні ключі (в команді з декількома ключами, в транзакції або в Lua-скрипті) знаходяться в одному слоті. Для цього Dragonfly використовує хештеги. З хештегами система обчислює слот виключно на основі вмісту ключа у фігурних дужках «{hash-tag}real_key» . Цей механізм дозволяє користувачам явно контролювати розподіл ключів між шардами.
Якщо клієнт запитує ключі у вузла, але ці ключі належать до слота, яким керує інший вузол, клієнт отримує помилку перенаправлення -MOVED. Це гарантує, що клієнт завжди зможе знайти правильний вузол, який обробляє запитувані ключі.
Dragonfly надає лише сервер, без панелі керування для розгортання кластера. Моніторинг стану вузлів, автоматичне перемикання на резервні вузли та перерозподіл слотів не входять до функціональності Dragonfly-бекенда і будуть надаватись як частина сервісу Dragonfly Cloud.
Dragonfly Cluster пропонує безперешкодну міграцію для існуючих клієнтів Redis Cluster. Він повністю дотримується поведінки Redis Cluster з погляду клієнта, забезпечуючи нульові зміни в коді. Однак Dragonfly використовує принципово інший підхід до управління кластером.
На відміну від моделі розподіленого консенсусу в Redis Cluster, Dragonfly застосовує централізовану стратегію управління. Вузли працюють незалежно, без прямого спілкування або спільного стану. Це рішення забезпечує єдине джерело істини, а також є більш простим, надійним і швидким.
Режими Кластера
Dragonfly підтримує два режими кластера.
Режим емуляції кластера (можна увімкнути за допомогою `—cluster_mode=emulated`) повністю сумісний із безкластерним режимом роботи, підтримуючи команди SELECT і операції з декількома ключами, а також надаючи можливість використовувати команди кластеру, такі як CLUSTER SHARDS.
Цей режим працює як одиничний екземпляр Dragonfly і не включає горизонтальне масштабування, перерозподіл шардів або деякі інші розширені функції кластера. Цей режим підходить для:
- Розробки та тестового середовища: надає моживість швидкого розгортання середовища розробки.
- Міграції: служить як проміжне рішення при переході від безкластерного до кластерного режиму роботу.
- Ресурсообмежених сценаріїв: емуляційний режим кластера може оптимізувати використання ресурсів, функціонуючи як репліка для декількох шардів, що дозволяє одному вузлу ефективно реплікувати кілька кластерних вузлів.
Багатовузловий режим кластера (можна увімкнути за допомогою `—cluster_mode=yes`) — це той режим Dragonfly Cluster, який нас цікавить більше. Він має певні обмеження в порівнянні з безкластерним або емуляційним режимами:
- Заборонена команда SELECT.
- Всі ключі в операції з декількома ключами (команди з декількома ключами, транзакції та Lua-скрипти) повинні належати до одного слоту. В іншому випадку повертається помилка -CROSSSLOT.
Dragonfly підтримує деякі команди CLUSTER для сумісності з клієнтами Redis Cluster. Ці команди переважно надають інформаційні дані про налаштування кластера:
- CLUSTER HELP: перелічує доступні команди CLUSTER.
- CLUSTER MYID: повертає ідентифікатор вузла.
- CLUSTER SHARDS: відображає інформацію про шарди кластера.
- CLUSTER SLOTS: перелічує всі слоти та їхні відповідні вузли.
- CLUSTER NODES: показує інформацію про всі вузли кластера.
- CLUSTER INFO: надає загальну інформацію про кластер.
Управління Кластером Dragonfly
Команди DFLYCLUSTER
Команди DFLYCLUSTER є специфічними для Dragonfly та надають розширені можливості управління кластером:
- DFLYCLUSTER CONFIG: управляє ролями вузлів, розміщенням слотів і процесами міграції.
- DFLYCLUSTER GETSLOTINFO: надає детальну статистику використання слотів, включаючи кількість ключів, використання пам’яті та операції читання/запису.
- DFLYCLUSTER FLUSHSLOTS: видаляє дані з певних слотів.
- DFLYCLUSTER SLOT-MIGRATION-STATUS: відстежує прогрес міграції слотів, вказуючи на поточний стан і статус завершення.
Створення Кластера
Щоб розпочати створення кластера Dragonfly, спочатку запустимо два окремих екземпляри Dragonfly у режимі кластера. Кожен екземпляр матиме унікальний ідентифікатор (ID).
$> ./dragonfly --cluster_mode=yes --admin_port=31001 --port 30001
$> ./dragonfly --cluster_mode=yes --admin_port=31002 --port 30002
Після запуску екземплярів нам потрібно отримати їхні унікальні ідентифікатори за допомогою наступних команд (усі команди DFLYCLUSTER мають виконуватись через admin_port):
$> redis-cli -p 31001 CLUSTER MYID
$> redis-cli -p 31002 CLUSTER MYID
У моєму випадку два унікальні ідентифікатори мають префікси 97486c... і 728cf2... відповідно. Тепер ми можемо створити конфігурацію кластера у форматі JSON, підставивши ці унікальні ID та IP-адреси обох вузлів.
[
{
"slot_ranges": [
{
"start": 0,
"end": 999
}
],
"master": {
"id": "97486c9d7e0507e1edb2dfba4655224d5b61c5e2",
"ip": "localhost",
"port": 30001
},
"replicas": []
},
{
"slot_ranges": [
{
"start": 1000,
"end": 16383
}
],
"master": {
"id": "728cf25ecd4d1230805754ff98939321d72d23ef",
"ip": "localhost",
"port": 30002
},
"replicas": []
}
]
За допомогою команди DFLYCLUSTER CONFIG цей JSON надсилається на обидва наші вузли, і Dragonfly-кластер вже готовий.
Міграція слотів
Міграція слотів є критично важливою операцією, яка може призвести до втрати даних, якщо не використовувати її обережно. Вона є необхідною для налаштування конфігурації кластера відповідно до зміни вимог. Dragonfly підтримує одночасні міграції слотів, але в будь-який момент може бути запущена лише одна міграція між двома конкретними вузлами. Це означає, що можна одночасно ініціювати кілька міграцій між різними парами вузлів у кластері.
Щоб ініціювати та завершити міграцію слотів, скористайтеся командою DFLYCLUSTER CONFIG, вказавши додаткове поле migrations у конфігурації JSON. У наведеному нижче прикладі я вирішив перемістити слоти [1000, 8000] на інший вузол, і ось як виглядає нова JSON-конфігурація:
[
{
"slot_ranges": [
{
"start": 0,
"end": 999
}
],
"master": {
"id": "97486c9d7e0507e1edb2dfba4655224d5b61c5e2",
"ip": "localhost",
"port": 30001
},
"replicas": []
},
{
"slot_ranges": [
{
"start": 1000,
"end": 16383
}
],
"master": {
"id": "728cf25ecd4d1230805754ff98939321d72d23ef",
"ip": "localhost",
"port": 30002
},
"replicas": [],
"migrations": [
{
"slot_ranges": [
{
"start": 1000,
"end": 8000
}
],
"node_id": "97486c9d7e0507e1edb2dfba4655224d5b61c5e2",
"ip": "localhost",
"port": 31001
}
]
}
]
Щоб підтримувати узгодженість кластера під час міграції слотів, команда DFLYCLUSTER CONFIG має бути відправлена на всі вузли, навіть на ті, що не беруть безпосередньої участі в процесі. Це забезпечує актуальність конфігурації кластера на всіх вузлах, що запобігає виникненню невідповідностей, які можуть виникнути через одночасні процеси міграції або збої. Щоб відстежувати прогрес міграції слотів, скористайтеся наступною командою:
$> redis-cli -p 31002 DFLYCLUSTER SLOT-MIGRATION-STATUS
Як тільки статус міграції покаже FINISHED, нову конфігурацію кластера (з оновленими діапазонами слотів) треба застосувати до всіх вузлів:
[
{
"slot_ranges": [
{
"start": 0,
"end": 8000
}
],
"master": {
"id": "97486c9d7e0507e1edb2dfba4655224d5b61c5e2",
"ip": "localhost",
"port": 30001
},
"replicas": []
},
{
"slot_ranges": [
{
"start": 8001,
"end": 16383
}
],
"master": {
"id": "728cf25ecd4d1230805754ff98939321d72d23ef",
"ip": "localhost",
"port": 30002
},
"replicas": []
}
]
Після застосування нової конфігурації кластера дані з мігрованих слотів назавжди видаляються з вихідного вузла. Міграцію можна скасувати, видаливши поле міграції з JSON-конфігурації, зберігши при цьому розміщення слотів. Якщо оновлена конфігурація не буде застосована своєчасно після завершення міграції, кластер знаходитиметься в перехідному стані, де вузли матимуть невідповідну інформацію про слоти. Тому клієнти спочатку можуть бути перенаправлені на вихідний вузол для мігрованих слотів, але подальші запити до цього вузла будуть правильно маршрутизовані до цільового вузла.
Хоча ця тимчасова невідповідність існує, вона не загрожує цілісності даних.
Репліки
Реплікація в Dragonfly налаштовується за допомогою стандартної команди `REPLICAOF`, аналогічно до налаштувань у некластерних конфігураціях. Незважаючи на те, що деталі реплікації включені в конфігурацію кластера, репліки функціонують незалежно, копіюючи дані безпосередньо з мастер-вузла. Це означає, що репліки копіюють всі дані з головного вузла, незалежно від призначення слотів.
Міграція слотів під мікроскопом
Dragonfly використовує набір внутрішніх команд з префіксом DFLYMIGRATE для управління процесом міграції слотів. Процес міграції слотів включає кілька ретельно скоординованих кроків для забезпечення цілісності даних і безперебійної роботи при зміні вузлів.
- Ініціювання міграції: процес починається з команди DFLYCLUSTER CONFIG, яка відправляється на вихідний і цільовий вузли для налаштування параметрів міграції.
- Підготовка цільового вузла: вихідний вузол відправляє команду DFLYMIGRATE INIT [SOURCE_NODE_ID, SHARDS_NUM, SLOT_RANGES] на цільовий вузол. Цільовий вузол відповідає OK, що свідчить про готовність приймати дані.
- Налаштування передачі даних: для кожного шард-потоку (потік, який має виключне право на частину даних) вихідний вузол відправляє команду DFLYMIGRATE FLOW [SOURCE_NODE_ID, FLOW_ID]. Кожна команда DFLYMIGRATE FLOW встановлює з’єднання для передачі даних, що підтверджується відповіддю OK.
- Передача даних: використовуючи встановлені з’єднання командами DFLYMIGRATE FLOW, вихідний вузол серіалізує та передає дані на цільовий вузол.
- Завершення міграції: після передачі даних мігровані слоти на вихідному вузлі блокуються, щоб запобігти подальшої зміни даних. Після цього вихідний вузол відправляє маркер фіналізації у кожному з’єднанні створенному DFLYMIGRATE FLOW, щоб завершити передачу даних.
- Завершення процесу: вихідний вузол відправляє команду DFLYMIGRATE ACK [SOURCE_NODE_ID, ATTEMPT_ID] цільовому вузлу, щоб остаточно завершити міграцію. Цільовий вузол порветає у відповіді ATTEMPT_ID, завершуючи міграцію. ATTEMPT_ID використовується для обробки помилок, які можуть виникнути під час завершення процесу.
Хоча більшість кроків у процесі міграції є досить простими, четвертий крок вимагає детальнішого пояснення через його складність. У Dragonfly існують два джерела даних, які необхідно відправити на цільовий вузол: знімок (snapshot) і журнал. Нижче ми детальніше розглянемо ці два джерела даних.
Створення знімка
Для створення знімка Dragonfly проходить через кожен шард даних (частина даних, яким керує тільки один потік), серіалізуючи дані. За відсутності запитів на запис це лінійний процес, де кожен сегмент даних серіалізується по черзі. У процес включаються періодичні паузи, щоб дозволити системі обробляти запити користовучів, мінімізуючи вплив на базову роботу системи.
Процес стає більш складним, коли під час серіалізації виникають запити на запис. На відміну від Redis, який використовує механізм fork для запобігання змінам даних під час серіалізації, Dragonfly застосовує більш складний механізм, включно з версіонуванням та серіалізуванням даних перед оновленням. Щоб створювати знімки без зростання використання пам’яті або затримок.
Серіалізація журналу
Під час створення знімка Dragonfly також керує журналом, який фіксує всі нещодавні операції запису. Ці записи журналу серіалізуються і відправляються на цільовий вузол разом із даними знімка.
Розглянемо невеликий приклад для ілюстрації процесу:
- Є кілька записів даних: A, B, C і D.
- Дані A і B серіалізуються і відправляються на цільовий вузол.
- Клієнт виконує команду MSET, оновлюючи записи B і D.
- Оскільки B вже був серіалізований раніше, ми тимчасово нічого з ним не робимо.
- Дані D серіалізуються і відправляються на цільовий вузол.
- Журнал отримує інформацію про оновлення B і D, серіалізує її і відправляє на цільовий вузол.
- Нарешті, дані C серіалізуються і відправляються на цільовий вузол.
Дотримуючись цього процесу, Dragonfly гарантує, що цільовий вузол отримує узгоджену версію даних з вихідного вузла, включаючи всі нещодавні операції запису, виконані під час процесу міграції слотів.
Висновок
Dragonfly Cluster є потужним інструментом, що пропонує горизонтальне масштабування навіть для найвимогливіших робочих навантажень. Сучасні сервери можуть бути оснащені понад сотнею ядер і кількома сотнями гігабайт пам’яті, тому вертикальне масштабування повинно бути у пріоритеті, якщо це можливо. Перед впровадженням кластера рекомендується оцінити потенціал вертикального масштабування.
Якщо ви не впевнені в потребах майбутнього вертикального масштабування, можна розпочати з емуляції кластера та перейти до справжнього кластера при зростанні вимог.
Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів