ليلى تدير مختبر تحاليل في بغداد، وفي قاعدة بياناته 34,000 نتيجة. التقرير الذي يخرج المرضى الذين لم يستلموا نتائجهم كان يستغرق 12 ثانية. سطر واحد أنزله إلى 40 مللي ثانية، ولم يتغير في الاستعلام حرف.
CREATE INDEX idx_results_status ON results (status, created_at);
وفي القاعدة نفسها عطل أهدأ وأغلى: مريض واحد مسجل ثلاث مرات، ونتائجه موزعة على ثلاثة سجلات. لم يمنع ذلك قيد التفرد الموضوع على الاسم، وهو موضوع وصحيح ويعمل.
الفهرس نسخة ثانية، والقراءة تشتريها بالكتابة
الفهرس نسخة ثانية من العمود، محفوظة مرتبة. بدونه تقرأ القاعدة الجدول كله وتفحص كل صف؛ ومعه تنزل مباشرة إلى موضع القيمة، لأن النسخة مرتبة والمرتب يبحث فيه بالتنصيف لا بالمشي.
وهذا يقرأ من القاعدة نفسها لا من التخمين:
EXPLAIN ANALYZE SELECT * FROM results WHERE status = 'pending' ORDER BY created_at;
قبل الفهرس كانت الخطة مسحا كاملا يمر على 34,000 صف؛ بعده صارت قراءة 210 صفوف. والثمن ليس صفرا: كل إدراج وكل تحديث صار يكتب مرتين، مرة في الصف ومرة في النسخة المرتبة، والنسخة تشغل مساحة على القرص وتبقى محدثة. جدول يقرأ منه مرة في الشهر ويكتب فيه كل ثانية لا يريد شيئا من هذا.
ترتيب المقارنة هو الذي قرر أن المريض ثلاثة
الأسماء الثلاثة في سجل ليلى كانت: إبراهيم العزاوي، وابراهيم العزاوي، وإبراهيم العزّاوي. الأولان يختلفان في همزة الألف، والثالث فيه شدة. على الشاشة تكاد لا ترى، وفي التخزين ثلاث قيم مختلفة.
ترتيب المقارنة في العمود هو الذي يقرر أي قيمتين متساويتان. المساواة في قاعدة البيانات ليست قرار برنامجك، هي خاصية اخترتها يوم أنشأت العمود، وأكثر الناس لم يختاروها بل ورثوها من لوحة الاستضافة. والجواب لا يخمن، يسأل في سطر واحد:
SELECT 'إبراهيم' = 'ابراهيم';
يرد 0 أو 1، وذلك هو الجواب في قاعدتك أنت لا في قاعدة كاتب الدرس. في PostgreSQL الترتيب الافتراضي حاسم، فالجواب 0 دائما ولا مساواة بين الشكلين. وفي MySQL يتوقف على أي ترتيب من عائلة utf8mb4 أعطي للعمود، وهي عائلة أفرادها لا يتفقون على هذا. والفرق بين 0 و1 هنا هو الفرق بين قيد تفرد يعمل وقيد يمر من تحته ثلاثة سجلات.
والعلاج الذي لا يتوقف على أي جواب: عمود مطوي يكتبه البرنامج ويفهرس، هو الحل الذي لا يعتمد على ترتيب المقارنة. يكتب البرنامج إلى جانب الاسم نسخة موحدة منه، بلا همزات ولا شدات، ويوضع قيد التفرد والفهرس عليها لا على الاسم المعروض. وهذه هي العلة نفسها التي تقابل قارئ البرمجة عند الفرز داخل البرنامج، إلا أن التوحيد على حافة البرنامج لا يبلغ هنا: المقارنة تجري داخل القاعدة، تحت قاعدة اختيرت مرة ولم تعد تسأل.
ويلحق بهذا قرار يتخذ في اليوم الأول ولا يؤجل. تغيير ترتيب المقارنة بعد الملء يعيد بناء الجدول وكل فهارسه، وهو على جدول صغير دقائق، وعلى جدول حي مع قيود خارجية تعتمد عليه عملية تخطط لها ولا ترتجل.
ثلاث مصائد تصمت بدل أن تشتكي
NULL لا يساوي NULL، وIS NULL هو الفحص الوحيد الذي يعمل. قيمة NULL ليست قيمة، هي غياب قيمة، وأي مقارنة معها ترد غيابا لا صوابا ولا خطأ. فشرط WHERE phone = NULL لا يرد صفا واحدا أبدا، ولا يرفع خطأ، ولا يشكو. الصحيح WHERE phone IS NULL. وأسوأ صورها أن NOT IN على قائمة فيها قيمة NULL واحدة ترد قائمة فارغة، فيبدو التقرير سليما وهو خال.
وربط بلا شرط ON هو كل صف في مقابل كل صف. جدول مرضى فيه 3,000 صف مربوط بجدول نتائج فيه 34,000 بلا شرط يعطي أكثر من مئة مليون صف. القاعدة لا تعترض، هي تنفذ ما كتب، والعطل يظهر في صورة استعلام لا ينتهي أو تقرير فيه كل نتيجة مكررة بعدد المرضى.
والثالثة أن المعاملة كلها أو لا شيء منها. تسجيل نتيجة يمس جدولين: النتيجة، وحالة الطلب. بين السطرين انقطاع كهرباء، فتبقى نتيجة بلا طلب أو طلب بلا نتيجة. المعاملة تجمعهما:
BEGIN;INSERT INTO results ...; UPDATE orders SET status = 'done' ...;COMMIT;
أربع درجات، ويبدأ أكثر الناس من الثالثة
الدرجة الأولى لغة الاستعلام نفسها وقراءة الخطة التي ترد بها القاعدة. والثانية شكل الجداول: ما المفتاح، وما الذي يجب ألا يتكرر، وما الذي لا يجوز أن يغيب، وبأي ترتيب مقارنة. والثالثة المحرك: أي منتج، وأي لهجة، وماذا يفعل عند انقطاع في منتصف الكتابة. والرابعة الشكل الآخر، حين يكون الصف الثابت وعاء خاطئا أصلا.
ويبدأ أكثر القراء من الثالثة، لأنها الدرجة التي لها اسم في إعلان وظيفة: MySQL أو PostgreSQL أو SQL Server. والدرجتان تحتها غير مرئيتين لمن بدأ فوقهما، ولذلك يحفظ كثيرون اسم المحرك ولا يقرؤون خطة استعلام واحدة.
ولغة SQL أسهل هذه الأربع تعلما وأكثرها تأجيلا، لأن المحول بين الكائنات والجداول يكتب استعلامات مقبولة للمبتدئ ويكف عن ذلك عند أول تقرير حقيقي. وتصميم قواعد البيانات هي الدرجة التي تقرر فيها ليلى ترتيب المقارنة والعمود المطوي، قبل أن يدخل صف واحد.
الترتيب
أربع درجات، وكل واحدة غير مرئية لمن بدأ فوقها
يبدأ أكثر الناس من الدرجة الثالثة، لأنها وحدها لها اسم في إعلان وظيفة.
- 04الشكل الآخرمستندات ومفاتيح، حين يكون الصف الثابت وعاء خاطئا فعلا.بلا الثلاث قبلها فهو تخمين
- 03المحركأي منتج، وأي لهجة، وماذا يفعل عند انقطاع في منتصف الكتابة.مختار قبل وصولك عادة
- 02الشكلما الجداول، وما لا يتكرر، وما لا يغيب، وبأي ترتيب مقارنة.تخطيها ولا فهرس ينقذك
- 01اللغةالسؤال المباشر، وقراءة الخطة التي ترد بها القاعدة.تخطيها يجعل المحول معتما
الدرجات ليست ترتيب صعوبة. الأولى أسهلها تعلما وأكثرها تأجيلا، لأن المحول بين الكائنات والجداول يكتب استعلامات مقبولة للمبتدئ ويكف عن ذلك عند النقطة التي تهم.
الجدار: ترميز اختارته لوحة الاستضافة
القارئ العربي يقابل هذا الموضوع أول مرة من خلال لوحة استضافة مشتركة، لا من خلال خادم أنشأه بنفسه. اللوحة تنشئ القاعدة بضغطة، وتختار لها ترميزا وترتيب مقارنة بلا سؤال، وتكتب الاختيار في مكان لا يمر عليه أحد. والقارئ يكتب بالعربية على ما اختير له.
وأعراض هذا معروفة ومشهورة في المنتديات العربية: علامات استفهام مكان الحروف، أو بحث لا يجد ما هو مكتوب أمام عينيه، أو اسم يقص في منتصفه. والجواب المتكرر هناك جملة واحدة: غير الترميز إلى utf8mb4. وهي صحيحة وناقصة، لأنها لا تقول إن الترميز شيء وترتيب المقارنة شيء آخر، ولا تقول إن التغيير بعد الملء يعيد بناء الجدول.
وثمن هذا الجدار أنه يظهر متأخرا جدا. الحرف العربي يخزن في يونيكود على بايتين لا على بايت، فجدول عمل شهرا على مئة صف يبدأ في الاختناق عند مئة ألف، وقيد التفرد الذي بدا صحيحا في التجربة يمر من تحته صفوف حقيقية. وهذا كله يقرر مرة واحدة، في أول جملة إنشاء جدول، وقبلها لا يكلف شيئا.
ثلاث أدوات، وأولاها ملف واحد بلا خادم
SQLite قاعدة كاملة داخل ملف واحد، بلا خادم وبلا تنصيب وبلا شبكة. حلت محل اختراع صيغة ملف وكتابة قارئ لها، وهو ما يفعله كل من احتاج حفظ بيانات ولم يرد خادما. ومجانيتها هنا ليست تفصيلا: تعمل على حاسوب بلا إنترنت، وتنسخ بنسخ ملف، وهي المدخل الوحيد إلى هذا الموضوع لمن لا يستطيع فتح حساب سحابي.
وPostgreSQL هو الخيار الافتراضي اليوم، وحل محل MySQL في هذا الموضع على مدى العقد الأخير تقريبا. مجاني ومفتوح المصدر، ويعمل على أي جهاز، وله مناطق سحابية داخل هذه السوق نفسها في البحرين والإمارات وقطر والسعودية، وهذه مسألة تعني قارئا يبني لمستخدمين هنا. وهو الذي جوابه على سطر المقارنة أعلاه صريح ولا يتغير بتغير الاستضافة.
وEXPLAIN ليست منتجا بل أمر داخل القاعدتين معا، وهي التي أعطت الرقمين في أول هذه الصفحة. حلت محل قياس الاستعلام بساعة ثم التخمين في السبب. ولا تكلف شيئا، ولا تحتاج صلاحية خاصة في أكثر الحالات، وقراءتها هي الفرق بين إضافة فهرس عن علم وإضافته على الأمل.
الخمسة تحت هذه الصفحة، والسؤال الذي يليها
والمحركات هي الدرجة الثالثة، وتقرأ بعد الأوليين لا قبلهما، لأن الفرق بين محرك وآخر لا يعني شيئا لمن لم يقرأ خطة استعلام. وNoSQL جواب عن سؤال لا يطرح إلا بعد الثلاث: متى يكون الصف الثابت وعاء خاطئا. والتعامل مع البيانات في الكود هي الطبقة بين كائنات برنامجك وهذه الجداول، وهي الموضع الذي يكتب فيه العمود المطوي فعلا.
ومن أراد أن يعرف من أين تصل هذه الصفوف أصلا فموضعه تطوير الويب، ومن أراد أثمان الأشكال داخل البرنامج نفسه قبل أن تصل إلى جدول فموضعها أساسيات البرمجة.
ويبقى سؤال يصل بعد أول قاعدة حقيقية. قاعدة ليلى تعمل على خادم واحد، وفيها نتائج مرضى، والنسخة الاحتياطية الوحيدة ملف على حاسوبها. ما الذي ينقل هذه القاعدة من جهاز إلى خادم يعمل بلا انقطاع، ويعيدها إلى ما كانت عليه أمس إن فسد شيء اليوم؟ جوابه في نشر المشاريع.