English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
مقدمة
في MongoDB (إصدار 3.2.9)، يمثل مجموعة الشظايا (sharded cluster) طريقة لتوسيع قوة نظام البيانات بشكل أفقي، يمكن من خلالها تخزين مجموعة البيانات بشكل توزيعي على شظايا مختلفة، حيث يحفظ كل shard جزءًا من مجموعة البيانات فقط، يضمن MongoDB عدم وجود بيانات مكررة بين الشظايا المختلفة، ويعادل مجموع البيانات المحفوظة في جميع الشظايا مجموعة البيانات الكاملة. يمكن لتخزين مجموعة البيانات بشكل توزيعي توزيع العبء على شظايا متعددة، حيث يتولى كل shard مسؤولية قراءة وكتابة جزء من البيانات فقط، مما يعزز استخدام موارد shard ويحسن معدل تدفق النظام البياني.
يتم تقسيم مجموعة البيانات إلى قطع بيانات (chunk)، يحتوي كل قطعة على عدة doc، يتم تخزين قطع البيانات بشكل توزيعي في مجموعة الشظايا. يتحمل MongoDB تتبع توزيع قطع البيانات على الشظايا، ما هي قطع البيانات التي يتم تخزينها في كل shard، تسمى بيانات الشظية، يتم حفظها في قاعدة بيانات config على server التكوين، عادة ما يتم استخدام 3 servers تكوين، يجب أن تكون قاعدة بيانات config في جميع servers تكوين متطابقة. يمكن للـ mongos الوصول مباشرة إلى قاعدة بيانات config، لمراجعة بيانات الشظية؛ يقدم mongo shell دالة مساعدة sh، يمكن استخدامها لمراجعة بيانات الشظية لمجموعة الشظايا بشكل آمن.
للبحث عن shard أي، سيتم الحصول فقط على مجموعة بيانات المجموعة في shard الحالي، وليس مجموعة البيانات بأكملها. يجب على التطبيق فقط الاتصال بمونغوس، وإجراء عمليات القراءة والكتابة عليه، سيقوم mongos تلقائيًا بتوجيه طلبات القراءة والكتابة إلى shard المناسب. يجعل MongoDB تحقيق التشابك الأساسي للشظايا للتطبيق شفافًا، يبدو التطبيق أن لديه الوصول إلى مجموعة البيانات بأكملها.
أولاً، shard الرئيسي
في cluster الشق، ليست كل مجموعة تخزن بشكل موزع، فقط بعد استخدام الأمر sh.shardCollection() لتحديد shard للمجموعة بشكل صريح، سيتم تخزين المجموعة في shard مختلفة. للمجموعات غير المشقطة (un-sharded collection)، سيتم تخزين بياناتها فقط في shard الرئيسي (Primary shard)، بشكل افتراضي، يكون shard الرئيسي هو shard تم إنشاء قاعدة البيانات الأولي، ويستخدم لتخزين بيانات مجموعات الشق غير المشقطة في قاعدة البيانات. لدى كل قاعدة بيانات shard رئيسي.
لدي كل قاعدة بيانات في cluster الشق shard رئيسي يحتوي على جميع مجموعات الشق غير المشقطة لهذه القاعدة البيانات. لدى كل قاعدة بيانات shard رئيسي خاص بها.
على سبيل المثال، إذا كان لديك cluster شق يحتوي على ثلاثة shards: shard1،shard2،shard3، وأنت تقوم بإنشاء قاعدة بيانات blog في shard1، إذا قمت بشق قاعدة بيانات blog، فإن MongoDB سيقوم تلقائيًا بإنشاء قاعدة بيانات blog بنفس التركيب في shard2،shard3، ويكون shard الرئيسي لـ قاعدة بيانات blog هو shard1.
الشكل، shard الرئيسي لـ Collection2 هو ShardA.
استخدام الأمر movePrimary لتغيير shard الرئيسي للقاعدة البيانات الافتراضي، ستنقل مجموعات الشق غير المشتتة من shard الحالي إلى shard الرئيسي الجديد.
db.runCommand( { movePrimary : "test", to : "shard0001" } )
بعد تغيير mongos الأمر movePrimary لتحديد shard الرئيسي للقاعدة البيانات، تكون معلومات التكوين في server config أحدث، وتصبح معلومات التكوين المخزنة في mongos قديمة.يقدم MongoDB الأمر: flushRouterConfig للإجبار mongos على الحصول على معلومات التكوين الأحدث من server config، و刷新 مخزن mongos.
db.adminCommand({"flushRouterConfig":1})
ثانيًا، بيانات الشق
لا تقم بالوصول مباشرة إلى server config للتحقق من بيانات معلومات cluster الشق، هذه البيانات مهمة جدًا، الطريقة الآمنة هي الاتصال بمخدم البيانات via mongos للتحقق، أو استخدام الوظيفة الإضافية sh للتحقق.
استخدام وظيفة المساعدة sh لمراجعة
sh.status()
الاتصال بـ mongos لمراجعة مجموعات قاعدة البيانات config
mongos> use config
1، مجموعة shards تحفظ معلومات الفصوص
db.shards.find()
تخزن بيانات shard في replica set المحدد بواسطة host أو mongod المستقل.
{ "_id" : "shard_name", "host" : "replica_set_name/host:port", "tag":[shard_tag1,shard_tag2] }
2، مجموعة databases تحفظ معلومات جميع قواعد البيانات في مجموعة الفصوص، بغض النظر عما إذا كانت قاعدة البيانات مفصلة أم لا
db.databases.find()
إذا قمت بتشغيل sh.enableSharding(“db_name”) على قاعدة البيانات، فإن قيمة حقل partitioned ستكون true؛ ويحدد حقل primary الفص الأساسي (primary shard) للقاعدة البيانات.
{ "_id" : "test", "primary" : "rs0", "partitioned" : true }
3، مجموعة collections تحفظ معلومات جميع المجموعات المفصلة، وليس تشمل المجموعات غير المفصلة (un-sharded collections)
key هي مفتاح الفص
db.collections.find() { "_id" : "test.foo", "lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16"), "lastmod" : ISODate("1970-02-19T17:02:47.296Z"), "dropped" : false, "key" : { "_id" : 1 }, "unique" : true }
4، مجموعة chunks تحفظ معلومات قطع البيانات،
ns: مجموعة الفصوص، بنية: db_name.collection_name
min و max: القيمة الأدنى والقيمة الأعلى لملك الكتلة
shard: الكتلة الموجودة على الفص
db.chunks.find() { "_id" : "test.foo-_id_MinKey", "lastmod" : Timestamp(1, 1), "lastmodEpoch" : ObjectId("57dcd4899bd7f7111ec15f16"), "ns" : "test.foo", "min" : {}} "_id" : 1 }, "max" : { "_id" : 3087 }, "shard" : "rs0" }
5،جمعية السجلات تسجل العمليات في مجموعة الشظايا، بما في ذلك تقسيم الشظية وانتقالها، إضافة أو حذف الشظية
حقل what: يوضح نوع العملية، مثل: multi-split يمثل تقسيم الشظية،
"what" : "addShard", "what" : "shardCollection.start", "what" : "shardCollection.end", "what" : "multi-split",
6،تسجل العلامات tag للشظية والنطاق المتبادل للشفتة
{ "_id" : { "ns" : "records.users", "min" : { "zipcode" : "10001" } }, "ns" : "records.users", "min" : { "zipcode" : "10001" }, "max" : { "zipcode" : "10281" }, "tag" : "NYC" }
7،جمعية الإعدادات تسجل حالة الحاجز وحجم الشظية، الحجم الافتراضي للشظية هو 64MB.
{"_id" : "chunksize", "value" : 64 } {"_id" : "balancer", "stopped" : false }
8،جمعية القفل تسجل قفل موزع (distributed lock)، تضمن أن يتمكن فقط مثيل mongos واحد من تنفيذ المهام الإدارية في مجموعة الشظايا.
عندما يكون mongos في دور الحاجز، فإنه يحصل على قفل ويضيف doc إلى config.locks.
جمعية القفل تتحفظ على قفل موزع. هذا يضمن أن يتمكن فقط مثيل mongos واحد من أداء المهام الإدارية على المجموعة في وقت واحد. mongos الذي يعمل كحاجز يأخذ قفل عن طريق إدراج مستند يشبه التالي في جمعية القفل.
{ "_id" : "balancer" "process" : "example.net:40000:1350402818:16807", "state" : 2, "ts" : ObjectId("507daeedf40e1879df62e5f3"), "when" : ISODate("2012-10-16T19:01:01.593Z"), "who" : "example.net:40000:1350402818:16807:Balancer:282475249", "why" : "doing balance round" }
ثالثًا،حذف الشريحة
عند حذف الشريحة،يجب التأكد من أن البيانات في الشريحة قد تم نقلها إلى شريحة أخرى،بالنسبة للمجموعات المفردة،استخدم معادلة التوزيع لنقل البيانات المهاجرة،بالنسبة للمجموعات غير المفردة،يجب تعديل الشريحة الرئيسية للمجموعة.
1،حذف بيانات المجموعات المفردة
step1،تأكد من تشغيل معادلة التوزيع
sh.setBalancerState(true);
step2،تحويل جميع المجموعات المفردة إلى شريحة أخرى
use admin db.adminCommand({"removeShard":"shard_name"})
أمر removeShard سيمسح البيانات المهاجرة من الشريحة الحالية ويقوم بتحويلها إلى شريحة أخرى،إذا كان هناك الكثير من البيانات المهاجرة في الشريحة،فقد يستغرق عملية التحويل وقتًا طويلاً.
step3،فحص حالة تحويل البيانات المهاجرة
use admin db.runCommand( { removeShard: "shard_name" } )
استخدام الأمر removeShard يمكن الرؤية حالة تحويل البيانات المهاجرة،حقل remaining يعبر عن عدد البيانات المتبقية
{ "msg" : "draining ongoing", "state" : "ongoing", "remaining" : { "chunks" : 42, "dbs" : 1 }, "ok" : 1 }
step4،البيانات المهاجرة قد اكتملت
use admin db.runCommand( { removeShard: "shard_name" } ) { "msg" : "removeshard completed successfully", "state" : "completed", "shard" : "shard_name", "ok" : 1 }
2. حذف قاعدة البيانات غير المشروعة
الخطوة 1، عرض قاعدة البيانات غير المشروعة
قاعدة البيانات غير المشروعة تشمل جزءين:
1. لم يتم تشرع القاعدة البيانات، لم يتم استخدام sh.enableSharding(“db_name”)، في قاعدة البيانات config، هو حقل partitioned هو false
2. هناك مجموعة غير مشرعة في القاعدة البيانات، أي أن shard الحالي هو shard الرئيسي لهذه المجموعة
استخدام الإعدادات db.databases.find({$or:[{"partitioned":false},{"primary":"shard_name"}]})
بالنسبة للقواعد البيانات التي لها partitioned=false، يتم حفظ جميع البيانات في shard الحالي؛ بالنسبة للقواعد البيانات التي لها partitioned=true، primary="shard_name"، فإن ذلك يعني أن هناك مجموعة غير مشرعة (un-sharded collection) مخزنة في هذه القاعدة البيانات، ويجب تغيير shard الرئيسي لهذه المجموعات.
الخطوة 2، تعديل shard الرئيسي للقاعدة البيانات
db.runCommand( { movePrimary: "db_name", to: "new_shard" })
رابعاً، زيادة التشريح
بما أن التخزين الموزع يحتوي على جزء من مجموعة البيانات، لضمان توفر البيانات العالية، يُنصح باستخدام Replica Set كـ shard، حتى لو كان يحتوي Replica Set على عضو واحد فقط. الاتصال بمونغوس، باستخدام دالة sh المساعدة لزيادة التشريح.
sh.addShard("replica_set_name/host:port")
لا يُنصح بتحويل standalone mongod إلى shard
sh.addShard("host:port")
خامساً، jumbo chunk
في بعض الحالات، يمكن أن ينمو chunk بشكل مستمر، يتجاوز الحد المحدد لـ chunk size، ويصبح jumbo chunk، السبب في وجود jumbo chunk هو أن جميع المستندات في chunk تستخدم نفس مفتاح الشريحة (shard key)، مما يجعل mongoDB غير قادر على تقسيم هذا chunk، وإذا استمر هذا chunk في النمو، فإن ذلك سيؤدي إلى توزيع غير متساوٍ للchunk، ويصبح عائق أداء.
في عملية نقل chunk، هناك قيود: حجم chunk لا يمكن أن يتجاوز 25,000 مستند، أو 1.3 ضعف القيمة المعدلة. القيمة المعدلة الافتراضية لـ chunk size هي 64MB، وسيتم علامة chunk الذي يتجاوز الحد بالـ jumbo chunk، ولا يمكن لمongoDB نقل jumbo chunk إلى shard آخر.
MongoDB cannot move a chunk if the number of documents in the chunk exceeds either 250000 documents or 1.3 times the result of dividing the configured chunk size by the average document size.
1,查看特大块
使用sh.status(),能够发现特大块,特大块的后面存在 jumbo 标志
{ "x" : 2 } -->> { "x" : 3 } on : shard-a Timestamp(2, 2) jumbo
2,分发特大块
特大块不能拆分,不能通过均衡器自动分发,必须手动分发。
step1,关闭均衡器
sh.setBalancerState(false)
step2,增大Chunk Size的配置值
由于MongoDB不允许移动大小超出限制的特大块,因此,必须临时增加chunk size的配置值,再将特大块均衡地分发到分片集群中。
استخدام الإعدادات db.settings.save({"_id":"chunksize","value":"1024"})
step3,移动特大块
sh.moveChunk("db_name.collection_name",{sharded_filed:"value_in_chunk"},"new_shard_name")
step4,启用均衡器
sh.setBalancerState(true)
step5,刷新mongos的配置缓存
强制mongos从config server同步配置信息,并刷新缓存。
use admin db.adminCommand({ flushRouterConfig: 1 } )
六,均衡器
均衡器是由mongos转变的,就是说,mongos不仅负责将查询路由到相应的shard上,还要负责数据块的均衡。一般情况下,MongoDB会自动处理数据均衡,通过config.settings能够查看balancer的状态,或通过sh辅助函数查看
sh.getBalancerState()
返回true,表示均衡器在正运行,系统自动处理数据均衡,使用sh辅助函数能够关闭balancer
sh.setBalancerState(false)
لا يمكن إنهاء عملية نقل قطع البيانات التي تشغلها balancer بشكل فوري، عند تحويل mongos إلى balancer، يتم طلب قفل balancer، وتحديد config.locks.
استخدام الإعدادات db.locks.find({"_id":"balancer"}) --أو sh.isBalancerRunning()
إذا كان state=2، فإن ذلك يعني أن التوازن يعمل حاليًا، وإذا كان state=0، فإن ذلك يعني أن التوازن تم إغلاقه.
عملية التوازن هي في الواقع نقل قطع البيانات من shard إلى shard آخر، أو تقسيم chunk كبير إلى قطع صغيرة، ثم نقل القطع الصغيرة إلى shard آخر، حيث أن نقل القطع وتقسيمها يزيد من حمل الوحدة العتادية، من الأفضل أن يتم تحديد وقت نشاط التوازن في وقت فراغ النظام، يمكن تعيين نافذة نشاط التوازن لتحديد وقت تقسيم وتنقل قطع البيانات.
استخدام الإعدادات db.settings.update( {"_id":"balancer"}, "$set":{"activeWindow":{"start":"23:00","stop":"04:00"}}), صحيح )
الوحدات التي يتم تقسيمها وتحريكها هي chunk، يضمن التوازن في عدد chunk على كل shard، ولكن ليس بالضرورة أن يكون عدد الوثائق في كل chunk متساوٍ. قد يكون هناك بعض chunk تحتوي على عدد كبير من الوثائق، بينما قد تحتوي بعض chunk على عدد قليل من الوثائق، أو حتى لا تحتوي على أي وثائق. لذلك، يجب أن يتم اختيار مفتاح الشريحة بحذر، أي مفتاح الشريحة، إذا كان يمكن أن يلبي معظم احتياجات الاستعلام، ويجعل توزيع عدد الوثائق متساوياً، فإن هذا الحقل هو خيار أفضل لمفتاح الشريحة.
الخاتمة
هذا هو نهاية محتوى هذا المقال، آمل أن يكون قد ساعدكم في التعلم أو العمل، إذا كان لديكم أي أسئلة، يمكنكم ترك تعليقات للتفاعل.