Antipatterns PostgreSQL: قتال جحافل "الموتى"

تسمح خصائص الآليات الداخلية لـ PostgreSQL بأن تكون سريعة جدًا في بعض المواقف و"ليست سريعة جدًا" في حالات أخرى. سنركز اليوم على مثال كلاسيكي للتعارض بين كيفية عمل نظام إدارة قواعد البيانات (DBMS) وما يفعله المطور به - UPDATE مقابل مبادئ MVCC.

قصة مختصرة من مقالة عظيمة:

عندما يتم تعديل صف بواسطة أمر UPDATE، يتم تنفيذ عمليتين فعليًا: DELETE وINSERT. في الإصدار الحالي من السلسلة يتم تعيين xmax مساويا لعدد المعاملة التي نفذت التحديث. ثم يتم إنشاؤه نسخة جديدة نفس الخط؛ تتطابق قيمة xmin مع قيمة xmax للإصدار السابق.

بعد مرور بعض الوقت على اكتمال هذه المعاملة، يعتمد الإصدار القديم أو الجديد على COMMIT/ROOLBACK، سيتم التعرف عليه "ميت" (صفوف ميتة) عند المرور VACUUM وفقا للجدول وتطهيرها.

Antipatterns PostgreSQL: قتال جحافل "الموتى"

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

رقم 1: أحب تحريكه

لنفترض أن طريقتك تعمل وفقًا لمنطق الأعمال، وفجأة أدركت أنه سيكون من الضروري تحديث الحقل X في بعض السجلات:

UPDATE tbl SET X = <newX> WHERE pk = $1;

ثم، مع تقدم التنفيذ، اتضح أنه يجب أيضًا تحديث الحقل Y:

UPDATE tbl SET Y = <newY> WHERE pk = $1;

... ثم أيضًا Z - لماذا نضيع الوقت في تفاهات؟

UPDATE tbl SET Z = <newZ> WHERE pk = $1;

كم عدد إصدارات هذا السجل الموجودة لدينا الآن في قاعدة البيانات؟ نعم 4 قطع! من بين هذه العناصر، هناك واحدة ذات صلة، وسيتعين تنظيف 3 بعدك بواسطة فراغ [تلقائي].

لا تفعل ذلك بهذه الطريقة! يستخدم تحديث كافة الحقول في طلب واحد - يمكن دائمًا تغيير منطق الطريقة على النحو التالي:

UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;

رقم 2: الاستخدام مختلف عن لوك!

لذلك، كنت لا تزال تريد تحديث العديد والعديد من السجلات في الجدول (أثناء استخدام البرنامج النصي أو المحول، على سبيل المثال). وشيء من هذا القبيل يدخل في البرنامج النصي:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;

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

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;

كثير من الناس لا يدركون وجود مثل هذا المشغل الرائع، لذلك إليك ورقة الغش IS DISTINCT FROM والعوامل المنطقية الأخرى للمساعدة:
Antipatterns PostgreSQL: قتال جحافل "الموتى"
... والقليل عن العمليات المعقدة ROW()-التعبيرات:
Antipatterns PostgreSQL: قتال جحافل "الموتى"

#3: أتعرف على حبيبتي عن طريق...الحجب

يتم إطلاقها عمليتان متوازيتان متماثلتان، يحاول كل منها وضع علامة على الإدخال بأنه "قيد التقدم":

UPDATE tbl SET processing = TRUE WHERE pk = $1;

حتى لو كانت هذه العمليات تقوم بالفعل بأشياء مستقلة عن بعضها البعض، ولكن ضمن نفس المعرف، فسيتم "تأمين" العميل الثاني على هذا الطلب حتى اكتمال المعاملة الأولى.

القرار رقم 1: يتم تقليل المهمة إلى سابقتها

دعنا نضيفه مرة أخرى IS DISTINCT FROM:

UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;

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

القرار رقم 2: الأقفال الاستشارية

موضوع كبير لمقال منفصل يمكنك أن تقرأ عنه طرق التطبيق و "أشعل النار" لحجب التوصيات.

القرار رقم 3: مكالمات غبية

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

المصدر: www.habr.com

إضافة تعليق