PHP: تغيير هيكل قاعدة البيانات في تطوير الفريق



في عالم PHP ، أدوات ترحيل بنية قاعدة البيانات معروفة جيدًا - العقيدة ، Phinx من CakePHP ، من Laravel ، من Yii - هذا هو أول ما يتبادر إلى الذهن. بالتأكيد هناك أكثر من عشرة. ويعمل معظمهم مع عمليات الترحيل - أوامر لإجراء تغييرات تدريجية على مخطط قاعدة البيانات.

لن أصف سبب ذلك ، فهناك العديد من المنشورات حول هذا الموضوع على حبري. على سبيل المثال:


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

مزود الخام مقابل PHP api


نكتب الهجرات في SQL النقي. توفر العديد من الأدوات PHP api لكتابة التعليمات المترجمة إلى كود SQL. الآن لا أفهم لماذا هذا؟ ستكون هذه الأداة محدودة دائمًا في قدراتها. لا يسمحون لك بكتابة تعليمات محددة لمحرك معين ؛ لا يزال يتعين عليك استخدام SQL خالص. أنا لا أتحدث عن إجراءات الكتابة ووجهات النظر.

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

تتم كتابة عمليات ترحيل البيانات (INSERT، UPDATE) دائمًا في SQL. لأنه لا يمكنك أبدًا الاعتماد على الإصدار الحالي من ORM والموديلات. في مراجعة واحدة هم ، في الآخر لم يعد.

على سبيل المثال:

Rollback Country::delete()->where(....)->execute(); 

تريد استعادة حالة قاعدة البيانات. وهذه الفئة PHP لم تعد في الريبو. تحتاج إلى البحث عن الالتزام الأخير حيث كان والعودة من هناك. Brrr ...

لذلك ، SQL بسيطة وموثوقة:

 --TRANSACTION --UP ALTER TABLE authors ADD COLUMN code INT; ALTER TABLE posts ADD COLUMN slug TEXT; UPDATE authors SET ... --DOWN ALTER TABLE authors DROP COLUMN code; ALTER TABLE posts DROP COLUMN slug; 

المعاملات في DDL


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

المشاهدات (المشاهدات) والوظائف


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

 --UP DROP VIEW ... CREATE VIEW mvstock AS SELECT (now() - '7 days'::interval) AS refreshed_at, o.pid, COALESCE(sum(o.debit), 0)::integer AS debit, COALESCE(sum(o.credit) FILTER (WHERE d.type <> 104), 0)::integer AS credit, COALESCE(sum(o.debit), 0) - COALESCE(sum(o.credit), 0)::integer AS total FROM operations o JOIN docs d ON d.id = o.doc_id AND d.deleted_at IS NULL WHERE d.closed_at < (now() - '7 days'::interval) AND d.type <> 500 GROUP BY o.pid WITH DATA; --DOWN DROP VIEW ... CREATE VIEW mvstock AS SELECT (now() - '10 days'::interval) AS refreshed_at, o.pid, COALESCE(sum(o.debit), 0)::integer AS debit, COALESCE(sum(o.credit) FILTER (WHERE d.type <> 104), 0)::integer AS credit, COALESCE(sum(o.debit), 0) - COALESCE(sum(o.credit), 0)::integer AS total FROM operations o JOIN docs d ON d.id = o.doc_id AND d.deleted_at IS NULL WHERE d.closed_at < (now() - '10 days'::interval) AND d.type <> 500 GROUP BY o.pid WITH DATA; 

ما الذي تغير هنا؟

لقد توقفنا عن حقيقة أن بجانب عمليات الترحيل هو بابا ، حيث يتم تخزين رمز العرض والإجراء الحالي ، والذي يتم تحديثه ونسخه في ترحيل الاستعادة.

والآن أصبح الفرق مثل:



مرة أخرى في Avito ، لقد توصلنا إلى حل مثير للاهتمام لإصدار رمز الإجراء المخزن.

بشكل عام ، تثير هذه الحالة مشكلة جيدة - كيفية إلقاء نظرة على تاريخ التغييرات في كائن معين من بنية قاعدة البيانات. لكل جدول ، أريد أن أرى تاريخ التغييرات فيما يتعلق بحل مهام محددة.



تم العثور على Habré على طريقة ممتعة لأتمتة إصلاح التغييرات في بنية قاعدة البيانات.

العمل مع الفروع


ألمي الأبدي هو كيفية التبديل بين فرعين A و B ، كل منها له تعديلات على بنية قاعدة البيانات.



من الضروري استعادة عمليات الترحيل في الفرع "أ" (يجب علينا أيضًا أن نتذكر أيها وعددها) ، ثم قم بالتبديل إلى فرع "ب" وقم بترحيل عمليات الترحيل الجديدة. حسنًا ، إذا كانت تعديلاتنا متوافقة ويمكنني فقط التبديل إلى الفرع الثاني وترحيل عمليات ترحيل إضافية من B.

وإذا لم يكن كذلك؟ وإذا كان لدي أكثر من فرع واحد من هذا القبيل؟ ثم دحر كل هذه المراجعة الحالة؟ لقد كرهت دائما ...

الآن ، عند التبديل إلى فرع شخص آخر ، يمكنني تلقائيًا حذف ترحيلات الآخرين ولف المتغيرات الحالية:



حيث:

د - عمليات الترحيل التي تم إطلاقها في الفرع أ ، لكنها ليست في الفرع الحالي ، ويوصى بحذفها
أ- ب- الهجرات التي ظهرت في الفرع الجديد وتحتاج إلى تدحرج

تصبح مريحة بشكل لا يصدق عند الاختبار والتجميع التلقائي على قاعدة واحدة. عندما لا يكون هناك أي معنى أو فرصة لكل فرع لإنشاء قاعدة من الصفر. قم بالتبديل إلى الفرع ومزامنة حالة قاعدة البيانات تلقائيًا.

الترقيم وترتيب التنفيذ


جميع الأدوات التي أعرفها هي أن عمليات الترحيل المختومة ذات التوقيت المحدد هي حل جيد. إذا كتبت عدة عمليات ترحيل ، فسيتم الحفاظ على التسلسل اللازم. يمكن أن يكون لمطور آخر أي تاريخ في سلسلة رسائل أخرى ، حتى تاريخي - ولكن لا يهم بأي ترتيب نتداول معه ، فإن تغييراتنا مستقلة عن بعضها البعض. حتى لو عملنا مع نفس الجدول (إضافة حسب العمود) ، فسيتم إجراء جميع التغييرات اللازمة بأي ترتيب. الشيء الرئيسي هو أنه يتم احترام تسلسل التعديلات التي أجريتها.



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

هنا مثال مثير للاهتمام.

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

 CREATE VIEW vusers AS SELECT login, name, -- .... 
الفرع أالفرع ب
 DROP VIEW vusers; CREATE VIEW vusers AS SELECT login, name, col_A, -- .... 
 DROP VIEW vusers; CREATE VIEW vusers AS SELECT login, name, col_B, -- .... 
في هذه الحالة ، يجب جعل فرع واحد يعتمد على فرع آخر.

حالة أخرى مثيرة للاهتمام هي التصحيحات في الهجرات.

خلاصة القول هي أن الترحيل الذي تم تطبيقه لن يتم تطبيقه مرة أخرى ، بغض النظر عن عدد التغييرات التي تجريها عليه (تحتاج إلى التراجع أولاً ثم تطبيقها مرة أخرى). أي لقد أرسلت Migration للاختبار ، وجميع القواعد ، وبعد ذلك أدركت ذلك وقمت بتحرير صغير. لكن الاختبار أو الخادم الآخر الذي استخدمته فيه لن يعرف عنه.

في هذه الحالات ، قمنا بإعادة تسمية ملف الترحيل ، بإضافة رقم إصدار جديد ، بحيث يبدأ المهاجر في تفسير هذا الأمر على أنه أمران - استرجاع 1 واللف 2 ،
على سبيل المثال:



التراجع


اكتب دائمًا ROLLBACK ، حتى إذا لم تتمكن من إعادة القاعدة إلى حالتها الأصلية. على سبيل المثال ، DROP TABLE ، ما نوع ROLLBACK الذي يمكن أن يكون؟

في مثل هذه الحالات ، نكتب جدول CREATE فارغ. خلاصة القول هي أن نظام ديف يمكن دائما التبديل بسهولة بين الفروع. بالنسبة لـ PROD ، قررت إدارة المراجعة التي لا رجعة فيها بالفعل على مستوى مختلف. يمكنني عمل نسخة من الجدول ، أو إعادة تسميته بدلاً من حذفه. لكن مبدأ ترحيل الكتابة - التراجع ملزم بإرجاع بنية القاعدة إلى المستوى الأولي ، والبيانات ممكنة بالفعل.

في بيئة القتال ، استخدمت التراجع 1-2 مرات فقط في حياتي. وفي ديف طوال الوقت. لذلك ، أتحقق دائمًا من أن الاستعادة تُرجع كل شيء إلى الحالة المطلوبة.

في كثير من الأحيان ، يمكن للمطورين ارتكاب أخطاء في الاستعادة. بسبب أنها تركز في المقام الأول على التعديلات الجديدة ، يتم اختبارها والعمل معهم. الأشخاص والعمليات الأخرى تعمل بالفعل مع الاستعادة. لذلك ، أنا دائمًا اختبر عمليات الترحيل UP - ROLLBACK - UP

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

 ALTER TABLE abc ALTER COLUMN code SET NULL 

عظيم! بعد الاختبار ، قاعدة البيانات مليئة بالقيم الفارغة. هل استرجاع:

 ALTER TABLE abc ALTER COLUMN code SET NOT NULL 

والعكس بالعكس :-(

تحتاج إلى إضافة الأمر:

 DELETE FROM abc WHERE code IS NULL 

تكمن الصعوبة في أنك بحاجة إلى وضع ذلك في الاعتبار وعدم تشغيله تلقائيًا إذا لم نتحدث عن إعادة إنشاء قاعدة البيانات من نقطة الصفر في كل مرة.

قليلا عن حذف البيانات

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

 ALTER TABLE user_logs RENAME TO user_logs_20190223; --  CREATE TABLE user_logs_20190223 AS TABLE user_logs; 

المهاجر


نحن نعمل مع Laravel الآن - لديه محرك إدارة هجرة قياسي ومألوف. إذا كنت تريد ذلك ، فاكتب حتى باللغة SQL الخالصة ، على الرغم من أنها لا تزال في فئة PHP. لكن محاولاتي المتكررة لجعلها تعمل بالطريقة التي نحتاجها أدت إلى إعادة الشراء منفصلة:

  • يتكون الحل من جزأين - lib وتطبيق لوحدة تحكم محددة (Laravel، Symfony). يمكنك الاندماج في أي وحدة تحكم ، أو على الأقل في كمامة الويب.
  • لا يوجد التكوين والاتصال - لماذا ، عندما يكون بالفعل في المشروع الخاص بك. التشبث اتصالك إلى الواجهة وتذهب.
  • يتم تخزين الاستعادة SQL في قاعدة البيانات. هذا ضروري للتبديل بين الفروع.
  • اختبار على Postgesql ، Mysql (لا المعاملات). انها مناسبة من حيث المبدأ لأي قواعد وهياكل ، لأنه يتم استخدام التنسيق الخام.


المراجع
- الهجرات ليب
- التنفيذ تحت لارافيل / الحرفي
- التنفيذ تحت Symfony / Console

Source: https://habr.com/ru/post/ar435438/


All Articles