الرئيسيةمن أناالدوراتالمدونةسوق الأوامرالمناهج والباقاتالشركاء

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • المناهج والباقات
  • سوق الأوامر
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالمناهجالمدونةالدخول
Optimizing بالعربي

الإدخال الدفعي: أدخِل 100 ألف صف في ثوانٍ بدل دقائق

مبتدئ30 يوليو 20264 دقائق قراءة
الإدخال الدفعي: أدخِل 100 ألف صف في ثوانٍ بدل دقائق

مستوى المقال: مبتدئ. الشرح يبدأ بمثال بسيط ثم ينتقل للتفسير العلمي، وكل الأكواد قابلة للنسخ والتجربة.

لو إدخال 100 ألف صف في قاعدة بياناتك بياخد دقايق، المشكلة غالبًا مش في السيرفر. اللي بيحصل فعلاً إنك بتدخل صف واحد في كل أمر، وكل أمر بيكلفك رحلة كاملة للسيرفر. اجمع الصفوف في دفعات، وهتنزّل الزمن من دقايق لثوانٍ بسطر أو اتنين.

ليه إدخال الصفوف صفًا بصف بطيء، وإزاي الإدخال الدفعي بيحلها

المشكلة باختصار

عندك سكربت بيستورد ملف فيه 100 ألف صف لجدول PostgreSQL. بيلف على كل صف وبينفّذ INSERT لوحده. السكربت بياخد وقت طويل غريب، والسيرفر نص طاقته فاضي. ده موقف شائع في الاستيراد الأولي، ترحيل البيانات، أو تسجيل الأحداث بالجملة.

المفهوم بمثال بسيط

تخيّل إنك رايح السوبر ماركت ومعاك 100 صنف. فيه طريقتين:

  • الطريقة الأولى: تودّي صنف واحد للكاشير، تدفع، ترجع البيت، بعدين تيجي تاني عشان الصنف اللي بعده. تكرر ده 100 مرة.
  • الطريقة التانية: تحط كل الـ 100 صنف في عربة واحدة، وتعدّي على الكاشير مرة واحدة.

الحساب على السلعة نفسها تقريبًا نفس الزمن في الحالتين. اللي بيقتلك هو "الرايح جاي" المتكرر. الإدخال صف بصف هو الطريقة الأولى بالظبط. الإدخال الدفعي هو العربة الواحدة.

التفسير العلمي

كل أمر INSERT منفصل بيدفع تكلفتين خفيتين:

  • رحلة شبكة ذهاب وعودة (round trip): تطبيقك بيبعت الأمر ويستنى الرد. الرحلة دي ممكن تكون 0.5 إلى 2 مللي ثانية على شبكة محلية، وأعلى بكتير على شبكة سحابية.
  • التزام المعاملة (commit): لو كل INSERT في معاملة لوحده، السيرفر بيكتب على القرص ويعمل fsync بعد كل صف. ده أبطأ حاجة ممكن تعملها للتخزين.

اضرب التكلفتين دول في 100 ألف صف، هتفهم الدقايق راحت فين. الإدخال الدفعي بيدمج آلاف الصفوف في أمر واحد ومعاملة واحدة، فبتدفع الرحلة والـ commit مرة واحدة لكل دفعة بدل مرة لكل صف.

الحل خطوة بخطوة

  1. اجمع الصفوف في أمر INSERT واحد متعدد القيم (multi-row VALUES).
  2. لُفّ الإدخال كله في معاملة واحدة بدل معاملة لكل صف.
  3. للأحجام الكبيرة جدًا استخدم COPY، وهو أسرع مسار إدخال في PostgreSQL.

الشكل الأساسي لأمر إدخال دفعي في SQL:

SQL
-- دفعة واحدة تُدخل عدة صفوف
INSERT INTO events (user_id, action, created_at) VALUES
  (1, 'click', now()),
  (2, 'view',  now()),
  (3, 'click', now());
-- بدل ثلاثة أوامر INSERT منفصلة

ومن بايثون باستخدام psycopg2، بدل اللف على كل صف:

Python
import psycopg2
from psycopg2.extras import execute_values

rows = [(i, "click") for i in range(100_000)]

conn = psycopg2.connect("dbname=app")
with conn, conn.cursor() as cur:          # معاملة واحدة تلقائيًا
    execute_values(
        cur,
        "INSERT INTO events (user_id, action) VALUES %s",
        rows,
        page_size=1000,                   # 1000 صف لكل دفعة
    )

وCOPY لو البيانات في ملف أو مجرى (stream):

Python
with conn, conn.cursor() as cur:
    with open("events.csv") as f:
        cur.copy_expert(
            "COPY events (user_id, action) FROM STDIN WITH CSV",
            f,
        )

الأرقام

على PostgreSQL 16، جدول بفهرس واحد، شبكة محلية:

  • صفًا بصف، معاملة لكل صف: حوالي 210 ثانية لـ 100 ألف صف.
  • دفعات 1000 صف/أمر داخل معاملة واحدة: حوالي 3.5 ثانية — أسرع نحو 60 مرة.
  • COPY: أقل من ثانية في كثير من الحالات.

الافتراض إن دي أرقام تقديرية بترتيب الحجم (order of magnitude)، وبتتغير حسب العتاد والشبكة وعدد الفهارس والـ triggers على الجدول. لكن النسبة بين الطرق بتفضل ثابتة تقريبًا: الدفعات أسرع بعشرات المرات.

الـ trade-offs وما يجب الانتباه له

حجم الدفعة هو المتغير المهم. بتكسب سرعة كبيرة كل ما كبّرت الدفعة، لكن بتخسر في نقطتين لو بالغت:

  • دفعة ضخمة (مثلاً 100 ألف صف في أمر واحد) بتاكل ذاكرة في العميل والسيرفر، وبتطوّل القفل على الجدول.
  • لو صف واحد في الدفعة غلط (قيمة مكررة أو نوع خاطئ)، الدفعة كلها بتفشل. التحكم في الأخطاء على مستوى الصف بيتضاءل.

الحجم المعقول عمليًا بين 500 و5000 صف لكل دفعة. ابدأ بـ 1000 وقِس.

متى لا تستخدم هذه الطريقة

الإدخال الدفعي مش دايمًا الصح:

  • لو بتدخل صف أو صفين بس في كل عملية، الفرق مش هيبان والتعقيد مالوش لازمة.
  • لو محتاج تتعامل مع فشل كل صف على حدة بمنطق مختلف (مثلاً upsert معقد لكل صف)، الدفعة بتصعّب ده.
  • لو البيانات بتوصل واحد واحد في الزمن الحقيقي (real-time)، مفيش صفوف تتجمّع أصلًا عشان تعمل منها دفعة.

الخطوة التالية

افتح أبطأ سكربت استيراد عندك دلوقتي. لُفّه في معاملة واحدة، وجمّع الإدخال في دفعات 1000 صف باستخدام execute_values أو ما يقابلها في لغتك. قِس الفرق بـ \timing في psql قبل وبعد. لو ما نزلش الزمن بشكل واضح، غالبًا لسه بتعمل commit لكل صف — راجع حدود المعاملة.

المصادر

  • PostgreSQL 16 Documentation — Populating a Database (الفصل 14.4): توصيات الإدخال الجماعي، المعاملة الواحدة، وCOPY.
  • PostgreSQL Documentation — أمر COPY: أسرع مسار لإدخال البيانات دفعة واحدة.
  • psycopg2 Documentation — psycopg2.extras.execute_values: الإدخال الدفعي من بايثون مع page_size.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة