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

منطقيا ، يمكن اعتبار مجلة باعتبارها سلسلة من السجلات من أطوال مختلفة. يحتوي كل سجل على
بيانات حول عملية معينة ، مسبوقة
برأس قياسي. يشير العنوان ، من بين أشياء أخرى ، إلى:
- رقم المعاملة التي ينتمي إليها السجل.
- مدير الموارد - مكون النظام المسؤول عن التسجيل ؛
- الاختباري (CRC) - يسمح لك بتحديد تلف البيانات ؛
- طول السجل وصلة إلى السجل السابق.
البيانات نفسها لها تنسيق ومعنى مختلف. على سبيل المثال ، يمكن أن يمثلوا جزءًا من الصفحة التي يجب كتابتها على محتوياتها مع إزاحة معينة. يفهم مدير الموارد المحدد كيفية تفسير البيانات في سجله. هناك مديرين منفصلين للجداول ، لكل نوع من أنواع الفهرس ، لحالة المعاملة ، وما إلى ذلك. يمكن الحصول على قائمة كاملة بها إذا رغبت في الأمر
pg_waldump -r list
الجهاز المادي
على القرص ، يتم تخزين السجل كملفات في الدليل $ PGDATA / pg_wal. كل ملف افتراضي إلى 16 ميغابايت. يمكن زيادة الحجم لتجنب عدد كبير من الملفات في دليل واحد. قبل PostgreSQL 11 ، يمكن القيام بذلك فقط عند تجميع الكود المصدري ، لكن الآن يمكنك تحديد الحجم عند تهيئة الكتلة (
--wal-segsize
).
إدخالات السجل تندرج في الملف الحالي قيد الاستخدام ؛ عندما ينتهي ، يبدأ استخدام التالي.
يتم تخصيص المخازن المؤقتة الخاصة للسجل في الذاكرة المشتركة للخادم. يتم تعيين حجم ذاكرة التخزين المؤقت للمجلة اليومية بواسطة المعلمة
wal_buffers (القيمة الافتراضية تعني التكوين التلقائي: يتم تخصيص 1/32 من ذاكرة التخزين المؤقت المخزن المؤقت).
يتم تخزين ذاكرة التخزين المؤقت للمجلة كذاكرة التخزين المؤقت للمخزن المؤقت ، لكنها تعمل بشكل أساسي في وضع المخزن المؤقت الحلقي: تتم إضافة الإدخالات إلى "الرأس" وتتم كتابتها على القرص من "الذيل".
تُظهر مواضع التسجيل ("الذيل") والإدراج ("الرأس") الدالتين pg_current_wal_lsn و pg_current_wal_insert lsn على التوالي:
=> SELECT pg_current_wal_lsn(), pg_current_wal_insert_lsn();
pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 0/331E4E64 | 0/331E4EA0 (1 row)
للإشارة إلى سجل معين ، يتم استخدام نوع البيانات pg_lsn (LSN = رقم تسلسل السجل) - هذا هو رقم 64 بت يمثل إزاحة البايت قبل السجل بالنسبة لبداية السجل. يتم إخراج LSN كرقمين 32 بت بترميز سداسي عشري.
يمكنك معرفة أي ملف سنعثر على الموضع المطلوب ، ومع أي إزاحة من بداية الملف:
=> SELECT file_name, upper(to_hex(file_offset)) file_offset FROM pg_walfile_name_offset('0/331E4E64');
file_name | file_offset --------------------------+------------- 000000010000000000000033 | 1E4E64 \ /\ / 0/331E4E64
يتكون اسم الملف من جزأين. تُظهر الأرقام السداسية عشرية العشرة العليا عدد فرع الوقت (يتم استخدامه عند الاستعادة من النسخة الاحتياطية) ، والباقي يتوافق مع أعلى أرقام LSN (وتشير الأرقام LSN المتبقية المتبقية إلى الإزاحة).
يمكن عرض ملفات السجل على نظام الملفات في دليل PGDATA / pg_wal / $ ، ولكن بدءًا من PostgreSQL 10 ، يمكن أيضًا رؤيتها بوظيفة خاصة:
=> SELECT * FROM pg_ls_waldir() WHERE name = '000000010000000000000033';
name | size | modification --------------------------+----------+------------------------ 000000010000000000000033 | 16777216 | 2019-07-08 20:24:13+03 (1 row)
الكتابة إلى الأمام
دعونا نرى كيف يحدث يوميات وكيف يتم توفير تسجيل استباقية. إنشاء جدول:
=> CREATE TABLE wal(id integer); => INSERT INTO wal VALUES (1);
سوف ننظر إلى رأس صفحة الجدول. للقيام بذلك ، نحتاج إلى امتداد مألوف بالفعل:
=> CREATE EXTENSION pageinspect;
لنبدأ المعاملة ونتذكر موضع الإدراج في السجل:
=> BEGIN; => SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/331F377C (1 row)
لنقم الآن ببعض العمليات ، على سبيل المثال ، قم بتحديث السطر:
=> UPDATE wal set id = id + 1;
تم تسجيل هذا التغيير في السجل ، تم تغيير موضع الإدراج:
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/331F37C4 (1 row)
للتأكد من عدم دفع صفحة البيانات المعدلة إلى القرص قبل إدخال دفتر اليومية ، يتم تخزين LSN لآخر إدخال دفتر اليومية المتعلقة بهذه الصفحة في رأس الصفحة:
=> SELECT lsn FROM page_header(get_raw_page('wal',0));
lsn ------------ 0/331F37C4 (1 row)
ضع في اعتبارك أن المجلة شائعة في المجموعة بالكامل ، وتندرج إدخالات جديدة فيها طوال الوقت. لذلك ، قد يكون LSN على الصفحة أقل من القيمة التي تم إرجاع الدالة pg_current_wal_insert_lsn للتو. لكن لا شيء يحدث في نظامنا ، وبالتالي فإن الأرقام هي نفسها.
الآن إكمال المعاملة.
=> COMMIT;
ينتقل سجل الالتزام أيضًا إلى السجل ، ويتغير الموضع مرة أخرى:
=> SELECT pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn --------------------------- 0/331F37E8 (1 row)
يقوم Commit بتغيير حالة المعاملة في بنية تسمى XACT (
تحدثنا عنها بالفعل ). يتم تخزين الحالات في ملفات ، لكنها تستخدم أيضًا ذاكرة التخزين المؤقت الخاصة بها ، والتي تشغل 128 صفحة في الذاكرة المشتركة. لذلك ، بالنسبة لصفحات XACT ، يجب تتبع LSN لآخر إدخال في دفتر اليومية. ولكن لا يتم تخزين هذه المعلومات في الصفحة نفسها ، ولكن في ذاكرة الوصول العشوائي.
في مرحلة ما ، سيتم كتابة إدخالات دفتر اليومية التي تم إنشاؤها على القرص. في أي واحدة - سنتحدث مرة أخرى ، ولكن في حالتنا حدث هذا بالفعل:
=> SELECT pg_current_wal_lsn(), pg_current_wal_insert_lsn();
pg_current_wal_lsn | pg_current_wal_insert_lsn --------------------+--------------------------- 0/331F37E8 | 0/331F37E8 (1 row)
بعد هذه النقطة ، يمكن إخراج البيانات وصفحات XACT من ذاكرة التخزين المؤقت. ولكن إذا كان مطلوبًا إجبارهم على الخروج في وقت مبكر ، فسيتم اكتشافه وسيتم فرض إدخالات دفتر اليومية على التسجيل أولاً.
من خلال معرفة وضعي LSN ، يمكنك الحصول على حجم إدخالات دفتر اليومية بينهما (بالبايت) عن طريق طرح موضع واحد من الآخر. تحتاج فقط إلى إلقاء المواضع على نوع pg_lsn:
=> SELECT '0/331F37E8'::pg_lsn - '0/331F377C'::pg_lsn;
?column? ---------- 108 (1 row)
في هذه الحالة ، يتطلب تحديث الخط والالتزام 108 بايت في السجل.
بنفس الطريقة ، يمكنك تقدير مقدار إدخالات دفتر اليومية التي يتم إنشاؤها بواسطة الخادم لكل وحدة زمنية عند تحميل معين. هذه هي المعلومات الهامة التي ستكون مطلوبة أثناء الإعداد (والتي سنتحدث عنها في المرة القادمة).
الآن سوف نستخدم الأداة المساعدة pg_waldump للنظر في إدخالات السجل التي تم إنشاؤها.
الأداة المساعدة يمكن أن تعمل مع نطاق LSN (كما في هذا المثال) وتحديد سجلات للمعاملة المحددة. يجب تشغيله نيابة عن postgres OS user ، لأنها تحتاج إلى الوصول إلى ملفات السجل على القرص.
postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/331F377C -e 0/331F37E8 000000010000000000000033
rmgr: Heap len (rec/tot): 69/ 69, tx: 101085, lsn: 0/331F377C, prev 0/331F3014, desc: HOT_UPDATE off 1 xmax 101085 ; new off 2 xmax 0, blkref #0: rel 1663/16386/33081 blk 0
rmgr: Transaction len (rec/tot): 34/ 34, tx: 101085, lsn: 0/331F37C4, prev 0/331F377C, desc: COMMIT 2019-07-08 20:24:13.945435 MSK
هنا نرى رؤوس الإدخالات اثنين.
الأول هو عملية
HOT_UPDATE ، المتعلقة بمدير موارد كومة الذاكرة المؤقتة. يشار إلى اسم الملف ورقم الصفحة في حقل blkref ومطابقة صفحة الجدول المحدثة:
=> SELECT pg_relation_filepath('wal');
pg_relation_filepath ---------------------- base/16386/33081 (1 row)
الإدخال الثاني هو COMMIT ، المتعلق بـ Resource Resource Manager.
ليس التنسيق الأكثر قابلية للقراءة ، ولكن يمكنك معرفة ذلك إذا لزم الأمر.
انتعاش
عندما نبدأ تشغيل الخادم ، تبدأ عملية مدير مكتب البريد أولاً ، وتبدأ بدورها عملية بدء التشغيل التي تتمثل مهمتها في ضمان الاسترداد في حالة حدوث عطل.
لتحديد ما إذا كان الاسترداد مطلوبًا ، يبحث بدء التشغيل في ملف التحكم الخاص $ PGDATA / global / pg_control ويبحث في حالة الكتلة. يمكننا التحقق من الحالة بأنفسنا باستخدام الأداة المساعدة pg_controldata:
postgres$ /usr/lib/postgresql/11/bin/pg_controldata -D /var/lib/postgresql/11/main | grep state
Database cluster state: in production
سيكون للخادم الذي تم إيقافه بدقة الحالة "إيقاف التشغيل". إذا لم يعمل الخادم ، وظلت الحالة "قيد الإنتاج" ، فهذا يعني أن نظام إدارة قواعد البيانات قد انخفض ثم سيتم تنفيذ الاسترداد تلقائيًا.
من أجل الاسترداد ، ستقوم عملية بدء التشغيل بقراءة السجل بالتتابع وتطبيق الإدخالات على الصفحات ، إذا لزم الأمر. يمكنك التحقق من الحاجة عن طريق مقارنة LSN للصفحة الموجودة على القرص مع LSN لإدخال دفتر اليومية. إذا كان LSN للصفحة أكبر ، فإن السجل ليس ضروريًا. لكن في الواقع - هذا غير ممكن ، لأن السجلات مصممة للتطبيق المتسق تمامًا.
هناك استثناءات. يتم تكوين بعض السجلات كصورة صفحة كاملة (FPI ، صورة صفحة كاملة) ، ومن الواضح أنه يمكن تطبيق هذه الصورة على صفحة في أي حالة - ستظل تمسح كل ما كان هناك. يمكن تطبيق تغيير آخر في حالة المعاملة على أي إصدار من صفحة XACT - لذلك ، داخل هذه الصفحات ليست هناك حاجة لتخزين LSN.
يحدث تغيير الصفحات أثناء الاسترداد في ذاكرة التخزين المؤقت المخزن المؤقت ، كما هو الحال أثناء العمل العادي - لهذا مدير مكتب البريد يبدأ عمليات الخلفية اللازمة.
وبالمثل ، تنطبق إدخالات دفتر اليومية على الملفات: على سبيل المثال ، إذا ذكر أحد السجلات أن الملف يجب أن يكون موجودًا ، لكنه غير موجود ، يتم إنشاء الملف.
حسنًا ، في نهاية عملية الاسترداد ، يتم الكتابة على جميع الجداول غير اليومية ب "دمى" من
طبقات التهيئة .
هذا هو عرض مبسط للغاية من الخوارزمية. على وجه الخصوص ، لم نذكر أي شيء حول مكان بدء قراءة إدخالات دفتر اليومية (يجب تأجيل هذه المحادثة حتى يتم النظر في نقطة التفتيش).
والتوضيح الأخير. تتكون عملية الاسترداد "الكلاسيكية" من مرحلتين. في المرحلة الأولى (إعادة التوجيه) ، يتم إرجاع إدخالات دفتر اليومية ، ويكرر الخادم كل العمل المفقود أثناء الفشل. في الثاني (التراجع) ، يتم التراجع عن المعاملات التي لم يتم الالتزام بها في وقت الفشل. لكن PostgreSQL لا يحتاج إلى مرحلة ثانية. كما ذكرنا
سابقًا ، نظرًا لخصائص تنفيذ المعاملات متعددة الإصدارات ، لا تحتاج إلى التراجع فعليًا ؛ يكفي أنه لن يتم تعيين بت الإصلاح في XACT.
أن تستمر .