MongoDB: التكرار: بنية التوافر العالي
آخر تحديث: 2026-08-26
تُعد مجموعات النسخ المتماثلة حجر الزاوية في توفر خدمة MongoDB العالي — وإتقان التعامل معها يتيح لك إنشاء مجموعات تشغيلية تتمتع بتوفر يبلغ 99.99%.
1. ما ستتعلمه
- مفهوم مجموعة النسخ المتماثلة (الأساسية / الثانوية / الوسيط)
- آلية الانتخاب (استنادًا إلى Raft)
- مزامنة البيانات (المزامنة الأولية / تتبع سجل العمليات)
- الفصل بين القراءة والكتابة (readPreference)
- نشر مجموعة نسخ متماثلة مكونة من 3 عقد باستخدام Docker
- التحويل التلقائي والاستعادة
2. بنية مجموعة النسخ المتماثلة
نظرة عامة على المفهوم: تُعد مجموعة النسخ المتماثلة (Replica Set) الأساس الذي تقوم عليه بنية التوافر العالي في MongoDB. وهي تتألف من عدة عمليات mongod — عقدة أساسية واحدة + N عقد ثانوية + عقدة تحكيم اختيارية. تقبل العقدة الأساسية جميع عمليات الكتابة، التي يتم نسخها بشكل غير متزامن إلى العقد الثانوية عبر سجل العمليات (oplog)، مما يضمن تكرار البيانات والتحويل التلقائي في حالة الفشل.
كيفية العمل: يقوم الخادم الأساسي بتسجيل كل عملية كتابة في سجل عمليات (oplog) (وهو مجموعة ذات حجم ثابت ومحدود)، بينما يقوم الخادم الثانوي باستمرار بسحب عمليات الكتابة وإعادة تنفيذها من خلال متابعة سجل عمليات (oplog) للحفاظ على اتساق البيانات مع الخادم الأساسي. وعندما يتعطل الخادم الأساسي، تنتخب العقد المتبقية خادمًا أساسيًا جديدًا باستخدام بروتوكول انتخاب (مستند إلى Raft)، ويقوم التطبيق بإعادة الاتصال تلقائيًّا، مما يحقق نسبة توفر تبلغ 99.99%.
بروتوكول Raft وآلية الانتخاب: تعتمد عملية الانتخاب في MongoDB على نسخة معدلة من بروتوكول Raft، حيث تتمثل القاعدة الأساسية في «حكم الأغلبية» — فلا يمكن أن يصبح «الرئيسي» (Primary) سوى المرشح الذي يحصل على أصوات من أكثر من نصف العقد. ويضمن ذلك وجود «رئيسي» واحد على الأكثر في أي وقت (لمنع حدوث انقسام الدماغ)، وأن «الرئيسي» الجديد يمتلك البيانات الأكثر اكتمالاً.
graph TB
App[Applications] --> P[Primary<br/>Master Node<br/>Accept all posts]
P -.Copy oplog.-> S1[Secondary 1<br/>From node<br/>Readable+Backup]
P -.Copy oplog.-> S2[Secondary 2<br/>From node<br/>Readable+Backup]
A[Arbiter<br/>Arbitration Node<br/>Vote Only]
subgraph "Data Flow"
W[Write] --> P
P -->|oplog| S1
P -->|oplog| S2
end
subgraph "Election"
P -->|Heartbeat| S1
P -->|Heartbeat| S2
P -->|Heartbeat| A
end
style P fill:#d4edda
style S1 fill:#cce5ff
style S2 fill:#cce5ff
style A fill:#fff3cd
مقارنة أدوار العقد:
| العقدة | المسؤوليات | تخزين البيانات | قابلية القراءة | المشاركة في الانتخابات | الأولوية |
|---|---|---|---|---|---|
| الأساسي | يقبل جميع عمليات الكتابة | ✅ | ✅ | ✅ | القيمة الافتراضية 1 (يمكن زيادتها) |
| الثانوي | نسخ البيانات الأساسية | ✅ | ✅ (يتطلب التهيئة) | ✅ | الافتراضي 1 |
| الحكم | يشارك فقط في التصويت الانتخابي | ❌ | ❌ | ✅ | 0 (لا يمكن انتخابه) |
مجموعات النسخ المتماثلة مقابل العقد الفردية:
| البعد | العقدة الفردية | مجموعة النسخ المتماثلة |
|---|---|---|
| التوافر | نقطة فشل واحدة | 99.99% (التحويل التلقائي في حالة الفشل) |
| أمن البيانات | تعطل القرص يعني فقدان البيانات | التكرار متعدد النسخ |
| توسيع القراءة | لا شيء | موازنة الحمل الثانوية للقراءة |
| دعم المعاملات | ❌ | ✅ (يتطلب مجموعة نسخ متماثلة) |
| التعقيد التشغيلي | منخفض | متوسط |
3. بدء تشغيل مجموعة النسخ المتماثلة
شرح المفهوم: يتضمن بدء تشغيل مجموعة النسخ المتماثلة خطوتين: 1) بدء تشغيل العملية mongod باستخدام المعلمة --replSet؛ 2) تنفيذ rs.initiate() في mongosh لتهيئة تكوين مجموعة النسخ المتماثلة. أثناء التهيئة، حدد عناوين جميع الأعضاء، وسيقوم MongoDB تلقائيًا باختيار عضو رئيسي.
كيفية العمل: يقوم rs.initiate() بكتابة تكوين مجموعة النسخ المتماثلة في قاعدة البيانات المحلية لكل عقدة، مما يؤدي إلى تشغيل بروتوكول الانتخاب. وعادةً ما تصبح العقدة الأولى التي يتم تهيئتها هي العقدة الرئيسية؛ بينما تقوم العقد الأخرى تلقائيًا بمزامنة التكوين والبدء في تتبع سجل العمليات (oplog).
# === Start the first node(Raid Mode)===
mongod --replSet rs0 --port 27017 --dbpath /data/db1 --bind_ip localhost
# === mongosh Initialization ===
mongosh
> rs.initiate({
_id: 'rs0',
members: [
{ _id: 0, host: 'localhost:27017' },
{ _id: 1, host: 'localhost:27018' },
{ _id: 2, host: 'localhost:27019' }
]
});
معلمات تكوين التهيئة:
| المعلمة | الوصف | مثال |
|---|---|---|
_id |
اسم مجموعة المثيلات؛ يجب أن يتطابق مع --replSet |
'rs0' |
members |
قائمة الأعضاء | [{_id: 0, host: '...'}] |
members._id |
رقم العضو (فريد) | 0، 1، 2 |
members.host |
عنوان العضو | 'localhost:27017' |
members.priority |
أولوية الانتخاب (0 = غير مؤهل) | الافتراضي 1 |
settings.electionTimeoutMillis |
مهلة نبض القلب | القيمة الافتراضية 10000 مللي ثانية |
4. إضافة/حذف العقد
شرح المفهوم: تدعم مجموعات النسخ المتماثلة إضافة العقد وإزالتها أثناء التشغيل دون الحاجة إلى انقطاع الخدمة. بعد إضافة عقدة، تقوم العقدة الجديدة تلقائيًا بإجراء مزامنة أولية (مزامنة كاملة) من العقدة الأساسية، ثم تنتقل إلى تتبع سجل العمليات (oplog) (مزامنة تزايدية).
عملية مزامنة البيانات:
sequenceDiagram
participant New as New Node
participant P as Primary
participant Oplog as oplog
New->>P: Request to Join a Raid Group
P-->>New: Confirm + Current Configuration
Note over New,P: Phase 1: Initial Sync(Full Synchronization)
New->>P: Request Full Data
P-->>New: All Aggregated Data + Index
Note over New,P: Phase 2: Oplog Tailing(Incremental Synchronization)
loop Continuous
New->>Oplog: Get the latest oplog Entry
Oplog-->>New: Incremental Operations
New->>New: Replay oplog Operation
end
Note over New: Once synchronization is complete, you can participate in the election.
| العملية | الأمر | الوصف |
|---|---|---|
| إضافة عقدة بيانات | rs.add('host:port') |
المزامنة الأولية التلقائية |
| إضافة عقدة تحكيم | rs.addArb('host:port') |
للتصويت فقط، لا تخزن البيانات |
| إزالة العقدة | rs.remove('host:port') |
تخفيض مستوى العقدة تلقائيًا |
| عرض الحالة | rs.status() |
حالة جميع العقد + سلامتها |
| عرض التكوين | rs.conf() |
تفاصيل تكوين مجموعة النسخ المتماثلة |
// === Add a New Node ===
rs.add('localhost:27020');
// === Add Arbiter(Do not save data)===
rs.addArb('localhost:27021');
// === Remove Node ===
rs.remove('localhost:27020');
// === View Replica Set Status ===
rs.status();
تحليل النقاط الرئيسية:
- أثناء عملية المزامنة الأولية، لا تشارك العقد الجديدة في الانتخابات أو في خدمات القراءة؛ وبمجرد اكتمال المزامنة، تصبح تلقائيًا عقدًا ثانوية.
- تستغرق المزامنة الأولية لمجموعات البيانات الكبيرة وقتًا طويلاً (قد تستغرق مزامنة 100 جيجابايت عدة ساعات)، لذا يُنصح بإضافتها خلال ساعات الذروة.
- لا يقوم «أربيتر» بتخزين البيانات؛ فهو يُستخدم حصريًّا لضمان مشاركة عدد فردي من العقد في التصويت، وأولويته هي 0.
5. آلية الانتخاب (استنادًا إلى Raft)
شرح المفهوم: تُعد آلية الانتخاب عنصراً أساسياً في ضمان التوافر العالي لمجموعة النسخ المتماثلة — فعندما يتعطل الخادم الأساسي (Primary)، يقوم الخادم الثانوي (Secondary) تلقائياً بإجراء عملية انتخاب لاختيار خادم أساسي جديد. وتستند عملية الانتخاب في MongoDB إلى نسخة معدلة من بروتوكول Raft، حيث تتمثل الضمانات الأساسية في «وجود خادم أساسي واحد على الأكثر في أي وقت» (لمنع حدوث انقسام الدماغ) و«أن يكون الخادم الأساسي الجديد هو الذي يمتلك البيانات الأكثر اكتمالاً».
عملية انتخاب أعضاء مجلس «رافت»:
- الكشف عن الأعطال: لم يتلقَ الخادم الثانوي إشارة «نبض» من الخادم الأساسي خلال فترة «electionTimeout» (الافتراضية 10 ثوانٍ)
- بدء عملية الانتخاب: يصبح الخيار الثانوي المتاح ذو الأولوية الأعلى مرشحًا، ويتم زيادة مدة صلاحيته.
- طلب التصويت: يرسل المرشح طلب تصويت إلى جميع العقد
- قواعد التصويت: تُدلي كل عقدة بصوت واحد فقط في كل فترة ولاية، لصالح المرشح الذي يمتلك أحدث البيانات.
- الانتخاب بالأغلبية: المرشح الذي يحصل على أكثر من نصف الأصوات (> N/2) يصبح الرئيس الجديد.
- إعادة الاتصال بالتطبيق: يقوم برنامج التشغيل تلقائيًا باكتشاف الجهاز الأساسي الجديد والتبديل إليه بشكل شفاف
sequenceDiagram
participant S1 as Secondary 1<br/>(Candidates)
participant S2 as Secondary 2
participant A as Arbiter
Note over S1: Primary Heartbeat Timeout 10s<br/>Call for an Election
S1->>S1: Auto-increment term = 2<br/>Vote for yourself
S1->>S2: RequestVote(term=2, lastOplogTime)
S1->>A: RequestVote(term=2, lastOplogTime)
S2->>S1: GrantVote ✅<br/>(The data is recent enough)
A->>S1: GrantVote ✅
Note over S1: receive a majority of the votes (2/3)<br/>Become a New Primary
S1->>S2: Heartbeat(term=2, role=PRIMARY)
S1->>A: Heartbeat(term=2, role=PRIMARY)
Note over S1,S2: The election is over,App Auto-Reconnect
المعايير الرئيسية للانتخابات:
| قواعد الانتخابات | الوصف | القيمة الافتراضية |
|---|---|---|
| المشغل | لم يتم الوصول إلى الحساب الرئيسي لفترة أطول من «electionTimeout» | 10 ثوانٍ |
| المرشح | جميع المراحل الثانوية + «أربيتر» | — |
| التصويت | يجب الحصول على أغلبية الأصوات (> النصف) ليصبح المرشح الرئيسي | — |
| الأولوية | يتم انتخاب العقد ذات الأولوية الأعلى أولاً | 1 |
| سلامة البيانات | لا يصوّت الناخبون إلا للمرشحين الذين لديهم سجلات عمليات محدثة | — |
| فترة التهدئة الانتخابية | منع إجراء انتخابات متكررة | 30 ثانية |
graph LR
A[Primary Failure] --> B[Secondary Testing]
B --> C{receive a majority of the votes?}
C -->|Yes| D[Promoted to Primary]
C -->|No| E[Waiting]
D --> F[App Reconnection]
style D fill:#d4edda
| قواعد الانتخابات | شرح |
|---|---|
| المشغل | لم يتم الوصول إلى الحساب الرئيسي لفترة أطول من «electionTimeout» |
| المرشح | جميع المراحل الثانوية + «أربيتر» |
| التصويت | يجب الحصول على أغلبية الأصوات (> النصف) ليصبح المرشح الرئيسي |
| الأولوية | يتم انتخاب العقد ذات الأولوية الأعلى أولاً |
لماذا يلزم وجود عدد فردي من العقد: يمكن للنظام المكون من 3 عقد أن يتحمل عطلًا واحدًا (أغلبية 2/3)؛ كما يمكن للنظام المكون من 4 عقد أن يتحمل عطلًا واحدًا (أغلبية 3/4)؛ ويمكن للنظام المكون من 5 عقد أن يتحمل عطلين (أغلبية 3/5). لا يؤدي العدد الزوجي للعقد إلى زيادة القدرة على تحمل الأعطال، بل يؤدي إلى زيادة التكاليف، لذا يُنصح باستخدام عدد فردي من العقد.
▶ المثال 1: تكوين أولوية الاختيار
// TechCorp:Give priority to the more powerful server Primary
rs.reconfig({
_id: 'rs0',
members: [
{ _id: 0, host: 'mongo1:27017', priority: 3 }, // The Strongest Server,Elected by a landslide
{ _id: 1, host: 'mongo2:27017', priority: 2 }, // Second choice
{ _id: 2, host: 'mongo3:27017', priority: 1 }, // Lowest Priority
{ _id: 3, host: 'mongo4:27017', priority: 0 } // Never Elected(Cold Standby)
]
});
// priority: 0 That node will never be elected Primary,Suitable for use as a cold standby or reporting server
// Adjustment priority This will trigger a re-election(If the current Primary No longer the best option)
الإخراج:
TEXT 📖 للعرض فقط{ "ok" : 1 }
6. الفصل بين عمليات القراءة والكتابة
شرح المفهوم: تدعم مجموعات النسخ المتماثلة بطبيعتها الفصل بين القراءة والكتابة — حيث يتم توجيه عمليات الكتابة إلى الخادم الأساسي، بينما يمكن توزيع عمليات القراءة على الخادم الثانوي. ومن خلال تكوين سياسة توجيه القراءة عبر readPreference، يمكن تخفيف الحمل على الخادم الأساسي بشكل كبير في السيناريوهات التي تتطلب عمليات قراءة مكثفة.
كيفية العمل: يستخدم برنامج تشغيل Mongoose/Mongo المعلمة readPreference لتحديد العقدة التي تُرسل إليها عملية القراءة. تضمن المعلمة primary التناسق القوي، بينما تقلل المعلمة secondary الحمل على العقدة الأساسية، لكنها تؤدي إلى حدوث تأخير في النسخ المتماثل.
تفاصيل تفضيلات القراءة:
| النمط | السلوك | الاتساق | زمن الاستجابة | حالات الاستخدام |
|---|---|---|---|---|
primary |
العقدة الرئيسية للقراءة فقط | الأعلى | الأدنى | المعاملات، البيانات الحساسة |
primaryPreferred |
الأولي أولاً؛ إذا لم يكن متاحًا، فالتحول إلى الثانوي | قوي | منخفض | السيناريوهات العامة |
secondary |
عقدة ثانوية للقراءة فقط | ضعيفة | عالية | التقارير/التحليلات |
secondaryPreferred |
استخدم هذا أولاً؛ وإذا لم يكن متاحًا، فاستخدم الخيار الأساسي | أضعف | أقل | كثيف القراءة، خفيف الكتابة |
nearest |
أدنى زمن انتقال للشبكة | غير معروف | أدنى | موزعة جغرافيًا |
المفاضلة بين الاتساق والأداء:
graph LR
A[primary<br/>Strong consistency<br/>High latency] --> B[primaryPreferred<br/>Strong consensus]
B --> C[secondaryPreferred<br/>Weak consensus]
C --> D[secondary<br/>Weak consistency<br/>Low latency]
D --> E[nearest<br/>Not sure<br/>Lowest Latency]
style A fill:#d4edda
style D fill:#fff3cd
// === Default:All read and write operations take place in Primary ===
const user = await User.findById(userId);
// === Read from a child node(Reduce Primary Pressure)===
const products = await Product.find().read('secondary');
// === Read Preference Options ===
await Product.find().read('primary'); // Master node only
await Product.find().read('primaryPreferred'); // Preferred Primary Node
await Product.find().read('secondary'); // From the node alone
await Product.find().read('secondaryPreferred'); // Priority Node
await Product.find().read('nearest'); // Recent Online Trends
تحليل النقاط الرئيسية:
- قد تنطوي عملية القراءة من إحدى العقد على تأخير في النسخ المتماثل (عادةً ما يكون أقل من ثانية واحدة، وإن كان من الممكن أن يطول في حالة حدوث مشكلات في الشبكة).
- في وضع
secondary، إذا كانت جميع العقد الثانوية غير متاحة، فسيُرجع الاستعلام خطأً. - بالنسبة للحالات التي لا يمثل فيها الاتساق مصدر قلق كبير، مثل إعداد التقارير وإنشاء فهارس البحث، نوصي باستخدام
secondaryأوsecondaryPreferred.
7. نشر Docker على 3 عقد
شرح المفهوم: يُعد Docker Compose الطريقة الأكثر ملاءمة لتطوير مجموعات النسخ المتماثلة واختبارها محليًّا. ويُعد التكوين المكون من 3 عقد (1 رئيسية + 2 ثانوية) هو الحد الأدنى الموصى به للتكوين في بيئة الإنتاج، حيث يوفر توافرًا عاليًا وتحويلًا تلقائيًّا في حالة الفشل.
بنية النشر:
graph TB
subgraph "Docker Compose"
M1[mongo1:27017<br/>Primary/Secondary]
M2[mongo2:27017<br/>Primary/Secondary]
M3[mongo3:27017<br/>Primary/Secondary]
end
App[Applications] --> M1
App --> M2
App --> M3
M1 <--> M2
M2 <--> M3
M1 <--> M3
style M1 fill:#d4edda
style M2 fill:#cce5ff
style M3 fill:#cce5ff
# docker-compose.yml
version: '3.8'
services:
mongo1:
image: mongo:7.0
command: mongod --replSet rs0 --port 27017
ports:
- "27017:27017"
mongo2:
image: mongo:7.0
command: mongod --replSet rs0 --port 27017
ports:
- "27018:27017"
mongo3:
image: mongo:7.0
command: mongod --replSet rs0 --port 27017
ports:
- "27019:27017"
// === Initialize the replica set ===
rs.initiate({
_id: 'rs0',
members: [
{ _id: 0, host: 'mongo1:27017' },
{ _id: 1, host: 'mongo2:27017' },
{ _id: 2, host: 'mongo3:27017' }
]
});
خطوات النشر:
| الخطوة | الأمر | الوصف |
|---|---|---|
| 1 | docker-compose up -d |
بدء تشغيل 3 حاويات |
| 2 | mongosh --port 27017 |
الاتصال بالعقدة الأولى |
| 3 | rs.initiate({...}) |
تهيئة مجموعة النسخ المتماثلة |
| 4 | rs.status() |
حالة التحقق |
| 5 | rs.conf() |
عرض الإعدادات |
8. التحويل التلقائي
شرح المفهوم: يعد التحويل التلقائي (Failover) أهم ميزة لمجموعة النسخ المتماثلة — فعندما تتعطل العقدة الرئيسية (Primary)، تقوم العقد المتبقية تلقائيًا بانتخاب عقدة رئيسية جديدة، ويُعاد توصيل التطبيق بشكل شفاف. وعادةً ما تكتمل العملية بأكملها في غضون 10–30 ثانية؛ وخلال هذه الفترة، تكون عمليات الكتابة غير متاحة، لكن يمكن تحويل عمليات القراءة إلى العقدة الثانوية (Secondary).
عملية التحويل التلقائي:
sequenceDiagram
participant App as Applications
participant P as Primary (mongo1)
participant S1 as Secondary (mongo2)
participant S2 as Secondary (mongo3)
Note over P: mongo1 Failure
P-xApp: Heartbeat Disconnected
P-xS1: Heartbeat Disconnected
P-xS2: Heartbeat Disconnected
Note over S1,S2: 10Not received yetPrimaryHeartbeat
S1->>S2: Call for an Election
S2->>S1: Vote in favor
S1->>S1: receive a majority of the votes(2/3)
Note over S1: mongo2 Become a New Primary
App->>S1: Automatic Reconnection → Writing has returned to normal
App->>S2: Continue reading(secondaryPreferred)
Note over App,S1: Total downtime: 10-30 seconds
| سيناريو الفشل | التأثير | وقت الاستعادة |
|---|---|---|
| انقطاع التيار الأولي | انقطاع الكتابة لمدة 10–30 ثانية | الاستعادة التلقائية للانتخابات |
| انقطاع ثانوي | لا يوجد تأثير | التزامن التلقائي بعد إعادة التشغيل |
| تعطل غالبية العقد | مجموعة النسخ المتماثلة مخصصة للقراءة فقط | يجب استعادة غالبية العقد |
| قسم الشبكة | غير قابل للكتابة من جانب الأقلية | دمج تلقائي بعد استعادة الشبكة |
# === Primary Downtime Simulation ===
# kill mongo1 Container
docker stop mongo1
# === Automatic Election(30 Completed in seconds)===
# mongo2 or mongo3 Automatically promoted to Primary
# === App Auto-Reconnect(mongoose Layout)===
mongoose.connect('mongodb://mongo1,mongo2,mongo3/shopdb?replicaSet=rs0');
استكشاف الأخطاء وإصلاحها في طبقة التطبيق:
| الإعداد | الوصف | القيمة الموصى بها |
|---|---|---|
| سلسلة الاتصال | عرض جميع العقد | mongodb://m1,m2,m3/db?replicaSet=rs0 |
| إعادة محاولة الكتابة | إعادة المحاولة تلقائيًا في حالة حدوث أخطاء في الشبكة | retryWrites=true |
| تفضيلات القراءة | تدهور جودة القراءة أثناء الأعطال | secondaryPreferred |
| انتهت مهلة الاتصال | مدة مهلة الاتصال | connectTimeoutMS=5000 |
| انتهت مهلة اختيار الخادم | انتهت مهلة اختيار العقدة | serverSelectionTimeoutMS=30000 |
9. أفضل الممارسات لمجموعات النسخ المتماثلة
نظرة عامة على المفهوم: تشمل أفضل الممارسات الخاصة بمجموعات النسخ المتماثلة جوانب مثل عدد العقد، وتكوينات الأمان، والمراقبة والتنبيهات. ويمكن أن يساعد اتباع هذه الممارسات في منع الأعطال الشائعة التي تصيب مجموعات النسخ المتماثلة في بيئات الإنتاج.
الممارسات الرئيسية:
| التمرين | الوصف | السبب |
|---|---|---|
| إعداد مكون من 3 عقد | 1 عقدة رئيسية + 2 عقد ثانوية | التكوين الأدنى المتحمل للأعطال، الذي يتحمل تعطل عقدة واحدة |
| عدد العقد فردي | يتطلب الانتخاب أغلبية الأصوات: 3/5/7 | وجود عدد زوجي من العقد لا يزيد من قدرة النظام على تحمل الأعطال |
| writeConcern: majority | تم تأكيد الكتابة على غالبية العقد | لا يحدث فقدان للبيانات (حتى في حالة تعطل العقدة الرئيسية) |
| تفضيل القراءة: العقدة الأساسية المفضلة | القراءة الافتراضية للعقدة الأساسية | يضمن الاتساق؛ فإذا كانت العقدة الأساسية غير متاحة، يتم ترقية عقدة ثانوية |
| مراقبة نافذة سجل العمليات (oplog) | قد يؤدي تجديد سجل العمليات (oplog) إلى فقدان البيانات | يجب أن تكون مدة النافذة أكبر من 24 ساعة |
| تهيئة أولوية معقولة | يتمتع الخادم القوي بأولوية أعلى | بعد استعادة الخدمة عقب حدوث عطل، يتم إعادة انتخاب الخادم القوي |
| استخدم «أربيتر» بحذر | استخدمه فقط عندما تكون التكلفة عاملاً مهمًا | لا يقوم «أربيتر» بتخزين البيانات ولا يمكن استعادته |
مقارنة writeConcern:
| writeConcern | أمان البيانات | زمن انتقال الكتابة | حالات الاستخدام |
|---|---|---|---|
w: 1 |
التأكيد الأساسي فقط | الأسرع | السجلات، البيانات غير الحرجة |
w: majority |
تم تأكيده من قبل معظم العقد | متوسط | بيانات تجارية عامة |
w: majority, j: true |
أكبر عدد من عمليات التأكيد + كتابة السجل على القرص | الأبطأ | الشؤون المالية، البيانات الحساسة |
قائمة مراجعة المراقبة:
| عنصر المراقبة | الأمر | قيمة الحالة | عتبة التنبيه |
|---|---|---|---|
| حالة مجموعة النسخ المتماثلة | rs.status() |
1 أساسي + N ثانوي | لا يوجد أساسي |
| تأخير النسخ | rs.status().members[n].optimeDate |
< 1 ثانية | > 10 ثوانٍ |
| نافذة سجل العمليات | rs.printReplicationInfo() |
> 72 ساعة | < 24 ساعة |
| عدد الاتصالات | db.serverStatus().connections |
< 1000 | > 8000 |
| عدد الانتخابات | rs.status().electionMetrics |
قليل | انتخابات متكررة |
▶ مثال: نشر مجموعة النسخ المتماثلة المكونة من 3 عقد باستخدام Docker + اختبار التحويل التلقائي في حالة الفشل
# === 1. docker-compose.yml ===
# version: '3.8'
# services:
# mongo1:
# image: mongo:7.0
# command: mongod --replSet rs0 --bind_ip_all --port 27017
# ports: ["27017:27017"]
# networks: [mongo-net]
#
# mongo2:
# image: mongo:7.0
# command: mongod --replSet rs0 --bind_ip_all --port 27017
# ports: ["27018:27017"]
# networks: [mongo-net]
#
# mongo3:
# image: mongo:7.0
# command: mongod --replSet rs0 --bind_ip_all --port 27017
# ports: ["27019:27017"]
# networks: [mongo-net]
#
# networks:
# mongo-net:
# driver: bridge
# Start 3 Node
docker-compose up -d
# === 2. Initialize the replica set ===
mongosh --port 27017 --eval '
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
});
'
# === 3. Verify the replica set status ===
mongosh --port 27017 --eval 'rs.status();'
# Output:
# {
# set: 'rs0',
# members: [
# { name: 'localhost:27017', stateStr: 'PRIMARY' },
# { name: 'localhost:27018', stateStr: 'SECONDARY' },
# { name: 'localhost:27019', stateStr: 'SECONDARY' }
# ]
# }
# === 4. Writing Test Data(Automatically copy to Secondary)===
mongosh --port 27017 --eval '
db.products.insertOne({ sku: "PHONE-001", title: "Smartphone X", price: 599 });
'
# === 5. Verify Data Replication ===
mongosh --port 27018 --eval '
db.setSecondaryOk(); // Read Permitted Secondary
db.products.findOne({ sku: "PHONE-001" });
'
# Return to the same document,Proof that the copy was successful
# === 6. Failover Testing ===
# Stop Primary
docker stop mongo1
# View on mongo2
mongosh --port 27018 --eval 'rs.status();'
# Output:mongo2 Automatically promoted to PRIMARY(30 Completed in seconds)
# === 7. App Auto-Reconnect(mongoose)===
// mongoose The chain automatically discovers all nodes
const mongoose = require('mongoose');
await mongoose.connect(
'mongodb://localhost:27017,localhost:27018,localhost:27019/shopdb?replicaSet=rs0'
);
// Even if Primary fails, App automatically connects to new Primary
const products = await Product.find(); // Automatic Retry,No errors
// === 8. Read-Write Separation Configuration ===
// Write operation complete Primary
await Product.create({ sku: 'PHONE-002', title: 'Phone 2' });
// Read operations can proceed Secondary(Reduce Primary Pressure)
const products = await Product.find().read('secondaryPreferred');
// === 9. Restore a Failed Node ===
docker start mongo1
# mongo1 Automatically set to Secondary Join a Raid Group,Start Over Primary Synchronize Data
النتيجة: توفر مجموعة النسخ المتماثلة المكونة من 3 عقدة مستوى عالٍ من التوافر؛ ففي حالة تعطل العقدة الرئيسية، يتم التحويل التلقائي في غضون 30 ثانية، دون أن يؤثر ذلك على التطبيق.
▶ مثال 3: مراقبة صحة مجموعة النسخ المتماثلة والتنبيهات(الصعوبة ⭐⭐)
// نظام TechCorp لمراقبة مجموعة النسخ المتماثلة
const mongoose = require('mongoose');
// الاتصال بمجموعة النسخ المتماثلة
await mongoose.connect(
'mongodb://mongo1:27017,mongo2:27017,mongo3:27017/shopdb?replicaSet=rs0'
);
const db = mongoose.connection.db;
// مراقبة صحة مجموعة النسخ المتماثلة كل 30 ثانية
setInterval(async () => {
try {
const status = await db.admin().command({ replSetGetStatus: 1 });
// البحث عن العقدة الرئيسية
const primary = status.members.find(m => m.stateStr === 'PRIMARY');
const secondaries = status.members.filter(m => m.stateStr === 'SECONDARY');
console.log('=== حالة مجموعة النسخ المتماثلة ===');
console.log(`العقدة الرئيسية: ${primary?.name || 'غير متاحة!'}`);
console.log(`العقد الثانوية: ${secondaries.map(s => s.name).join(', ')}`);
// التحقق من تأخر النسخ المتماثل
secondaries.forEach(secondary => {
const lag = primary ? secondary.optimeDate - primary.optimeDate : 0;
if (lag > 10000) {
console.log(`⚠️ تحذير: العقدة ${secondary.name} متأخرة بـ ${lag} مللي ثانية`);
}
});
// التنبيه عند عدم وجود عقدة رئيسية
if (!primary) {
console.log('🚨 خطأ حرج: لا توجد عقدة رئيسية!');
await sendAlert('URGENT: No Primary in replica set!');
}
// التحقق من نافذة سجل العمليات
const replInfo = await db.admin().command({ replSetGetConfig: 1 });
console.log(`نافذة سجل العمليات: ${replInfo.config?.settings?.oplogSizeMB || 'غير معروف'} ميغابايت`);
} catch (err) {
console.error('خطأ في المراقبة:', err.message);
}
}, 30000);
// وظيفة إرسال التنبيهات
async function sendAlert(message) {
console.log(`[تنبيه] ${message}`);
// يمكن إضافة إرسال بريد إلكتروني أو إشعار Slack هنا
}
الإخراج:
TEXT 📖 للعرض فقط=== حالة مجموعة النسخ المتماثلة === العقدة الرئيسية: mongo1:27017 العقد الثانوية: mongo2:27017, mongo3:27017 نافذة سجل العمليات: 1024 ميغابايت
❓ أسئلة شائعة
📖 ملخص
- مجموعة النسخ المتماثلة: الأساسي + الثانوي + الوسيط
- آلية الانتخاب: تستند إلى نظام «رافت» (Raft)؛ وتتطلب أغلبية الأصوات
- مزامنة البيانات: المزامنة الأولية + تتبع سجل العمليات (oplog)
- الفصل بين القراءة والكتابة: readPreference
- نشر Docker على 3 عقد
- التحويل التلقائي: الانتخاب التلقائي + إعادة اتصال التطبيق
📝 تمارين
- السؤال الأساسي (⭐): قم بنشر مجموعة نسخ متماثلة مكونة من 3 عقد باستخدام Docker Compose.
- السؤال الأساسي (⭐): قم بتهيئة مجموعة النسخ المتماثلة والتحقق من حالتها (rs.status()).
- تمرين متقدم (⭐⭐): اختبار الفصل بين القراءة والكتابة (readPreference: ثانوي).
- تمرين متقدم (⭐⭐): قم بمحاكاة حدوث عطل في النظام الأساسي وراقب عملية الانتخاب التلقائية.
- التحدي (⭐⭐⭐): نشر مجموعة نسخ متماثلة كاملة (3 عقد + مراقبة مجموعة النسخ المتماثلة + اختبار التعافي من الأعطال).