مستوى المقال: مبتدئ. الشرح يبدأ بمثال بسيط ثم ينتقل للتفسير العلمي، وكل الأكواد قابلة للنسخ والتجربة.
لو إدخال 100 ألف صف في قاعدة بياناتك بياخد دقايق، المشكلة غالبًا مش في السيرفر. اللي بيحصل فعلاً إنك بتدخل صف واحد في كل أمر، وكل أمر بيكلفك رحلة كاملة للسيرفر. اجمع الصفوف في دفعات، وهتنزّل الزمن من دقايق لثوانٍ بسطر أو اتنين.
ليه إدخال الصفوف صفًا بصف بطيء، وإزاي الإدخال الدفعي بيحلها
المشكلة باختصار
عندك سكربت بيستورد ملف فيه 100 ألف صف لجدول PostgreSQL. بيلف على كل صف وبينفّذ INSERT لوحده. السكربت بياخد وقت طويل غريب، والسيرفر نص طاقته فاضي. ده موقف شائع في الاستيراد الأولي، ترحيل البيانات، أو تسجيل الأحداث بالجملة.
المفهوم بمثال بسيط
تخيّل إنك رايح السوبر ماركت ومعاك 100 صنف. فيه طريقتين:
- الطريقة الأولى: تودّي صنف واحد للكاشير، تدفع، ترجع البيت، بعدين تيجي تاني عشان الصنف اللي بعده. تكرر ده 100 مرة.
- الطريقة التانية: تحط كل الـ 100 صنف في عربة واحدة، وتعدّي على الكاشير مرة واحدة.
الحساب على السلعة نفسها تقريبًا نفس الزمن في الحالتين. اللي بيقتلك هو "الرايح جاي" المتكرر. الإدخال صف بصف هو الطريقة الأولى بالظبط. الإدخال الدفعي هو العربة الواحدة.
التفسير العلمي
كل أمر INSERT منفصل بيدفع تكلفتين خفيتين:
- رحلة شبكة ذهاب وعودة (round trip): تطبيقك بيبعت الأمر ويستنى الرد. الرحلة دي ممكن تكون 0.5 إلى 2 مللي ثانية على شبكة محلية، وأعلى بكتير على شبكة سحابية.
- التزام المعاملة (commit): لو كل
INSERTفي معاملة لوحده، السيرفر بيكتب على القرص ويعملfsyncبعد كل صف. ده أبطأ حاجة ممكن تعملها للتخزين.
اضرب التكلفتين دول في 100 ألف صف، هتفهم الدقايق راحت فين. الإدخال الدفعي بيدمج آلاف الصفوف في أمر واحد ومعاملة واحدة، فبتدفع الرحلة والـ commit مرة واحدة لكل دفعة بدل مرة لكل صف.
الحل خطوة بخطوة
- اجمع الصفوف في أمر
INSERTواحد متعدد القيم (multi-row VALUES). - لُفّ الإدخال كله في معاملة واحدة بدل معاملة لكل صف.
- للأحجام الكبيرة جدًا استخدم
COPY، وهو أسرع مسار إدخال في PostgreSQL.
الشكل الأساسي لأمر إدخال دفعي في SQL:
-- دفعة واحدة تُدخل عدة صفوف
INSERT INTO events (user_id, action, created_at) VALUES
(1, 'click', now()),
(2, 'view', now()),
(3, 'click', now());
-- بدل ثلاثة أوامر INSERT منفصلة
ومن بايثون باستخدام psycopg2، بدل اللف على كل صف: