كيف أصلحت قاعدة مكسورة وما جاء منها

بمجرد أن طُلب مني المساعدة في "إصلاح" قاعدة بيانات واحدة. أعطى CHECKDB أثناء التحقق قائمة من الأخطاء ، والتي تم وضع علامة "الفاسد" بعضها. التطبيق يعمل ، لكنه كان لا يزال مضطرب إلى حد ما.

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

تاريخ


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

Index row (1:386974:44) with values (C_FK_6bb5032ec2f94557a7d4a9d39a356168 = '04DA7FC4-B8F2-4D97-B8D2-B207A918D3DF' and C_PK_dd87d9cad5504a1199d4ddaf511ea6a9 = 'F44E8C34-862D-4952-8821-D0E87143A74E') pointing to the data row identified by (C_PK_dd87d9cad5504a1199d4ddaf511ea6a9 = 'F44E8C34-862D-4952-8821-D0E87143A74E'). 

وبعض الأخطاء الأكثر خطورة:

 Table error: Object ID 1333579789, index ID 1, partition ID 72057609974841344, alloc unit ID 72057610075701248 (type In-row data). Page (1:20426) is missing a reference from previous page (1:267203). Possible chain linkage problem. 

حسنا اذن تم تحديد حجم العمل ، دعنا نبدأ!

الأخطاء الصحيحة


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

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

هذه المهمة ليست إبداعية تمامًا ، لذا يمكنك تكليفها بأمان بسيارة لا قيمة لها. تنفيذ الأمر

 DBCC CHECKDB (< >, REPAIR_REBUILD) 

ودراسة التقرير المرحلي.

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

اضطراب اتصال البيانات


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

يمكنك بالطبع الاستسلام والتقطيع باستخدام أمر CHECKDB DBCC (<اسم DB> ، REPAIR_ALLOW_DATA_LOSS). لكن ... لا أريد أن أفقد البيانات. أريد استعادة كل ما هو ممكن إلى الحد الأقصى. لذلك ، دعونا نرى بمزيد من التفصيل ما يخبرنا به تقرير الخطأ بشكل عام.

 Table error: Object ID 1333579789, index ID 1, partition ID 72057609974841344, alloc unit ID 72057610075701248 (type In-row data). Page (1:20426) is missing a reference from previous page (1:267203). Possible chain linkage problem. Table error: Object ID 1333579789, index ID 1, partition ID 72057609974841344, alloc unit ID 72057610075701248 (type In-row data). The previous link (1:267203) on page (1:267204) does not match the previous page (1:20426) that the parent (1:218898), slot 213 expects for this page. Table error: Object ID 1333579789, index ID 1, partition ID 72057609974841344, alloc unit ID 72057610075701248 (type In-row data). B-tree chain linkage mismatch. (1:20426)->next = (1:267204), but (1:267204)->Prev = (1:267203). 

يحتوي التقرير على قصة غامضة تتوقع إحدى الصفحات رؤية أحد الجيران ، لكن الجار لا يعرف شيئًا عنها. بعض البحارة pelevinsky Zheleznyak: ذهب على سطح السفينة ، ولكن لا سطح السفينة.

للحصول على فهم كامل ، هناك حاجة إلى مزيد من التفاصيل وسيتعين عليهم الانتقال مباشرةً إلى محتوى الصفحات. ولكن قبل ذلك ، سنكتشف كيف ترتبط صفحات فهرس SQL Server بشكل عام.

من الواضح أن الصفحات تحتوي على روابط "رأسية" هرمية ، والتي تشكل شجرة B. تحتوي الصفحة العليا على روابط لصفحات ذات مستوى أدنى وما إلى ذلك إلى الأوراق ذاتها. كما قلت سابقًا ، هذا مناسب جدًا للعثور على القيم: تريد العثور على "Vasya Pupkin" وبعد بضع صفحات ("من B إلى G" → "من Ba إلى Bb" → "Vasya Pupkin") ستجد القيمة المطلوبة.

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

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

نحن بحاجة للذهاب أعمق!


لعرض الصفحات ، سوف نستخدم فريق DBCC PAGE القديم الذي يستحقه جيدًا وغير الموثق. يستغرق 4 المعلمات:

  • قاعدة العيد
  • معرف الملف الأساسي
  • معرف الصفحة
  • مستوى التفاصيل (0 إلى 3)

بناءً على المعلمة الأخيرة ، يمكنك فقط رؤية عنوان الخدمة (0) ، أو محتوى الصفحة بالكامل (3) ، أو العنوان وجزء من المحتوى (1 و 2)

بالمناسبة ، في SQL Server 2019 ، ظهرت أخيراً طريقة عرض موثقة sys.db_db_page_info ، وهي تؤدي مهام مشابهة. لسوء الحظ ، لا يعرض سوى بيانات الرأس (التناظرية حتى التمرير لأسفل 0) ، لذلك لا يزال لا يجيب على مهامنا بالكامل.

لذلك ، بالنسبة للمبتدئين ، تنفيذ الأمر

 DBCC TRACEON (3604, 1) 

بحيث يحصل إخراج أوامر DBCC المتبقية على وحدة التحكم الخاصة بنا ، وليس إلى ErrorLog

بعد ذلك ، انظر إلى عنوان الصفحة 20426:

 DBCC PAGE (11, 1, 20426, 0) 

صورة

انا ارى الصفحة تريد أن تكون في المنتصف بين الصفحات 267203 و 267204. لكن ماذا عن هذه الصفحات نفسها؟

صورة

صورة

لا نوم ولا روح! انهم بخير دون ضيوف غير المدعوين.
حسنًا ، دعنا ننظر إلى جدول المحتويات القادم:

صورة

اللغز يتطور تدريجيا:

  • بالنسبة للارتباطات "الرأسية" (هيكل شجرة الفهرس) ، يجب أن تكون الصفحة 20426 بين 267203 و 267204
  • تتناقض الوصلات الأفقية مع هذا وتقول إنه لا يوجد أحد بين 267203 و 267204.

الآن دعونا نحاول فهم نوع البيانات التي عانت من الفشل.

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

يمكن الاطلاع على المفتاح ببساطة في وصف الفهرس. سوف نستخدم الأمر sp_helpindex لهذا الغرض. في هذه الحالة ، المفتاح هو عمود واحد فقط ، بما في ذلك. إيلاء الاهتمام لها.

صورة

من أجل العثور على المفاتيح التي حصلت على الصفحة ، نستخدم DBCC PAGE بحد أقصى 3 من التفاصيل. إذا قمت بالتمرير خلال سجل الإخراج ، يمكنك مشاهدة المحتويات "الأولية" لكل سطر وتفصيل لكل حقل:

صورة

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

يوضح فحص الصفحات أن الصفحة 20426 تحتوي على نصف الأسطر بالضبط من الصفحة 267203. تصبح طبيعة الفشل واضحة. عندما تكون صفحة قاعدة البيانات ممتلئة ولا تحتوي على مساحة إضافية لإدراج بيانات جديدة ، يتم تقسيمها إلى نصفين إلى صفحتين جديدتين. على ما يبدو ، عندما أصبحت صفحة 267203 ممتلئة ، تم إنشاء صفحة المشكلة 20426. بدأ نظام إدارة قواعد البيانات في إعادة بناء روابطه: لقد تمكن من كتابة صفحة جديدة إلى بنية الفهرس. لكن لسبب ما ، لا يمكن تحديث السجلات الأفقية و "تعليق" الصفحة الجديدة في حالة غير محددة.

ماذا سيكون كل هذا بالنسبة لنا؟


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

  • يتم تنفيذ التحديدات "الواسعة" (على سبيل المثال ، للتقارير) باستخدام السجلات الأفقية. من الأسهل التنقل بين عدة صفحات بالتسلسل ، واختيار نطاق كبير
  • يتم تنفيذ استعلامات "Point" (تحديث سجل محدد) من خلال بحث "جدول المحتويات".

اتضح عند تغيير سجلات محددة ، يتم الوصول إلى DBMS إلى صفحة "المشكلة" 20426. وعندما يقوم بتشغيل التقرير ، فإنه يقرأ البيانات "أفقياً" ولا يرى التغييرات التي تم إجراؤها. مرة أخرى: في الممارسة العملية ، قد تكون الخوارزمية أكثر تعقيدًا ، لكن فئة المشكلات المحتملة ما زالت مفهومة.

الأسئلة الأبدية للمفكر الروسي


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

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

ما يجب القيام به هو أكثر وضوحا. لاستعادة اتصال الجدول ، يكفي نسخ بياناته إلى جدول آخر بحيث يتم استخدام القراءة الأفقية عند النسخ. للإخلاص ، يمكنك تحديد طريقة الوصول الصحيحة إلى نظام إدارة قواعد البيانات بشكل صريح باستخدام تلميح FORCESCAN

 select * into T_bca79e9e77c24cdc8bbb7cfd0ddc16fd_BKP from T_bca79e9e77c24cdc8bbb7cfd0ddc16fd with (FORCESCAN) 

بعد ذلك ، يمكنك مسح الجدول المصدر وإرجاع البيانات المنسوخة هناك.

ولكن بعد ذلك ، سيتم فقد التغييرات التي تم إجراؤها في الصفحة 20426. لذلك ، قبل عمل نسخة من الجدول ، ستحتاج إلى نسخ الصفوف ذات المعرفات من الصفحة 20426. وبعد استعادة الجدول ، قم بتصحيح الإدخالات اللازمة. الوصول إلى سجلات الصفحة 20426 ضروري بشكل صريح بواسطة المعرفات:

 select * from T_bca79e9e77c24cdc8bbb7cfd0ddc16fd where C_PK_bca79e9e77c24cdc8bbb7cfd0ddc16fd = '' 

يمكن الحصول على المعرفات من خلال قراءة محتويات الصفحة بنفس صفحة DBCC. كما كتبت بالفعل ، كان هناك بالضبط نصف هذه الصفحات 267203 ، أي 15 رابط.

النسخ الاحتياطي للصفحات التالفة ، وإعادة تحميل الجدول ومطابقة السجلات المطابقة - بعد بضع دقائق من استعادة الجدول.

الصيحة ، فزنا! لكن هل هذا صحيح؟


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

ماذا تقرأ عن هذا الموضوع


  • بناء جملة الأوامر CHECKDB (الانتباه إلى التحذيرات حول المخاطر المحتملة لاستخدام الأمر!)
  • الوصف غير الرسمي لـ DBCC PAGE
  • مقالة جيدة عن فهارس MS SQL التي تشرح الكثير من الأشياء المثيرة للاهتمام. بما في ذلك كيفية تخزين الفهارس المادية في قاعدة البيانات

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


All Articles