Розподіл даних (шардинг) – це процес поділу великих обсягів інформації на менші, керовані частини, які розміщуються на різних екземплярах MongoDB у розподіленому середовищі.
Що таке шардинг?
Шардинг у MongoDB є масштабованим рішенням, яке дозволяє зберігати значні масиви даних на багатьох серверах, а не обмежуватися одним.
З практичної точки зору, розміщення постійно зростаючих обсягів даних на одній машині є неможливим. Звернення до великого набору даних, що знаходиться на одному сервері, може призвести до перевантаження ресурсів і недостатньої пропускної здатності для операцій читання та запису.
Існує два основних підходи до масштабування систем для збільшення обсягу оброблюваних даних:
Вертикальне масштабування передбачає покращення продуктивності окремого сервера шляхом встановлення потужнішого процесора, збільшення обсягу оперативної пам’яті або розширення дискового простору. Однак, цей метод може мати обмеження у зв’язку з технічними можливостями сучасних апаратних конфігурацій.
Горизонтальне масштабування, навпаки, полягає в додаванні нових серверів і розподілі навантаження між ними. Оскільки кожен сервер обробляє лише частину даних, це забезпечує вищу ефективність та є економічно вигіднішим, ніж використання потужного дорогого обладнання. Проте, це створює потребу в додатковому обслуговуванні складної інфраструктури.
Шардинг у MongoDB використовує принцип горизонтального масштабування.
Компоненти шардингу
Для реалізації шардингу в MongoDB необхідні наступні компоненти:
Шард (Shard) – це екземпляр MongoDB, який обробляє частину вихідних даних. Шарди повинні бути розгорнуті як репліки.
Mongos – це екземпляр MongoDB, який діє як посередник між клієнтським застосунком та шардованим кластером. Він виконує роль маршрутизатора запитів до відповідних шардів.
Сервер конфігурації (Config Server) – це екземпляр MongoDB, який зберігає метадані та конфігураційні параметри кластера. MongoDB вимагає, щоб сервер конфігурації був розгорнутий як реплікаційний набір.
Архітектура шардингу
Кластер MongoDB складається з декількох реплікаційних наборів.
Кожен реплікаційний набір містить щонайменше 3 або більше екземплярів mongo. Шардований кластер може включати кілька шардів, і кожен шард працює в межах свого реплікаційного набору. Застосунок взаємодіє з Mongos, який, у свою чергу, комунікує з шардами. Тому, застосунки не взаємодіють безпосередньо з шардами. Маршрутизатор запитів розподіляє підмножини даних між вузлами на основі ключа шардингу.
Реалізація шардингу
Для налаштування шардингу виконайте наступні кроки:
Крок 1
- Запустіть сервер конфігурації в режимі реплікації та активуйте реплікацію між ними.
mongod --configsvr --port 27019 --replSet rs0 --dbpath C:datadata1 --bind_ip localhost
mongod --configsvr --port 27018 --replSet rs0 --dbpath C:datadata2 --bind_ip localhost
mongod --configsvr --port 27017 --replSet rs0 --dbpath C:datadata3 --bind_ip localhost
Крок 2
- Ініціалізуйте реплікаційний набір на одному з серверів конфігурації.
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] })
rs.initiate( { _id : "rs0", configsvr: true, members: [ { _id: 0, host: "IP:27017" }, { _id: 1, host: "IP:27018" }, { _id: 2, host: "IP:27019" } ] }) { "ok" : 1, "$gleStats" : { "lastOpTime" : Timestamp(1593569257, 1), "electionId" : ObjectId("000000000000000000000000") }, "lastCommittedOpTime" : Timestamp(0, 0), "$clusterTime" : { "clusterTime" : Timestamp(1593569257, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1593569257, 1) }
Крок 3
- Запустіть сервери шардів в режимі реплікації та ввімкніть реплікацію між ними.
mongod --shardsvr --port 27020 --replSet rs1 --dbpath C:datadata4 --bind_ip localhost
mongod --shardsvr --port 27021 --replSet rs1 --dbpath C:datadata5 --bind_ip localhost
mongod --shardsvr --port 27022 --replSet rs1 --dbpath C:datadata6 --bind_ip localhost
MongoDB ініціалізує перший сервер шардів як основний. Для зміни основного сервера шардів використовуйте метод movePrimary.
Крок 4
- Ініціалізуйте реплікаційний набір на одному з серверів шардів.
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] })
rs.initiate( { _id : "rs0", members: [ { _id: 0, host: "IP:27020" }, { _id: 1, host: "IP:27021" }, { _id: 2, host: "IP:27022" } ] }) { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1593569748, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1593569748, 1) }
Крок 5
- Запустіть Mongos для шардованого кластера
mongos --port 40000 --configdb rs0/localhost:27019,localhost:27018,localhost:27017
Крок 6
- Підключіться до сервера Mongos
mongo --port 40000
- Додайте сервери шардів.
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022")
sh.addShard( "rs1/localhost:27020,localhost:27021,localhost:27022") { "shardAdded" : "rs1", "ok" : 1, "operationTime" : Timestamp(1593570212, 2), "$clusterTime" : { "clusterTime" : Timestamp(1593570212, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Крок 7
- В оболонці mongo увімкніть шардинг для бази даних та колекцій.
- Увімкніть шардинг для бази даних
sh.enableSharding("geekFlareDB")
sh.enableSharding("geekFlareDB") { "ok" : 1, "operationTime" : Timestamp(1591630612, 1), "$clusterTime" : { "clusterTime" : Timestamp(1591630612, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Крок 8
- Для розділення колекції необхідний ключ шардингу (описано далі в статті).
Синтаксис: sh.shardCollection("dbName.collectionName", { "key" : 1 } )
sh.shardCollection("geekFlareDB.geekFlareCollection", { "key" : 1 } ) { "collectionsharded" : "geekFlareDB.geekFlareCollection", "collectionUUID" : UUID("0d024925-e46c-472a-bf1a-13a8967e97c1"), "ok" : 1, "operationTime" : Timestamp(1593570389, 3), "$clusterTime" : { "clusterTime" : Timestamp(1593570389, 3), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Якщо колекція не існує, створіть її таким чином.
db.createCollection("geekFlareCollection") { "ok" : 1, "operationTime" : Timestamp(1593570344, 4), "$clusterTime" : { "clusterTime" : Timestamp(1593570344, 5), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Крок 9
Вставте дані до колекції. Журнали MongoDB почнуть відображати активність балансувальника, що свідчить про його роботу з розподілу даних між шардами.
Крок 10
Останнім кроком є перевірка стану шардингу. Це можна зробити за допомогою команди, виконаної на вузлі Mongos.
Статус шардингу
Перевірте статус шардингу, виконавши команду на вузлі маршрутизації mongo.
sh.status()
mongos> sh.status() --- Sharding Status --- sharding version: { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5ede66c22c3262378c706d21") } shards: { "_id" : "rs1", "host" : "rs1/localhost:27020,localhost:27021,localhost:27022", "state" : 1 } active mongoses: "4.2.7" : 1 autosplit: Currently enabled: yes balancer: Currently enabled: yes Currently running: no Failed balancer rounds in last 5 attempts: 5 Last reported error: Could not find host matching read preference { mode: "primary" } for set rs1 Time of Reported error: Tue Jun 09 2020 15:25:03 GMT+0530 (India Standard Time) Migration Results for the last 24 hours: No recent migrations databases: { "_id" : "config", "primary" : "config", "partitioned" : true } config.system.sessions shard key: { "_id" : 1 } unique: false balancing: true chunks: rs1 1024 too many chunks to print, use verbose if you want to force print { "_id" : "geekFlareDB", "primary" : "rs1", "partitioned" : true, "version" : { "uuid" : UUID("a770da01-1900-401e-9f34-35ce595a5d54"), "lastMod" : 1 } } geekFlareDB.geekFlareCol shard key: { "key" : 1 } unique: false balancing: true chunks: rs1 1 { "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0) geekFlareDB.geekFlareCollection shard key: { "product" : 1 } unique: false balancing: true chunks: rs1 1 { "product" : { "$minKey" : 1 } } -->> { "product" : { "$maxKey" : 1 } } on : rs1 Timestamp(1, 0) { "_id" : "test", "primary" : "rs1", "partitioned" : false, "version" : { "uuid" : UUID("fbc00f03-b5b5-4d13-9d09-259d7fdb7289"), "lastMod" : 1 } } mongos>
Розподіл даних
Маршрутизатор Mongos розподіляє навантаження між шардами на основі ключа шардингу. Для рівномірного розподілу даних використовується балансувальник.
Ключовим компонентом для розподілу даних між шардами є:
- Балансувальник відіграє важливу роль у розподілі даних між шардами. Він запускається, коли Mongos починає розподіляти навантаження. Після запуску балансувальник прагне забезпечити більш рівномірний розподіл даних. Для перевірки стану балансувальника використовуйте sh.status(),
sh.getBalancerState()
абоsh.isBalancerRunning()
.
mongos> sh.isBalancerRunning() true mongos>
АБО
mongos> sh.getBalancerState() true mongos>
Після вставки даних, демон Mongos відображатиме активність, пов’язану з переміщенням фрагментів даних між шардами. Це означає, що балансувальник працює для забезпечення рівномірного розподілу даних. Активний балансувальник може впливати на продуктивність, тому рекомендується його запускати у визначений проміжок часу.
mongos> sh.status() --- Sharding Status --- sharding version: { "_id" : 1, "minCompatibleVersion" : 5, "currentVersion" : 6, "clusterId" : ObjectId("5efbeff98a8bbb2d27231674") } shards: { "_id" : "rs1", "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022", "state" : 1 } { "_id" : "rs2", "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025", "state" : 1 } active mongoses: "4.2.7" : 1 autosplit: Currently enabled: yes balancer: Currently enabled: yes Currently running: yes Failed balancer rounds in last 5 attempts: 5 Last reported error: Could not find host matching read preference { mode: "primary" } for set rs2 Time of Reported error: Wed Jul 01 2020 14:39:59 GMT+0530 (India Standard Time) Migration Results for the last 24 hours: 1024 : Success databases: { "_id" : "config", "primary" : "config", "partitioned" : true } config.system.sessions shard key: { "_id" : 1 } unique: false balancing: true chunks: rs2 1024 too many chunks to print, use verbose if you want to force print { "_id" : "geekFlareDB", "primary" : "rs2", "partitioned" : true, "version" : { "uuid" : UUID("a8b8dc5c-85b0-4481-bda1-00e53f6f35cd"), "lastMod" : 1 } } geekFlareDB.geekFlareCollection shard key: { "key" : 1 } unique: false balancing: true chunks: rs2 1 { "key" : { "$minKey" : 1 } } -->> { "key" : { "$maxKey" : 1 } } on : rs2 Timestamp(1, 0) { "_id" : "test", "primary" : "rs2", "partitioned" : false, "version" : { "uuid" : UUID("a28d7504-1596-460e-9e09-0bdc6450028f"), "lastMod" : 1 } } mongos>
- Ключ шардингу визначає логіку розподілу документів шардованої колекції між шардами. Це може бути індексоване поле або складене індексоване поле, яке повинно бути присутнє в кожному документі колекції. Дані розділяються на частини, і кожна частина пов’язується з ключем шардингу на основі діапазону. Маршрутизатор, на основі діапазону ключа, визначає, на якому шарді зберігати фрагмент даних.
Ключ шардингу обирається з огляду на п’ять основних характеристик:
- Кардинальність
- Розподіл записів
- Розподіл читання
- Ціль читання
- Локальність читання
Оптимальний ключ шардингу дозволяє MongoDB рівномірно розподіляти навантаження між усіма шардами. Вибір правильного ключа шардингу є надзвичайно важливим.
Зображення: MongoDB
Видалення вузла шарда
Перед видаленням шарда з кластера необхідно забезпечити безпечну міграцію даних на інші шарди. MongoDB автоматично переміщує дані на інші вузли перед видаленням потрібного вузла.
Виконайте наведені нижче кроки для видалення шарда.
Крок 1
Спочатку потрібно визначити ім’я хоста шарда, який необхідно видалити. Команда нижче покаже список усіх шардів в кластері та їхній стан.
db.adminCommand( { listShards: 1 } )
mongos> db.adminCommand( { listShards: 1 } ) { "shards" : [ { "_id" : "rs1", "host" : "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022", "state" : 1 }, { "_id" : "rs2", "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025", "state" : 1 } ], "ok" : 1, "operationTime" : Timestamp(1593572866, 15), "$clusterTime" : { "clusterTime" : Timestamp(1593572866, 15), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Крок 2
Виконайте наступну команду, щоб видалити потрібний шард з кластера. Після цього, балансувальник автоматично перемістить дані з шарда, що видаляється, на інші вузли та збалансує розподіл даних між ними.
db.adminCommand( { removeShard: "shardedReplicaNodes" } )
mongos> db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } ) { "msg" : "draining started successfully", "state" : "started", "shard" : "rs1", "note" : "you need to drop or movePrimary these databases", "dbsToMove" : [ ], "ok" : 1, "operationTime" : Timestamp(1593572385, 2), "$clusterTime" : { "clusterTime" : Timestamp(1593572385, 2), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Крок 3
Щоб перевірити стан видалення шарда, виконайте ту саму команду ще раз.
db.adminCommand( { removeShard: "rs1/127.0.0.1:27020,127.0.0.1:27021,127.0.0.1:27022" } )
Необхідно дочекатися завершення процесу зливу даних. Поля msg та state вказують на статус процесу, як показано нижче:
"msg" : "draining ongoing", "state" : "ongoing",
Статус можна також перевірити за допомогою команди sh.status()
. Після успішного видалення, шард не буде відображатись у виводі. Під час процесу зливу даних, його стан буде “draining: true”.
Крок 4
Продовжуйте перевірку стану зливу до повного видалення шарда.
Після завершення процесу, команда відобразить повідомлення та статус “completed”.
"msg" : "removeshard completed successfully", "state" : "completed", "shard" : "rs1", "ok" : 1,
Крок 5
Нарешті, необхідно перевірити список шардів, що залишились в кластері, за допомогою sh.status()
або db.adminCommand( { listShards: 1 } )
mongos> db.adminCommand( { listShards: 1 } ) { "shards" : [ { "_id" : "rs2", "host" : "rs2/127.0.0.1:27023,127.0.0.1:27024,127.0.0.1:27025", "state" : 1 } ], "ok" : 1, "operationTime" : Timestamp(1593575215, 3), "$clusterTime" : { "clusterTime" : Timestamp(1593575215, 3), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } } }
Як видно, видалений шард відсутній у списку.
Переваги шардингу над реплікацією
- В реплікації основний вузол обробляє усі операції запису, тоді як вторинні вузли використовуються для резервного копіювання або для операцій читання. В шардингу, навантаження рівномірно розподіляється між кількома серверами, включно з реплікаційними наборами.
- Реплікаційний набір обмежений 12 вузлами, тоді як у шардингу немає обмежень щодо кількості шардів.
- Реплікація вимагає високопродуктивного обладнання або вертикального масштабування для обробки великих обсягів даних, що є дорогим. Шардинг дозволяє розширювати можливості системи шляхом додавання додаткових серверів.
- Реплікація дозволяє покращити продуктивність читання шляхом додавання додаткових вторинних вузлів, тоді як у шардингу, продуктивність як читання, так і запису зростає за рахунок додавання нових вузлів.
Обмеження шардингу
- Шардований кластер не підтримує унікальне індексування шардів, якщо ключ шардингу не є частиною унікального індексу.
- Всі операції оновлення в шардованій колекції повинні включати ключ шардингу або поле _id у запиті.
- Колекції можуть бути шардовані, якщо їх розмір не перевищує визначеного порогу. Цей поріг розраховується на основі середнього розміру всіх ключів шардингу та налаштованого розміру фрагментів.
- Шардинг має обмеження щодо максимального розміру колекції або кількості розділень.
- Неправильний вибір ключів шардингу може призвести до зниження продуктивності.
Висновок
MongoDB надає вбудовану підтримку шардингу для створення великих баз даних без зниження продуктивності. Сподіваємося, вищенаведена інформація допоможе вам налаштувати шардинг у MongoDB. Для подальшого вивчення можна ознайомитись з переліком часто використовуваних команд MongoDB.