PostgreSQL Antipatterns: تغيير البيانات بتجاوز الزناد

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

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

دعنا فقط نوقف المشغلات!


BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; --  -
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

في الواقع ، هذا كل شيء - كل شيء معلق بالفعل .

لأنه ALTER TABLEيفرض قفل AccessExclusive ، حيث لا يمكن لأي شخص يعمل بشكل متوازٍ ، حتى لو كان بسيطًا SELECT، قراءة أي شيء من الجدول. أي ، حتى اكتمال هذه الصفقة ، سينتظر كل من يريد "القراءة للتو". ونتذكر UPDATEأننا نفعل يا أولجي ...

لنقم بإيقاف تشغيله بسرعة ثم تشغيله بسرعة!


BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;

UPDATE ...;

BEGIN;
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

هنا الوضع أفضل ، ووقت الانتظار أقصر بكثير. لكن مشكلتان فقط تفسدان كل الجمال:

  • ALTER TABLE ينتظر جميع العمليات الأخرى على الطاولة ، بما في ذلك طويلة SELECT
  • عندما يكون الزناد متوقفًا ، فإن أي تغيير في الجدول سيطير ، ولا حتى تغييرنا. حسنًا ، لن تدخل الوحدات ، على الرغم من ذلك. مشكلة!

إدارة الدورة المتغيرة


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

session_replication_role


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

SET session_replication_role = replica; --  
UPDATE ...;
SET session_replication_role = DEFAULT; --    

الحالة داخل الزناد


ولكن الخيار أعلاه يعمل لجميع المشغلات دفعة واحدة (أو تحتاج إلى "تشغيل" المشغلات مقدمًا ولا تريد تعطيلها). وإذا كنا بحاجة إلى "إيقاف" مشغل محدد واحد ؟

سيساعدنا متغير جلسة "المستخدم" في ما يلي:
تتم كتابة أسماء معلمات الامتدادات على النحو التالي: اسم الامتداد ، والنقطة ، ثم اسم المعلمة نفسها ، مثل الأسماء الكاملة للكائنات في SQL. على سبيل المثال: plpgsql.variable_conflict.
نظرًا لأنه يمكن تعيين المعلمات غير المتعلقة بالنظام في العمليات التي لا تقوم بتحميل وحدة الامتداد المقابلة ، فإن PostgreSQL تقبل قيم أي أسماء ذات مكونين .
أولاً ، قم بتعديل الزناد ، شيء من هذا القبيل:
BEGIN
    --     
    IF current_setting('mycfg.my_table_convert_process') = 'TRUE' THEN
        IF TG_OP IN ('INSERT', 'UPDATE') THEN
            RETURN NEW;
        ELSE
            RETURN OLD;
        END IF;
    END IF;
...

بالمناسبة ، يمكن القيام بذلك "بشكل مربح" ، بدون أقفال ، من خلال CREATE OR REPLACEوظيفة الزناد. ثم في الاتصال الخاص نقوم بتخزين المتغير "لدينا":

SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; --    

هل تعرف طرق أخرى؟ شارك في التعليقات.

All Articles