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

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

لفترة وجيزة مؤامرة من مقال ممتاز :
عندما يتم تعديل خط باستخدام الأمر UPDATE ، يتم تنفيذ عمليتين: DELETE و INSERT. في الإصدار الحالي من الخط ، يتم تعيين xmax مساوٍ لعدد المعاملات التي أجرت UPDATE. ثم يتم إنشاء نسخة جديدة من نفس السطر ؛ تتطابق قيمة xmin مع قيمة xmax للإصدار السابق.
بعد مرور بعض الوقت على الانتهاء من هذه المعاملة ، COMMIT/ROOLBACKسيتم التعرف على الإصدار القديم أو الجديد ، اعتمادًا على أي منها ، على أنه "ميت" (الصفوف الميتة) عند المرور VACUUMعبر الطاولة وتنظيفها.



لكن هذا لن يحدث على الفور ، ولكن يمكن اكتشاف المشاكل المتعلقة بـ "القتلى" بسرعة كبيرة - من خلال التحديثات المتعددة أو الجماعية للسجلات في جدول كبير ، وبعد ذلك بقليل ، في مواجهة موقف لن تتمكن فيه 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المنطقية الأخرى للمساعدة:

... وقليلا عن العمليات على ROW()التعابير المعقدة :

رقم 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 : بدون [د] مكالمات ذكية

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

All Articles