MongoDB: إدراج المستندات

آخر تحديث: 2026-08-26

يُعد إدراج المستندات الخطوة الأولى في كتابة البيانات إلى MongoDB — ويُعد إتقان insertOne وinsertMany الأساس لمعالجة البيانات.

تقدم هذه الدورة نظرة متعمقة على الطرق المختلفة لإدراج المستندات، ومعالجة الأخطاء، وتحسين الأداء، وآلية «Write Concern».

1. ما ستتعلمه



2. قصة حقيقية لمهندس بيانات

(1) المشكلة: غالبًا ما تفشل عمليات الاستيراد المجمعة لـ 1 مليون سجل

أليس هي مهندسة بيانات في إحدى شركات التجارة الإلكترونية، وتحتاج إلى نقل مليون سجل منتج من قاعدة بيانات MySQL إلى قاعدة بيانات MongoDB:

"استخدمت insertMany لاستيراد مليون سجل منتج، لكن عملية الاستيراد بأكملها فشلت عند السجل رقم 500,000 بسبب تكرار قيمة _id، مما أدى إلى إهدار أربع ساعات. إما أن تنجح العملية بالكامل أو تفشل تمامًا — وهذا يمثل كارثة بالنسبة لعملية الترحيل التدريجي."

المشاكل التي تواجهها:

المشكلة التأثير
مرتبة: صحيح (الافتراضي) إذا فشل عنصر واحد في الدفعة، تفشل الدفعة بأكملها
عدم وجود مستوى أمان للكتابة فقدان البيانات بسبب تعطل الخادم
_تعارض في المعرفات فشل بسبب تكرار معرفات التزايد التلقائي في MySQL
الإدراج الجماعي استغرق إدراج مليون عملية فردية ساعة واحدة

(2) حل باستخدام MongoDB وbulkWrite

JAVASCRIPT
// === Usage bulkWrite + ordered: false Resolving Partial Failure Issues ===
const products = [...];  // 100,000 product records

const BATCH_SIZE = 1000;
for (let i = 0; i < products.length; i += BATCH_SIZE) {
  const batch = products.slice(i, i + BATCH_SIZE);

  try {
    await Product.bulkWrite(
      batch.map(doc => ({
        insertOne: { document: doc }
      })),
      { ordered: false }  // Allow for Partial Failure,Continue execution
    );
  } catch (err) {
    console.error(`Batch ${i / BATCH_SIZE} Failure:${err.writeErrors?.length} items`);
  }
}

// === Usage Write Concern Ensure Data Persistence ===
await Product.bulkWrite(
  batch.map(doc => ({ insertOne: { document: doc } })),
  {
    ordered: false,
    writeConcern: { w: 'majority', j: true, wtimeout: 5000 }
  }
);

(3) الإيرادات

البعد الإدراج في صف واحد الإدراج المجمَّع + مرتب: كاذب
الأداء مليون سجل ~60 دقيقة مليون سجل ~3 دقائق
التحمل للأعطال يؤدي أي عطل واحد إلى فقدان البيانات يسمح العطل الجزئي بمواصلة العمل
أمن البيانات عرضة للفقدان ديمومة الكتابة
تعقيد الكود بسيط متوسط


3. insertOne: إدراج مستند واحد

شرح المفهوم: insertOne هي طريقة الكتابة الأساسية في MongoDB، وتُستخدم لإدراج مستند في مجموعة. يتم تخزين كل مستند في MongoDB بتنسيق BSON ويتم تعيين مفتاح أساسي فريد _id له تلقائيًا. وعلى عكس INSERT INTO في قواعد البيانات العلائقية، لا تتطلب insertOne بنية جدول محددة مسبقًا، ويمكن أن تحتوي المستندات على أي مجموعة من الحقول.

كيفية العمل: عندما يبادر العميل بطلب insertOne، يقوم خادم MongoDB بتنفيذ الخطوات التالية: التحقق من صحة تنسيق BSON → التحقق من تفرد _id → الكتابة إلى محرك التخزين WiredTiger → تطبيق مستوى الاهتمام بالكتابة (Write Concern) → إرجاع insertedId. وتُعد عملية الكتابة بأكملها عملية متكاملة بالنسبة للمستند الواحد.

100%
sequenceDiagram
    participant App as Applications
    participant Mongod as MongoDB Server-side
    participant WT as WiredTiger Engine

    App->>Mongod: insertOne({ doc })
    Mongod->>Mongod: Verification BSON Format
    Mongod->>Mongod: Inspection _id Unique Index
    alt _id Conflict
        Mongod-->>App: E11000 duplicate key error
    else _id The Only One
        Mongod->>WT: Write to the document + Update Index
        WT-->>Mongod: Confirm Write
        Mongod-->>App: { acknowledged: true, insertedId }
    end
المعلمة النوع الوصف
document المستند المستند المراد إدراجه (مطلوب)
writeConcern المستند مستوى تأكيد الكتابة (اختياري)
الحالات التي ينطبق عليها الحالات التي لا ينطبق عليها
إنشاء سجل واحد (تسجيل المستخدم) استيراد البيانات بالجملة
يلزم استرداد insertedId كتابة جماعية لأكثر من 1,000 سجل
المستندات ذات التداخل المعقد الاستيراد مع إزالة البيانات المكررة

(1) قواعد النحو الأساسية

JAVASCRIPT
// === insertOne Basic Usage ===
db.products.insertOne({
  sku: "PHONE-001",
  title: "Smartphone X",
  price: NumberDecimal("599.99"),
  category: "Electronics",
  stock: 50,
  createdAt: new Date()
});

الإخراج:

TEXT 📖 للعرض فقط
{
  acknowledged: true,
  insertedId: ObjectId('507f1f77bcf86cd799439011')
}

تحليل النقاط الرئيسية:

  1. يشير acknowledged: true إلى أن خادم MongoDB قد أقر بعملية الكتابة (وفقًا لمستوى Write Concern)
  2. إذا كان writeConcern: { w: 0 }، فإن acknowledged يساوي false، ولا يتم إرجاع insertedId.
  3. تعتمد قيمة insertedId على ما إذا كان _id قد تم تحديده يدويًّا أم لا — ففي حالة عدم تحديده، يتم إنشاء معرّف الكائن (ObjectId) تلقائيًّا.

(2) تحليل قيم الإرجاع

شرح المفهوم: تحتوي قيمة الإرجاع لـ insertOne على حقلين رئيسيين — acknowledged و insertedId. إذا كانت قيمة acknowledged هي true، فهذا يشير إلى أن خادم MongoDB قد أقر بعملية الكتابة (وفقًا لـ Write Concern؛ إذا كانت w: 0، فإنها تكون false). .insertedId هي قيمة _id للمستند الذي تم إدراجه؛ ويتم إرجاعها بغض النظر عما إذا كان _id قد تم إنشاؤه تلقائيًا أو تم تحديده يدويًّا.

حقل الإرجاع النوع الوصف الملاحظات
acknowledged منطقية ما إذا كان قد تم تأكيد عملية الكتابة «false» عندما تكون w: 0
insertedId ObjectId/Any معرّف _id للمستند الذي تم إدراجه يُرجع القيمة المحددة عند تحديدها يدويًّا
JAVASCRIPT
const result = db.products.insertOne({
  sku: "TEST-001",
  title: "Test Product"
});

print(result.acknowledged);   // true(Write confirmed)
print(result.insertedId);      // ObjectId('507f1f77bcf86cd799439012')

الإخراج:

TEXT 📖 للعرض فقط
true
ObjectId('507f1f77bcf86cd799439012')
الحقل النوع الوصف
acknowledged منطقية تشير القيمة «true» إلى أن عملية الكتابة قد تم تأكيدها
insertedId ObjectId معرّف _id للمستند الذي تم إدراجه

(3) يتم إنشاء _id تلقائيًا

شرح المفهوم: _id هو المفتاح الأساسي لوثيقة MongoDB، ونوعه الافتراضي هو ObjectId (ثنائي مكون من 12 بايت). إذا لم يتم تحديد _id يدويًّا، يقوم برنامج تشغيل MongoDB بإنشائه تلقائيًّا على جانب العميل، مع ضمان تفرده قبل كتابته على الخادم. يختلف هذا التصميم عن معرّفات التزايد التلقائي في MySQL — حيث لا يعتمد ObjectId على عداد مركزي ويدعم بطبيعته البيئات الموزعة.

كيفية العمل: يتكون معرّف الكائن (ObjectId) من طابع زمني مكون من 4 بايت + قيمة عشوائية مكونة من 5 بايت (الآلة + العملية) + عداد متزايد مكون من 3 بايت. يضمن الجزء الخاص بالطابع الزمني أن يتم فرز معرّفات الكائنات بشكل طبيعي حسب وقت الإدراج؛ وتضمن القيمة العشوائية التفرّد عبر العمليات المختلفة؛ ويضمن العداد التفرّد خلال ثانية واحدة.

100%
graph LR
    A[Client-Side Generation ObjectId] --> B[Timestamp 4B<br/>Insertion Time]
    A --> C[Random value 5B<br/>Machine+Unique Process]
    A --> D[Counter 3B<br/>Increment within the same second]
    B --> E[Globally Unique<br/>Naturally Ordered<br/>Withdrawal Time]
_id الاستراتيجية مثال السيناريوهات القابلة للتطبيق الترتيب
Auto ObjectId ObjectId("...") السيناريوهات العامة (الافتراضية) ✅ الترتيب حسب الوقت
مفتاح العمل النصي "ORDER-2026-001" رقم الطلب، SKU يعتمد على التنسيق
التزايد التلقائي NumberInt(1) ترحيل النظام القديم ✅ الترتيب حسب الرقم
الطابع الزمني NumberLong(1700000000) بيانات السلسلة الزمنية ✅ مرتبة حسب الوقت
UUID UUID("...") فريد عبر الأنظمة ❌ غير مرتب
JAVASCRIPT
// === Not specified _id(Automatically Generated ObjectId)===
db.users.insertOne({
  name: "Alice",
  email: "alice@example.com"
});
// Automatically Generated _id: ObjectId('507f1f77bcf86cd799439011')

// === Specify manually _id ===
db.users.insertOne({
  _id: "user_001",       // String ID
  name: "Alice"
});

db.users.insertOne({
  _id: ObjectId(),       // Generated Manually ObjectId
  name: "Bob"
});

db.users.insertOne({
  _id: NumberLong(1700000000000),  // Timestamps as ID
  name: "Charlie"
});

(4) تفرد _id

شرح المفهوم: يقوم MongoDB تلقائيًا بإنشاء فهرس فريد في الحقل _id لكل مجموعة، وهو ما يُعد الضمانة الأساسية لسلامة البيانات. وتتمثل الميزة الفريدة للفهرس الفريد _id في أنه لا يمكن حذفه — فحتى في حالة تنفيذ dropIndexes()، يظل الفهرس _id موجودًا. وعند إدراج مستند يحتوي على _id مكرر، يُصدر MongoDB خطأ E11000 duplicate key error، ويتم التراجع عن عملية الإدراج بأكملها.

حالات الاستخدام: في سيناريوهات ترحيل البيانات والاستيراد بالجملة، تُعد تعارضات _id المصدر الأكثر شيوعًا للأخطاء. ويُعد فهم كيفية منع هذه التعارضات وحلها أمرًا أساسيًّا لضمان التشغيل المستقر في بيئات الإنتاج. وتتمثل استراتيجية الوقاية الموصى بها في الاستعلام عن المجموعة الحالية من _id قبل الاستيراد، أو استخدام مخطط upsert كبديل لـ insertOne.

السيناريو سبب النزاع الاستراتيجية الموصى بها
الترحيل من MySQL إلى MongoDB تعارض معرّفات الترقيم التلقائي مع البيانات الموجودة قم بإزالة الرمز القديم _id ودع MongoDB يقوم بإنشائه
دمج البيانات من مصادر متعددة مصادر البيانات المختلفة تشترك في نفس المفتاح التجاري إضافة البادئة: sourceA_ORDER-001
المزامنة التزايدية البيانات المصدر موجودة بالفعل في الوجهة updateOne + upsert
الاستيراد الجماعي ملفات CSV/JSON التي تحتوي على صفوف مكررة ordered: false تخطي التكرارات
JAVASCRIPT
// === _id Handling Repeated Errors ===
try {
  db.users.insertOne({
    _id: "user_001",     // Already exists
    name: "Alice Duplicate"
  });
} catch (err) {
  // E11000 duplicate key error collection: shopdb.users index: _id_
  print("❌ _id Already exists:" + err.message);
}

الإخراج:

TEXT 📖 للعرض فقط
❌ _id Already exists: E11000 duplicate key error collection: shopdb.users index: _id_ dup key: { _id: "user_001" }
JAVASCRIPT

// === Usage upsert Handling Duplicates ===
db.users.updateOne(
  { _id: "user_001" },
  { $set: { name: "Alice Updated" } },
  { upsert: true }       // If it doesn't exist, insert it,If it exists, update it
);

▶ المثال 1: الاستخدام الكامل لـ insertOne

JAVASCRIPT
// === Inserting Different Types of Fields ===
db.products.insertOne({
  // String
  sku: "PHONE-X-256-BLK",
  title: "Smartphone X 256GB Black",

  // Numeric Types
  price: NumberDecimal("599.99"),       // Decimal128(Accurate)
  stock: NumberInt(50),                  // Int32
  viewCount: NumberLong(1000000),         // Long

  // Boolean
  isActive: true,
  isFeatured: false,

  // Date
  createdAt: new Date(),
  releaseDate: ISODate("2026-01-01"),

  // Array
  tags: ["5g", "amoled", "fast-charging"],
  colors: ["Black", "White", "Blue"],

  // Nested Documents
  specs: {
    screen: "6.5 inch OLED",
    battery: "4500mAh",
    camera: "108MP"
  },

  // Binary
  thumbnail: BinData(0, "iVBORw0KGgoAAAANSUhEUgAA..."),

  // Null
  discount: null
});

الإخراج:

TEXT 📖 للعرض فقط
{
  acknowledged: true,
  insertedId: ObjectId('507f1f77bcf86cd799439011')
}

▶ المثال 2: insertOne مع استراتيجيات _id مختلفة

JAVASCRIPT
// === Strategy 1:Automatic ObjectId(Default)===
const r1 = db.users.insertOne({ name: "Alice", email: "alice@example.com" });
print(`Auto ObjectId: ${r1.insertedId}`);

// === Strategy 2:String Business Key ===
const r2 = db.orders.insertOne({
  _id: "ORD-20260701-0001",
  total: NumberDecimal("599.99"),
  status: "pending"
});
print(`Business key: ${r2.insertedId}`);

// === Strategy 3:Nested Documents + Array ===
const r3 = db.products.insertOne({
  _id: ObjectId(),
  sku: "PHONE-X-256-BLK",
  specs: { screen: "6.5 inch OLED", battery: "4500mAh" },
  tags: ["5g", "amoled"],
  price: NumberDecimal("599.99")
});
print(`Nested doc: ${r3.insertedId}`);

الإخراج:

TEXT 📖 للعرض فقط
Auto ObjectId: ObjectId('507f1f77bcf86cd799439011')
Business key: ORD-20260701-0001
Nested doc: ObjectId('507f1f77bcf86cd799439012')

الناتج: معرّف الكائن التلقائي: ObjectId('...') | المفتاح التجاري: ORD-20260701-0001 | المستند المتداخل: ObjectId('...')



4. insertMany: الإدراج الجماعي

وصف المفهوم: insertMany —إدراج مستندات متعددة في عملية واحدة— هي الطريقة الأساسية لكتابة البيانات دفعة واحدة. مقارنةً بطريقة insertOne، التي تعالج المستندات واحدًا تلو الآخر، تجمع طريقة insertMany مستندات متعددة في طلب شبكي واحد يُرسل إلى الخادم، مما يقلل بشكل كبير من عبء ذهاب وإياب الشبكة ويحسن الأداء بمقدار 10 إلى 100 ضعف.

كيفية العمل: يتلقى insertMany مجموعة من المستندات ويحدد استراتيجية التنفيذ بناءً على الخيار ordered. ordered: true (الافتراضي) يُدرج العناصر بالتسلسل واحدة تلو الأخرى ويتوقف فورًا عند مواجهة خطأ؛ ordered: false يسمح بالإدراج المتوازي ويتخطى العناصر الفاشلة لمواصلة التنفيذ. لهذه الاستراتيجيتين تأثير كبير على الأداء وسلامة البيانات.

100%
graph TB
    A[insertMany<br/>1000 Documents] --> B{ordered option}
    B -->|ordered: true| C[Sequential Insertion<br/>Item 1 -> Item 2 -> ...<br/>Stop on Error]
    B -->|ordered: false| D[Parallel Insertion<br/>Writing Multiple Rows Simultaneously<br/>Skip failed items]
    
    C --> C1[Performance:Intermediate<br/>Consistency:Strong]
    D --> D1[Performance:Higher<br/>Consistency:Weak]

    style D fill:#d4edda
المعلمة النوع الوصف
documents مصفوفة مصفوفة من المستندات (مطلوب، على الأقل إدخال واحد)
ordered منطقية true التنفيذ التسلسلي (الافتراضي)، false التنفيذ المتوازي
writeConcern المستند مستوى تأكيد الكتابة
الحالات التي ينطبق عليها الحالات التي لا ينطبق عليها
ترحيل البيانات، الاستيراد الجماعي إدراج مستند واحد
إنشاء بيانات الاختبار عمليات الكتابة التي تتطلب ترتيبًا صارمًا للمعاملات
كتابة سجل الدُفعات التبعيات القوية بين المستندات

(1) قواعد النحو الأساسية

JAVASCRIPT
// === insertMany Basic Usage ===
db.products.insertMany([
  { sku: "PHONE-001", title: "Phone A", price: 599.99 },
  { sku: "PHONE-002", title: "Phone B", price: 699.99 },
  { sku: "PHONE-003", title: "Phone C", price: 799.99 }
]);

الإخراج:

TEXT 📖 للعرض فقط
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('507f1f77bcf86cd799439011'),
    '1': ObjectId('507f1f77bcf86cd799439012'),
    '2': ObjectId('507f1f77bcf86cd799439013')
  },
  insertedCount: 3
}

(2) الخيار «المرتب» (أمر بالغ الأهمية!)

شرح المفهوم: يُعد ordered الخيار الأكثر أهمية بالنسبة لـ insertMany. فهو يحدد كيفية تعامل MongoDB مع الأخطاء أثناء عمليات الكتابة المجمعة — سواء كان ذلك بالإلغاء الفوري أو تخطي الخطأ والمضي قدمًا. ويُعد فهم ordered أمرًا بالغ الأهمية لاستيراد البيانات في بيئات الإنتاج.

حالات الاستخدام: بالنسبة لسيناريوهات ترحيل البيانات والمزامنة التراكمية، يُوصى باستخدام ordered: false لأن البيانات المصدر قد تحتوي على تكرارات _id؛ حيث إن تخطي التكرارات ومواصلة الاستيراد يعد خيارًا أكثر منطقية من فشل الدفعة بأكملها. أما بالنسبة لسيناريوهات المعاملات المالية، فيُوصى باستخدام ordered: true لضمان التسلسل التشغيلي الدقيق.

JAVASCRIPT
// === ordered: true(Default)— If it fails in the middle, stop ===
db.products.insertMany([
  { _id: 1, sku: "A" },
  { _id: 2, sku: "B" },
  { _id: 1, sku: "C" },    // ❌ _id Conflict
  { _id: 4, sku: "D" }     // ⚠️ Will not be inserted(Previous failure)
]);
// Error:E11000 duplicate key error
// Actual insertion:A, B(2 items),C and D Not inserted

// === ordered: false — Skip failure,Continue execution ===
db.products.insertMany([
  { _id: 1, sku: "A" },
  { _id: 2, sku: "B" },
  { _id: 1, sku: "C" },    // ❌ _id Conflict
  { _id: 4, sku: "D" }     // ✅ Still inserted
], { ordered: false });

// The error message lists all documents that failed to be indexed:
// BulkWriteError: 1 document(s) failed
// writeErrors: [
//   { index: 2, code: 11000, errmsg: 'duplicate key' }
// ]
// Actual insertion:A, B, D(3 items),C Not inserted

(3) مقارنة بين الخيارات «المرتبة»

البعد مرتبة: صحيح مرتبة: خطأ
نوع الخطأ فشل الدفعة بأكملها تخطي الخطأ والمتابعة
الأداء معتدل أسرع (بالتوازي)
حالات الاستخدام التناسق القوي (مثل تحويلات الأموال) الاستيراد التدريجي، السجلات
رسالة خطأ فشل العنصر الأول تفاصيل جميع حالات الفشل

▶ المثال 3: دليل شامل للإدراج الجماعي

JAVASCRIPT
// === Example of Batch Importing E-commerce Products ===
const products = [
  { sku: "LAPTOP-001", title: "Laptop Pro", price: NumberDecimal("1299.99"), category: "Electronics", stock: 20 },
  { sku: "LAPTOP-002", title: "Laptop Air", price: NumberDecimal("999.99"), category: "Electronics", stock: 30 },
  { sku: "PHONE-001", title: "Smartphone X", price: NumberDecimal("599.99"), category: "Electronics", stock: 50 },
  { sku: "BOOK-001", title: "JavaScript Guide", price: NumberDecimal("29.99"), category: "Books", stock: 200 },
  { sku: "BOOK-002", title: "MongoDB Tutorial", price: NumberDecimal("34.99"), category: "Books", stock: 150 }
];

// === Default Mode(Orderly)===
try {
  const result = db.products.insertMany(products);
  print(`✅ Insert ${result.insertedCount} Items`);
} catch (err) {
  print(`❌ Batch Failure:${err.message}`);
}

// === Fault-Tolerant Mode(Disorder)===
try {
  const result = db.products.insertMany(products, { ordered: false });
  print(`✅ Insert ${result.insertedCount} Items`);
} catch (err) {
  print(`⚠️ Partial failure:Success ${err.result.insertedCount} items,Failure ${err.writeErrors.length} items`);
  err.writeErrors.forEach(e => print(`  Index of Failures ${e.index}: ${e.errmsg}`));
}

الإخراج:

TEXT 📖 للعرض فقط
✅ Insert 5 Items


5. كتابة الشكوى

شرح المفهوم: «Write Concern» هي آلية أمان للكتابة في MongoDB تحدد «المتطلبات التي يجب استيفاؤها لاعتبار عملية الكتابة ناجحة». وهي تتحكم في عدد العقد المتماثلة التي يجب أن تؤكد عملية الكتابة قبل إرجاع استجابة إلى العميل. ويمثل هذا مفاضلة أساسية بين ديمومة البيانات وأداء الكتابة — فكلما ارتفع مستوى التأكيد، زادت درجة الأمان، ولكن زاد زمن الاستجابة أيضًا.

كيفية العمل: في بنية مجموعة النسخ المتماثلة، تصل عمليات الكتابة أولاً إلى العقدة الأساسية، ثم يتم نسخها بشكل غير متزامن إلى العقد الثانوية. تحدد المعلمة w الخاصة بـ «Write Concern» عدد تأكيدات العقد التي يجب انتظارها. تنتظر w: 1 تأكيد العقدة الرئيسية فقط (وهي الأسرع ولكنها تنطوي على خطر فقدان البيانات)، بينما تنتظر w: "majority" تأكيد غالبية العقد (موصى بها لبيئات الإنتاج)، وتضمن j: true أن البيانات قد تمت كتابتها في سجل القرص.

100%
sequenceDiagram
    participant App as Client
    participant P as Primary
    participant S1 as Secondary 1
    participant S2 as Secondary 2

    App->>P: insertOne({ doc }, { w: "majority" })
    P->>P: Write to memory + Journal
    P->>S1: Copy oplog
    P->>S2: Copy oplog
    S1-->>P: Confirm Write
    S2-->>P: Confirm Write
    Note over P: majority Reached(2/3 Node)
    P-->>App: { acknowledged: true }
البعد w: 0 w: 1 w: الأغلبية w: الأغلبية + j: صحيح
عدد عقد التأكيد بدون انتظار أساسية أغلبية العقد أغلبية العقد + القرص
الأداء الأسرع سريع متوسط بطيء
أمن البيانات احتمال فقدان البيانات قد تُفقد البيانات في حالة تعطل الخادم الرئيسي لا يوجد فقدان عمليًا الأكثر أمانًا
السيناريوهات الموصى بها التسجيل التطوير الإنتاج الشؤون المالية

(1) ما المقصود بـ «Write Concern»؟

يصف «مستوى تأكيد الكتابة» مستوى التأكيد المطلوب لكي تُعتبر عملية الكتابة ناجحة، ويحدد متى تُعتبر البيانات «محفوظة».

100%
graph LR
    A[Client] -->|insertOne| B[mongod Receive]
    B --> C{Write Concern Layout}
    C -->|w: 1| D[Primary Write and Return]
    C -->|w: majority| E[After most nodes have confirmed, return]
    C -->|j: true| F[Returns only after writing to disk]

    style E fill:#d4edda
    style F fill:#d4edda

(2) كتابة معلمات الاستفسار

المعلمة القيمة الوصف
w 0 / 1 / "majority" / الرقم عدد العقد التي تم تأكيد الكتابة عليها
j true / false كتابة سجل العمليات على القرص
wtimeout عدد الميلي ثانية مهلة الانتظار (الافتراضي: الانتظار إلى أجل غير مسمى)

(3) مقارنة مستويات الاهتمام بالكتابة

JAVASCRIPT
// === w: 0 — Do not wait for confirmation(Fastest,May be missing)===
db.products.insertOne(
  { sku: "TEST-001", title: "Test" },
  { writeConcern: { w: 0 } }
);
// Return Now,Write success is not guaranteed

// === w: 1 — Primary Node Confirmation(Default)===
db.products.insertOne(
  { sku: "TEST-002", title: "Test" },
  { writeConcern: { w: 1 } }
);
// Primary Write and Return

// === w: "majority" — Confirmed by a majority of nodes(Safest)===
db.products.insertOne(
  { sku: "TEST-003", title: "Test" },
  { writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
);
// The replica waits until most nodes have written to disk before returning(Recommended Production Environment)

(4) مقارنة بين إعدادات مستوى الاهتمام بالكتابة

المستوى الأداء أمن البيانات حالات الاستخدام
w: 0 ⚡⚡⚡ سريع للغاية ❌ عرضة للفقدان السجلات، البيانات المؤقتة
w: 1 ⚡⚡ سريع ⚠️ قد يُفقد تطوير مستقل
w: majority ⚡ متوسط ✅ لا توجد خسارة تقريبًا موصى به لبيئات الإنتاج
w: majority, j: true ⚠️ أبطأ ✅✅ الأكثر أمانًا الشؤون المالية، البيانات الحساسة

▶ المثال 4: تكوين مستوى الاهتمام بالكتابة في بيئة الإنتاج

JAVASCRIPT
// === Cluster-Level Settings(Recommendations)===
db.adminCommand({
  setDefaultRWConcern: 1,
  defaultWriteConcern: { w: "majority", j: true, wtimeout: 10000 },
  defaultReadConcern: { level: "majority" }
});

// === Single-Write Specification ===
db.orders.insertOne(
  { userId: "user_001", total: 599.99, items: [...] },
  { writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
);

// === mongoose Settings ===
const OrderSchema = new mongoose.Schema({
  userId: String,
  total: mongoose.Schema.Types.Decimal128,
  items: Array
}, {
  writeConcern: { w: 'majority', j: true, wtimeout: 5000 }
});

الإخراج:

TEXT 📖 للعرض فقط
{ acknowledged: true, insertedId: ObjectId('507f1f77bcf86cd799439011') }


6. استراتيجية حل تعارضات _id

شرح المفهوم: _id هو المعرّف الفريد لوثيقة MongoDB، ويقوم الحقل _id في كل مجموعة بإنشاء فهرس فريد تلقائيًا. وعندما يتطابق _id لوثيقة تم إدراجها مع E11000 duplicate key error لوثيقة موجودة بالفعل، فإن MongoDB تُصدر خطأ _id. في سيناريوهات مثل ترحيل البيانات، والاستيراد بالجملة، والدمج من مصادر متعددة، تُعد تعارضات _id من أكثر المشكلات شيوعًا.

كيفية العمل: قبل كتابة مستند ما، يتحقق MongoDB أولاً مما إذا كان الحقل _id ينتهك قيد الفهرس الفريد. في حالة حدوث تعارض، يتم التراجع عن عملية الكتابة بأكملها (الذريّة على مستوى المستند الواحد)، ويتم إرجاع رمز الخطأ 11000. ويُعد فهم الاختلافات بين استراتيجيات حل التعارض المختلفة أمرًا بالغ الأهمية لضمان سلامة البيانات في بيئات الإنتاج.

100%
graph TB
    A[_id Conflict E11000] --> B[Strategy Selection]
    B --> C[Ignore duplicates<br/>ordered: false]
    B --> D[Overwrite the old value<br/>replaceOne + upsert]
    B --> E[Partial Update<br/>updateOne + upsert]
    B --> F[Regenerate _id<br/>Remove _id Field]
    B --> G[Retry Mechanism<br/>Application-Layer Retry]

    style E fill:#d4edda
الاستراتيجية بناء الجملة سلامة البيانات حالات الاستخدام
تخطي التكرارات ordered: false الاحتفاظ بالبيانات القديمة الاستيراد التراكمي، السجل
استبدال القيم القديمة replaceOne + upsert استبدالها بالبيانات الجديدة مزامنة كاملة
تحديث جزئي updateOne + upsert دمج البيانات القديمة والجديدة حقول التحديث التراكمي
تجاهل _id حذف الحقل _id إدراج الكل (مع _id جديد) الاستيراد دون تكرارات
آلية إعادة المحاولة إعادة المحاولة على مستوى التطبيق تعتمد على قيمة _id الجديدة تعارض مؤقت

(1) أعراض الخطأ

JAVASCRIPT
// === _id Repeated Mistakes ===
db.users.insertOne({ _id: 1, name: "Alice" });
// { acknowledged: true, insertedId: 1 }

db.users.insertOne({ _id: 1, name: "Bob Duplicate" });
// E11000 duplicate key error collection: shopdb.users index: _id_ dup key: { _id: 1 }

الإخراج:

TEXT 📖 للعرض فقط
MongoServerError: E11000 duplicate key error collection: shopdb.users index: _id_ dup key: { _id: 1 }

(2) 5 استراتيجيات إدارية

100%
graph TB
    A[_id Conflict] --> B[Strategy 1<br/>Skip duplicates]
    A --> C[Strategy 2<br/>Overwrite the old value]
    A --> D[Strategy 3<br/>upsert Automatic Selection]
    A --> E[Strategy 4<br/>Ignore _id Field]
    A --> F[Strategy 5<br/>Retry Mechanism]

    style D fill:#d4edda

(3) تنفيذ الاستراتيجية

JAVASCRIPT
// === Strategy 1:Usage ordered: false Skip duplicates ===
try {
  db.users.insertMany(
    [{ _id: 1, name: "Alice" }, { _id: 2, name: "Bob" }, { _id: 1, name: "Dup" }],
    { ordered: false }
  );
} catch (err) {
  print(`Skip ${err.writeErrors.length} Duplicate entry`);
}

// === Strategy 2:Usage replaceOne Coverage ===
db.users.replaceOne(
  { _id: 1 },
  { _id: 1, name: "Alice Updated", updatedAt: new Date() },
  { upsert: true }
);

// === Strategy 3:Usage updateOne + upsert ===
db.users.updateOne(
  { _id: 1 },
  { $set: { name: "Alice", email: "alice@example.com" } },
  { upsert: true }   // If it doesn't exist, insert it,If it exists, update it
);

// === Strategy 4:When inserting, make sure to MongoDB Automatically Generated _id ===
const docs = externalData.map(d => {
  const { _id, ...rest } = d;   // Deconstruct and remove _id
  return rest;                   // let MongoDB automatically generate _id
});
db.users.insertMany(docs);

// === Strategy 5:Retry Mechanism(Application Layer)===
async function insertWithRetry(doc, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await db.collection('users').insertOne(doc);
    } catch (err) {
      if (err.code === 11000 && i < maxRetries - 1) {
        // Generate a new one _id Retry
        doc._id = new ObjectId();
        continue;
      }
      throw err;
    }
  }
}

▶ المثال 5: الاستيراد الجماعي + استراتيجية إزالة التكرارات

JAVASCRIPT
// === Scene:Import CSV Data,Part _id Already exists ===
const csvData = [
  { _id: "USER-001", name: "Alice", email: "alice@example.com" },
  { _id: "USER-002", name: "Bob", email: "bob@example.com" },
  { _id: "USER-001", name: "Alice Duplicate", email: "alice2@example.com" },
  { _id: "USER-003", name: "Charlie", email: "charlie@example.com" }
];

// === Plan A:Ignore duplicates,Insert new data only ===
const insertedIds = [];
const duplicates = [];

csvData.forEach(doc => {
  try {
    const result = db.users.insertOne(doc);
    insertedIds.push(result.insertedId);
  } catch (err) {
    if (err.code === 11000) {
      duplicates.push(doc._id);
    } else {
      throw err;
    }
  }
});

print(`✅ Insert ${insertedIds.length} new data entries`);
print(`⚠️ Skip ${duplicates.length} Duplicate entry:${duplicates.join(', ')}`);

// === Plan B:Duplicate Coverage,Update existing data ===
db.users.bulkWrite(
  csvData.map(doc => ({
    replaceOne: {
      filter: { _id: doc._id },
      replacement: doc,
      upsert: true
    }
  })),
  { ordered: false }
);

الإخراج:

TEXT 📖 للعرض فقط
✅ Insert 2 new data entries
⚠️ Skip 1 Duplicate entry: USER-001


7. تحسين الأداء لعمليات الإدراج المجمعة

شرح المفهوم: تنشأ معوقات الأداء في عمليات الإدراج المجمعة بشكل أساسي من ثلاثة مجالات: عبء رحلة الذهاب والإياب عبر الشبكة، وعبء تحديث الفهرس، وعبء عمليات الإدخال/الإخراج على القرص. ومن خلال فهم هذه المعوقات الثلاثة وتحسينها واحدة تلو الأخرى، يمكنك تقليل الوقت اللازم لاستيراد مليون سجل من 60 دقيقة إلى أقل من 3 دقائق.

كيفية العمل: تؤدي معالجة المستندات واحدًا تلو الآخر insertOne إلى إرسال طلب شبكة واحد، وتحديث فهرس واحد، وعملية كتابة واحدة على القرص في كل مرة. insertMany يجمع عدة مستندات في طلب شبكة واحد، مما يقلل من الأعباء الإضافية بمقدار الثلثين. bulkWrite يدعم أيضًا أنواع العمليات المختلطة (إدراج + تحديث + حذف)، ويُنجزها في طلب واحد. كما أن الإزالة المؤقتة للفهارس غير الضرورية قبل الاستيراد يمكن أن تحسّن الأداء بمقدار 5 إلى 10 أضعاف.

100%
graph LR
    A[100,000 data entries] --> B[item by item insertOne<br/>~60 minutes<br/>100,000 web requests]
    A --> C[insertMany 1000/batch<br/>~5 minutes<br/>1000 network requests]
    A --> D[bulkWrite + dropIndexes<br/>~3 minutes<br/>1000 requests + Updates Without Indexes]

    style D fill:#d4edda
استراتيجيات التحسين تحسينات الأداء المخاطر السيناريوهات الموصى بها
insertMany يحل محل insertOne 10–100x لا شيء جميع عمليات الكتابة المجمعة
ordered: false 1.5–3x قد يتخطى الإدخالات الفاشلة استيراد مقاوم للأعطال
حذف الفهارس مؤقتًا 5–10x يجب إعادة إنشاؤها بعد الاستيراد الاستيراد الأولي
bulkWrite يحل محل insertMany 1.2–1.5x لا شيء تشغيل مختلط
w: 0 (لا يتطلب انتظار التأكيد) 2–5x قد تُفقد البيانات البيانات المؤقتة، السجلات

(1) مقارنة الأداء

100%
graph LR
    A[Insert 100,000 data entries] --> B[Insert one by one<br/>~60 minutes]
    A --> C[insertMany 1000/batch<br/>~5 minutes]
    A --> D[bulkWrite 1000/batch<br/>~3 minutes]

    style D fill:#d4edda

(2) استراتيجيات التحسين

JAVASCRIPT
// === Optimization 1:Reasonable Batch Size ===
const BATCH_SIZE = 1000;  // Recommendations 500-5000

for (let i = 0; i < data.length; i += BATCH_SIZE) {
  const batch = data.slice(i, i + BATCH_SIZE);
  db.collection.insertMany(batch, { ordered: false });
}

// === Optimization 2:Usage bulkWrite Replace insertMany ===
await Collection.bulkWrite(
  data.map(doc => ({ insertOne: { document: doc } })),
  { ordered: false }
);

// === Optimization 3:Disable Index(During the import)===
// ⚠️ Use with caution:After the import is complete, remember to rebuild the indexes.
db.products.dropIndexes();
db.products.insertMany(data);
// Rebuild Index
db.products.createIndex({ sku: 1 }, { unique: true });

// === Optimization 4:Usage Write Concern 0(Extremely fast but unsafe)===
db.products.insertMany(data, { writeConcern: { w: 0 } });
// ⚠️ For temporary data only,Not recommended for production

// === Optimization 5:Usage mongoose bulkWrite ===
const result = await Product.bulkWrite(
  data.map(doc => ({
    insertOne: { document: doc }
  })),
  { ordered: false }
);

(3) اختيار حجم الدفعة

حجم البيانات الحجم الموصى به للدفعة السبب
أقل من 100 كيلوبايت 1,000–5,000 حمل شبكي منخفض
100 كيلوبايت - 1 ميغابايت 500–2000 تحقيق التوازن بين معدل النقل والكمون
> 1 ميغابايت 100–500 تجنب الطلبات الفردية ذات الحجم المفرط
مستند كبير جدًا (حوالي 16 ميغابايت) 1-10 المستند نفسه كبير الحجم

▶ المثال 6: برنامج نصي لاستيراد البيانات عالي الأداء

JAVASCRIPT
// === Import 100 Product Data for 10,000 Items(Optimized Version)===
const fs = require('fs');
const readline = require('readline');
const { MongoClient } = require('mongodb');

async function importLargeDataset() {
  const client = new MongoClient('mongodb://localhost:27017');
  await client.connect();
  const collection = client.db('shopdb').collection('products');

  // 1. Temporarily Delete an Index(Import Speed ↑5x)
  await collection.dropIndexes().catch(() => {});
  await collection.createIndex({ sku: 1 }, { unique: true }); // Preserve the unique index(Duplicate Prevention)

  // 2. Streaming Read CSV
  const fileStream = fs.createReadStream('products.csv');
  const rl = readline.createInterface({ input: fileStream });

  let buffer = [];
  const BATCH_SIZE = 2000;

  for await (const line of rl) {
    const [sku, title, price, category] = line.split(',');
    buffer.push({
      sku,
      title,
      price: price ? NumberDecimal(price) : null,
      category,
      createdAt: new Date()
    });

    if (buffer.length >= BATCH_SIZE) {
      try {
        await collection.insertMany(buffer, { ordered: false });
      } catch (err) {
        if (err.writeErrors) {
          console.warn(`⚠️ Skip ${err.writeErrors.length} Duplicate entry`);
        }
      }
      buffer = [];
    }
  }

  // 3. Insert the remaining data
  if (buffer.length > 0) {
    await collection.insertMany(buffer, { ordered: false });
  }

  // 4. Rebuild Index
  await collection.createIndex({ category: 1, price: 1 });
  await collection.createIndex({ title: 'text' });

  console.log(`✅ Import Complete`);
  await client.close();
}

importLargeDataset().catch(console.error);

الإخراج:

TEXT 📖 للعرض فقط
✅ Import Complete


8. أنواع خاصة من الإدخالات

شرح المفهوم: يدعم تنسيق BSON في MongoDB مجموعة من أنواع البيانات أوسع بكثير من تلك التي يدعمها JSON. عند إدراج المستندات، يُعد الاستخدام الصحيح لهذه الأنواع الخاصة أمرًا أساسيًّا لتجنب فقدان دقة البيانات وأخطاء الأنواع. وفيما يلي أكثر ثلاث مشكلات شيوعًا تتعلق بالدقة: (1) Number في JavaScript هو رقم عائم بدقة مزدوجة، 0.1 + 0.2 ≠ 0.3؛ (2) لا يحتوي JSON على نوع تاريخ، لذا يتم تحويل new Date() إلى سلسلة نصية JSON.stringify()؛ (3) لا يدعم JSON البيانات الثنائية، لذا لا يمكن تخزين الصور والملفات مباشرةً.

كيفية العمل: قبل إرسال طلب الإدراج، يقوم برنامج تشغيل MongoDB (بما في ذلك mongosh وبرنامج تشغيل Node.js) أولاً بتحويل كائن JavaScript إلى تنسيق BSON. خلال هذه العملية، يتم تسلسل الكائن Date كنوع BSON Date (طابع زمني بدقة 64 بت بالميلي ثانية)، ويتم تسلسل NumberDecimal() كنوع Decimal128 (دقة عالية 128 بت)، ويتم تسلسل Buffer كنوع BSON Binary. إن فهم عملية التسلسل هذه أمر أساسي لاستخدام هذه الأنواع الخاصة بشكل صحيح.

100%
graph TB
    A[JavaScript Object] --> B[Driver Serialization]
    B --> C{Field Type Determination}
    C -->|Date Object| D[BSON Date<br/>64-bit Millisecond timestamp]
    C -->|NumberDecimal| E[BSON Decimal128<br/>128-bit High precision]
    C -->|Number Constants| F[BSON Double<br/>64-bit Floating-point]
    C -->|Buffer / BinData| G[BSON Binary<br/>Subtype + Byte Stream]
    C -->|ObjectId| H[BSON ObjectId<br/>12 Byte]
    C -->|null| I[BSON Null]
    
    style E fill:#d4edda
    style D fill:#d4edda
النوع الصيغة الدقة/النطاق السيناريوهات النموذجية
Date new Date() / ISODate("...") دقة بالميلي ثانية الطابع الزمني، فترة الصلاحية
Decimal128 NumberDecimal("0.30") 34 رقمًا عشريًّا المبلغ، الحساب الدقيق
Int32 NumberInt(123) -2^31 ~ 2^31-1 العد، الجرد
Long NumberLong(1700000000) -2^63 ~ 2^63-1 معرّف الطابع الزمني، عدد صحيح كبير
BinData BinData(0, "base64...") أي ملف ثنائي الصور، ملفات PDF
ObjectId ObjectId() / new ObjectId() 12 بايت مرجع المستند، المفتاح الأساسي

(1) أدخل التاريخ

JAVASCRIPT
// === Current Time ===
db.logs.insertOne({ event: "login", timestamp: new Date() });

// === Specified time ===
db.logs.insertOne({
  event: "signup",
  timestamp: ISODate("2026-07-01T10:30:00Z")
});

// === Create from a string ===
db.logs.insertOne({
  event: "purchase",
  timestamp: new Date("2026-07-01")
});

(2) إدراج ObjectId

JAVASCRIPT
// === Automatically Generated ===
db.users.insertOne({ name: "Alice" });

// === Create Manually ===
db.users.insertOne({
  _id: new ObjectId(),
  name: "Bob"
});

// === Create from 24-digit hex string ===
db.users.insertOne({
  _id: ObjectId("507f1f77bcf86cd799439011"),
  name: "Charlie"
});

// === Created from a timestamp(Used for range queries)===
const startOfDay = ObjectId.createFromTime(
  Math.floor(new Date('2026-07-01').getTime() / 1000)
);
db.orders.insertOne({
  _id: startOfDay,
  total: 999.99
});

(3) إدراج المستندات المتداخلة

JAVASCRIPT
// === Nested Objects ===
db.products.insertOne({
  sku: "PHONE-001",
  specs: {
    screen: { size: "6.5", type: "OLED" },
    battery: { capacity: "4500mAh", type: "Li-Po" }
  }
});

// === Array ===
db.products.insertOne({
  sku: "SHIRT-001",
  sizes: ["S", "M", "L", "XL"],
  colors: [
    { name: "Red", hex: "#FF0000" },
    { name: "Blue", hex: "#0000FF" }
  ]
});

▶ المثال 7: إدراج نوع مركب

JAVASCRIPT
// === Order Documentation(Includes all special types)===
db.orders.insertOne({
  _id: ObjectId(),
  orderNumber: "ORD-20260701-0001",

  // String + Value
  userId: "user_001",
  total: NumberDecimal("1299.99"),
  tax: NumberDecimal("130.00"),

  // Array + Nested
  items: [
    { sku: "LAPTOP-001", qty: 1, price: NumberDecimal("1299.99") },
    { sku: "MOUSE-001", qty: 2, price: NumberDecimal("29.99") }
  ],

  // Status
  status: "pending",
  isPaid: false,

  // Date
  createdAt: new Date(),
  expectedDelivery: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days

  // Binary (PDF receipt)
  receiptPdf: BinData(0, "JVBERi0xLjQKJ..."),

  // Quote
  shippingAddressId: ObjectId("507f1f77bcf86cd799439011"),

  // Metadata
  metadata: {
    userAgent: "Mozilla/5.0...",
    ipAddress: "192.168.1.1"
  }
});


9. حل مشكلات أخطاء الإدراج الشائعة

شرح المفهوم: قد تفشل عمليات الإدراج لأسباب متنوعة — تعارض _id (E11000)، أو فشل التحقق من صحة المستند (121)، أو حجم مستند BSON كبير جدًّا (16755)، أو اسم حقل غير صالح (2). ويُعد فهم رموز الأخطاء واستراتيجيات معالجتها أمرًا ضروريًّا لضمان التشغيل المستقر في بيئة الإنتاج. المبدأ الأساسي لاستكشاف الأخطاء وإصلاحها هو: التحقق أولاً من رمز الخطأ → تحديد سبب الخطأ → اختيار استراتيجية التعامل معه.

كيفية العمل: تقوم MongoDB بإجراء عملية تحقق متعددة المستويات قبل كتابة المستند: التحقق من تنسيق BSON → التحقق من أسماء الحقول (لا توجد أسماء تبدأ بـ $، ولا .) → التحقق من فريدية الفهرس _id → التحقق من المخطط → التحقق من حجم المستند (16 ميغابايت) → التحقق من عمق التداخل (100 مستوى). سيؤدي الفشل في أي مستوى إلى منع عملية الكتابة وإرجاع رمز الخطأ المقابل.

نصائح حول تصحيح الأخطاء:

  1. تمكين التسجيل التفصيلي: db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } })
  2. عرض سجل الاستعلامات البطيئة: db.system.profile.find().sort({ ts: -1 }).limit(5)
  3. التحقق من حجم المستند: BSON.calculateObjectSize(doc) يُرجع عدد البايتات
  4. التحقق من عمق التداخل: وظيفة مخصصة getDepth()

مراقبة بيئة الإنتاج:

100%
graph TB
    A[insertOne Request] --> B{BSON Format Validation}
    B -->|Failure| B1[Error Code 2<br/>Invalid field name]
    B -->|Through| C{_id Single-Check Verification}
    C -->|Conflict| C1[Error Code 11000<br/>Duplicate Keys]
    C -->|Through| D{Schema Verification}
    D -->|Failure| D1[Error Code 121<br/>Verification Failed]
    D -->|Through| E{Document Size Verification}
    E -->|More than16MB| E1[Error Code 16755<br/>The document is too large]
    E -->|Through| F[Write successful ✅]

    style F fill:#d4edda
    style C1 fill:#f8d7da
    style D1 fill:#f8d7da
رمز الخطأ المعنى السبب الجذري استراتيجية الحل
11000 _id مكرر توجد بالفعل وثيقة تحمل نفس _id استخدم upsert أو ordered: false
121 فشل التحقق من صحة المستند قيمة الحقل لا تتوافق مع قواعد المخطط تحقق من قواعد التحقق من صحة المخطط
2 خطأ في اسم الحقل يبدأ اسم الحقل بـ $ أو يحتوي على . قم بتغيير اسم الحقل
16755 حجم مستند BSON كبير جدًّا يتجاوز المستند الحد الأقصى البالغ 16 ميغابايت قم بتقسيم المستند أو استخدم GridFS
14 مهلة الاهتمام بالكتابة مهلة استجابة عقدة النسخة المتماثلة زيادة قيمة wtimeout أو تبسيط w
50 يتجاوز الحد الأقصى لعمق BSON أكثر من 100 مستوى من التداخل تقليل عدد مستويات التداخل

(1) جدول مرجعي لرموز الأخطاء

رمز الخطأ المعنى الحل
11000 _id مكرر استخدم upsert أو ordered: false
121 فشل التحقق من صحة المستند تحقق من قواعد التحقق من صحة المخطط
2 اسم حقل غير صالح (على سبيل المثال، يبدأ بـ $) إعادة تسمية الحقل
16755 حجم مستند BSON كبير جدًّا (>16 ميغابايت) قم بتقسيم المستند أو استخدم GridFS
14 مهلة عملية الكتابة قم بزيادة قيمة wtimeout أو تبسيط w
50 يتجاوز الحد الأقصى لعمق BSON تقليل مستويات التداخل

(2) نصائح حول تصحيح الأخطاء

JAVASCRIPT
// === Enable detailed logging ===
db.adminCommand({ setParameter: 1, logComponentVerbosity: { write: { verbosity: 2 } } });

// === View the slow query log ===
db.system.profile.find().sort({ ts: -1 }).limit(5);

// === Check the document size ===
const doc = { /* your document */ };
print(`Document Size:${BSON.calculateObjectSize(doc)} bytes`);
print(`Nesting Depth:${getDepth(doc)}`);

(3) مراقبة الأداء

JAVASCRIPT
// === View Current Database Operations ===
db.currentOp({ "op": "insert" });

// === Monitoring Write Performance ===
db.serverStatus().opcounters;

الإخراج:

TEXT 📖 للعرض فقط
{
  insert: 12345,
  query: 67890,
  update: 2345,
  delete: 100
}
JAVASCRIPT

// === View Write Latency ===
db.serverStatus().opLatencies.writes;

الإخراج:

TEXT 📖 للعرض فقط
{ latency: 12345, ops: 10000 }


❓ أسئلة شائعة

س ما مدى الفرق في الأداء بين insertOne وinsertMany؟
ج insertMany أسرع بـ 10 إلى 100 مرة من الاستدعاءات المتعددة لـ insertOne للأسباب التالية: (1) يقلل من عدد رحلات الذهاب والإياب عبر الشبكة؛ (2) تقوم MongoDB بمعالجتها على شكل دفعات من جانب الخادم؛ (3) يقلل من عدد تحديثات الفهرس. نوصي بأن يتراوح حجم الدفعة بين 500 و5,000 سجل.
س هل يتعين عليّ إنشاء _id بنفسي؟
ج لا، ليس عليك ذلك. إذا لم تحدد هذا المعرّف، فإن MongoDB تقوم تلقائيًا بإنشاء ObjectId (طابع زمني + قيمة عشوائية + عداد) يكون فريدًا على مستوى النظام. ويُعد تحديد _id يدويًّا مناسبًا للحالات التي تتطلب مفتاحًا أساسيًّا خاصًّا بالأعمال (مثل رقم الطلب).
س لماذا يقدم ordered: false أداءً أفضل؟
ج في حالة ordered: true، يقوم MongoDB بإدراج البيانات بشكل تسلسلي ويتوقف في حالة اكتشاف خطأ؛ أما في حالة ordered: false، فيقوم بإدراج البيانات بشكل متوازٍ ويكتفي بتخطي عملية الإدراج الفاشلة في حالة اكتشاف خطأ، مما يؤدي إلى أداء أفضل. يُنصح باستخدام ordered: false في بيئات الإنتاج.
س هل تؤدي مشكلة الكتابة «الأغلبية» دائمًا إلى فقدان البيانات؟
ج ليس داخل مجموعة النسخ المتماثلة. بعد إجراء عملية كتابة على الخادم الأساسي، يجب أن تؤكد غالبية الخوادم الثانوية عملية الكتابة قبل إرجاع الاستجابة. ومع ذلك، في حالة تعطل إحدى العقد، قد تصبح عمليات الكتابة بطيئة أو تنتهي مهلة الانتظار. يمكن حل هذه المشكلة عن طريق تكوين wtimeout معقول (على سبيل المثال، 5 ثوانٍ).
س ما هو الحجم المناسب للدفعة عند الإدراج الجماعي؟
ج نوصي بـ 500–5,000 سجل لكل دفعة، أو تعديل هذا العدد بناءً على حجم البيانات (بشرط ألا يتجاوز حجم كل دفعة 16 ميغابايت). فقد تؤدي الدفعات الكبيرة جدًّا إلى استغراق وقت طويل جدًّا في معالجة الطلبات، في حين أن الدفعات الصغيرة جدًّا قد تزيد من الحمل على الشبكة.
س كيف يمكنني تخطي حقل _id عند إدراج البيانات؟
ج قم بإزالة حقل _id على مستوى التطبيق (على سبيل المثال، const { _id, ...rest } = doc) ودع MongoDB يقوم بإنشائه تلقائيًا. أو يمكنك مسح حقل _id الموجود قبل الاستيراد.
س أين تكمن معوقات الأداء أثناء عملية الإدراج؟
ج المعوقات الشائعة: (1) التحقق من صحة الفهرس الفريد؛ (2) انتظار "Write Concern" (w: الأغلبية)؛ (3) مزامنة مجموعة النسخ المتماثلة؛ (4) عمليات الإدخال/الإخراج على القرص. طريقة التحسين: أولاً، قم بإزالة جميع الفهارس (باستثناء الفهرس الفريد)، ثم أعد إنشاؤها بعد الاستيراد.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (⭐): استخدم insertOne لإدراج ثلاثة مستندات منتجات من أنواع مختلفة (بما في ذلك Decimal128 وDate وArray وObject)، وتحقق من insertedId الذي تم إرجاعه.

  2. السؤال الأساسي (⭐): استخدم insertMany لإدراج 10 مستندات مستخدم دفعة واحدة، مع إنشاء قيم مكررة لـ _id عن قصد، ثم قارن الاختلافات في النتائج بين ordered: true وordered: false.

  3. تمرين متقدم (⭐⭐): اكتب برنامجًا لإدراج 1,000 مستند منتج دفعة واحدة (مع أرقام SKU وعناوين وأسعار تم إنشاؤها عشوائيًا)، باستخدام ordered: false وWrite Concern w: majority، وسجل وقت الإدراج.

  4. مشكلة متقدمة (⭐⭐): استخدم bulkWrite لتنفيذ المنطق التالي: «التحديث إذا كان _id موجودًا بالفعل، وإلا فقم بالإدراج» (وضع upsert)، مع معالجة 100 سجل مختلط.

  5. تمرين متقدم (⭐⭐): اكتب برنامجًا لاختبار الأداء لمقارنة الوقت المستغرق في عمليات الإدراج لصف واحد (1,000 استدعاء لـ insertOne) وعمليات الإدراج المجمعة (10 استدعاءات لـ insertMany، بواقع 100 صف في كل دفعة)، وقم بتحليل أسباب الاختلافات في الأداء.

  6. التحدي (⭐⭐⭐): اكتب أداة كاملة لترحيل البيانات تقوم بقراءة مليون سجل طلب (بما في ذلك الحقول العشرية وحقول التاريخ والوقت) من قاعدة بيانات MySQL، وتحويلها إلى تنسيق BSON الخاص بـ MongoDB، واستيرادها دفعة واحدة. يجب أن تدعم الأداة ما يلي: (أ) المزامنة التراكمية؛ (ب) إعادة المحاولة في حالة الفشل؛ (ج) عرض التقدم؛ (د) مراقبة الأداء.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%