WAL في بوستجرس: 1. ذاكرة التخزين المؤقت المخزن المؤقت

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

ستتألف هذه الدورة من أربعة أجزاء:

  • ذاكرة التخزين المؤقت المخزن المؤقت (هذه المقالة) ؛
  • مجلة Prerecord - كيف يتم ترتيبها وكيف يتم استخدامها أثناء الانتعاش ؛
  • نقطة تفتيش وتسجيل الخلفية - لماذا هم بحاجة وكيف يتم تكوينها ؛
  • ضبط سجل - المستويات والمهام التي يتعين حلها والموثوقية والأداء.

لماذا اليومية ضرورية؟


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

ولكن ماذا سيحدث في حالة حدوث عطل ، على سبيل المثال ، عند إيقاف تشغيل الطاقة أو في حالة حدوث خطأ في كود DBMS أو نظام التشغيل؟ سيتم فقد جميع محتويات ذاكرة الوصول العشوائي ، وستبقى البيانات المكتوبة على القرص فقط (مع بعض أنواع الإخفاقات ، قد يعاني القرص أيضًا ، ولكن في هذه الحالة لن تساعد إلا نسخة احتياطية). من حيث المبدأ ، يمكن تنظيم الإدخال / الإخراج بطريقة يتم بها الاحتفاظ بالبيانات الموجودة على القرص دائمًا في حالة متسقة ، ولكن هذا أمر صعب وغير فعال (على حد علمي ، فايربيرد فقط هو الذي ذهب بهذه الطريقة).

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

ذاكرة التخزين المؤقت المخزن المؤقت


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

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

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

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

ولكن العودة إلى ذاكرة التخزين المؤقت DBMS.

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

  • الموقع على قرص الصفحة في المخزن المؤقت (ملف ورقم كتلة فيه) ؛
  • إشارة إلى أن البيانات الموجودة على الصفحة قد تغيرت وأن عاجلاً أم آجلاً يجب كتابتها على القرص (يسمى هذا المخزن المؤقت بالتسخير ) ؛
  • عدد المكالمات إلى المخزن المؤقت (عدد الاستخدام) ؛
  • علم تثبيت المخزن المؤقت (عدد الدبوس).

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



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

صفحة البحث في ذاكرة التخزين المؤقت


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

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

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

الازدحام


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

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

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

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

  1. يحتوي على عداد دخول صفري (عدد الاستخدام) ،
  2. وليست ثابتة (صفر رقم الدبوس).

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

بعد العثور على المخزن المؤقت ، يحدث ما يلي لذلك.

يتم تثبيت المخزن المؤقت لإظهار العمليات الأخرى التي يتم استخدامها. بالإضافة إلى الإصلاح ، يتم استخدام وسائل أخرى للحظر ، لكننا سنتحدث أكثر عن ذلك بشكل منفصل.

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

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

الآن يشير رابط "الضحية التالية" إلى المخزن المؤقت التالي ، والواحد الذي تم تحميله فقط لديه الوقت لزيادة عداد الدخول حتى يدور المؤشر حول ذاكرة التخزين المؤقت بأكملها ويعود مرة أخرى.

بأم عيني


كما هو معتاد في PostgreSQL ، هناك امتداد يسمح لك بالبحث داخل ذاكرة التخزين المؤقت المؤقتة.

=> CREATE EXTENSION pg_buffercache; 

إنشاء جدول وإدراج صف واحد فيه.

 => CREATE TABLE cacheme( id integer ) WITH (autovacuum_enabled = off); => INSERT INTO cacheme VALUES (1); 

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

 => SELECT bufferid, CASE relforknumber WHEN 0 THEN 'main' WHEN 1 THEN 'fsm' WHEN 2 THEN 'vm' END relfork, relblocknumber, isdirty, usagecount, pinning_backends FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('cacheme'::regclass); 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15735 | main | 0 | t | 1 | 0 (1 row) 

لذلك هو - هناك صفحة واحدة في المخزن المؤقت. إنه متسخ (isdirty) ، عداد العداد يساوي واحد (usagecount) ، ولا يتم إصلاحه بواسطة أي عملية (pinning_backends).

الآن قم بإضافة سطر آخر وكرر الاستعلام. لحفظ الحروف ، نقوم بإدراج سطر في جلسة أخرى ، ونكرر الطلب الطويل باستخدام الأمر \g .

 | => INSERT INTO cacheme VALUES (2); 

 => \g 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15735 | main | 0 | t | 2 | 0 (1 row) 

تمت إضافة لم المخازن المؤقتة الجديدة - السطر الثاني احتواء على نفس الصفحة. يرجى ملاحظة أن عداد الاستخدام قد زاد.

 | => SELECT * FROM cacheme; 
 | id | ---- | 1 | 2 | (2 rows) 

 => \g 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15735 | main | 0 | t | 3 | 0 (1 row) 

وبعد الوصول إلى الصفحة للقراءة ، يزداد العداد أيضًا.

وإذا كنت نظيفة؟

 | => VACUUM cacheme; 

 => \g 
  bufferid | relfork | relblocknumber | isdirty | usagecount | pinning_backends ----------+---------+----------------+---------+------------+------------------ 15731 | fsm | 1 | t | 1 | 0 15732 | fsm | 0 | t | 1 | 0 15733 | fsm | 2 | t | 2 | 0 15734 | vm | 0 | t | 2 | 0 15735 | main | 0 | t | 3 | 0 (5 rows) 

أنشأ التنظيف خريطة رؤية (صفحة واحدة) وخريطة مساحة حرة (ثلاث صفحات - الحد الأدنى لحجم هذه الخريطة).

حسنا وهلم جرا.

حجم الإعداد


يتم تعيين حجم ذاكرة التخزين المؤقت بواسطة المعلمة Shared_buffers . القيمة الافتراضية سخيفة 128 ميغابايت. هذه هي إحدى المعلمات التي من المنطقي زيادتها فورًا بعد تثبيت PostgreSQL.

 => SELECT setting, unit FROM pg_settings WHERE name = 'shared_buffers'; 
  setting | unit ---------+------ 16384 | 8kB (1 row) 

ضع في اعتبارك أن تغيير المعلمة يتطلب إعادة تشغيل الخادم ، حيث يتم تخصيص جميع ذاكرة التخزين المؤقت الضرورية عند بدء تشغيل الخادم.

لأي أسباب لاختيار القيمة المناسبة؟

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

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

التوصية القياسية هي أخذ 1/4 من ذاكرة الوصول العشوائي كتقريب أولي (بالنسبة لنظام Windows قبل PostgreSQL 10 ، يوصى باختيار حجم أصغر).

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

تأكد من إلقاء نظرة على تقرير نيكولاي ساموخفالوف في PgConf-2019: " مقاربة صناعية لضبط PostgreSQL: تجارب قاعدة البيانات "

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

على سبيل المثال ، يمكنك دراسة توزيع المخازن المؤقتة وفقًا لدرجة استخدامها:

 => SELECT usagecount, count(*) FROM pg_buffercache GROUP BY usagecount ORDER BY usagecount; 
  usagecount | count ------------+------- 1 | 221 2 | 869 3 | 29 4 | 12 5 | 564 | 14689 (6 rows) 

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

يمكنك معرفة مقدار الجداول المخزنة مؤقتًا في قاعدة البيانات الخاصة بنا ومدى فاعلية استخدام هذه البيانات (يعني الاستخدام النشط في هذا الاستعلام وجود مخازن مؤقتة بها عداد استخدام لأكثر من 3):

 => SELECT c.relname, count(*) blocks, round( 100.0 * 8192 * count(*) / pg_table_size(c.oid) ) "% of rel", round( 100.0 * 8192 * count(*) FILTER (WHERE b.usagecount > 3) / pg_table_size(c.oid) ) "% hot" FROM pg_buffercache b JOIN pg_class c ON pg_relation_filenode(c.oid) = b.relfilenode WHERE b.reldatabase IN ( 0, (SELECT oid FROM pg_database WHERE datname = current_database()) ) AND b.usagecount is not null GROUP BY c.relname, c.oid ORDER BY 2 DESC LIMIT 10; 
  relname | blocks | % of rel | % hot ---------------------------+--------+----------+------- vac | 833 | 100 | 0 pg_proc | 71 | 85 | 37 pg_depend | 57 | 98 | 19 pg_attribute | 55 | 100 | 64 vac_s | 32 | 4 | 0 pg_statistic | 27 | 71 | 63 autovac | 22 | 100 | 95 pg_depend_reference_index | 19 | 48 | 35 pg_rewrite | 17 | 23 | 8 pg_class | 16 | 100 | 100 (10 rows) 

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

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

  • يجب تكرارها عدة مرات: ستختلف الأرقام في حدود معينة ؛
  • ليس من الضروري القيام بها باستمرار (كجزء من المراقبة) نظرًا لحقيقة أن كتل الامتداد تعمل مع ذاكرة التخزين المؤقت المؤقتة لفترة قصيرة.

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

النزوح الجماعي


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

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

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

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

 => CREATE TABLE big( id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, s char(1000) ) WITH (fillfactor=10); => INSERT INTO big(s) SELECT 'FOO' FROM generate_series(1,4096+1); 

دعنا نحلل الجدول.

 => ANALYZE big; => SELECT relpages FROM pg_class WHERE oid = 'big'::regclass; 
  relpages ---------- 4097 (1 row) 

الآن يتعين علينا إعادة تشغيل الخادم لمسح ذاكرة التخزين المؤقت لبيانات الجدول التي يقرأها التحليل.

 student$ sudo pg_ctlcluster 11 main restart 

بعد إعادة التشغيل ، اقرأ الجدول بأكمله:

 => EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big; 
  QUERY PLAN --------------------------------------------------------------------- Aggregate (actual time=14.472..14.473 rows=1 loops=1) -> Seq Scan on big (actual time=0.031..13.022 rows=4097 loops=1) Planning Time: 0.528 ms Execution Time: 14.590 ms (4 rows) 

وتأكد من احتلال 32 صفحة فقط من صفحات جدولية في ذاكرة التخزين المؤقت المخزن المؤقت:

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 32 (1 row) 

إذا كان المسح التسلسلي محظورًا ، فسيتم قراءة الجدول حسب الفهرس:

 => SET enable_seqscan = off; => EXPLAIN (ANALYZE, COSTS OFF) SELECT count(*) FROM big; 
  QUERY PLAN ------------------------------------------------------------------------------------------- Aggregate (actual time=50.300..50.301 rows=1 loops=1) -> Index Only Scan using big_pkey on big (actual time=0.098..48.547 rows=4097 loops=1) Heap Fetches: 4097 Planning Time: 0.067 ms Execution Time: 50.340 ms (5 rows) 

في هذه الحالة ، لا يتم استخدام حلقة المخزن المؤقت ويظهر الجدول بأكمله في ذاكرة التخزين المؤقت المخزن المؤقت (والفهرس بأكمله تقريبًا ، أيضًا):

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 4097 (1 row) 

بطريقة مماثلة ، يتم استخدام الحلقات العازلة لعملية التنظيف (أيضًا 32 صفحة) ولعمليات الكتابة المجمعة COPY IN و CREATE TABLE AS SELECT (عادةً 2048 صفحة ، لكن لا تزيد عن 1/8 من إجمالي ذاكرة التخزين المؤقت المؤقتة).

الجداول المؤقتة


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

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

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

الاحماء ذاكرة التخزين المؤقت


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

 => CREATE EXTENSION pg_prewarm; 

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

 => ALTER SYSTEM SET shared_preload_libraries = 'pg_prewarm'; 

 student$ sudo pg_ctlcluster 11 main restart 

حقل إعادة التشغيل ، إذا لم يتم تغيير المعلمة pg_prewarm.autoprewarm ، سيتم تلقائيًا بدء عملية الخلفية الرئيسية autoprewarm تلقائيًا ، والتي مرة واحدة في pg_prewarm.autoprewarm_interval ستنفق قائمة الصفحات المخزنة مؤقتًا على القرص (لا تنسَ أن تأخذ في الاعتبار العملية الجديدة عند إعداد max_parallel_processes ).

 => SELECT name, setting, unit FROM pg_settings WHERE name LIKE 'pg_prewarm%'; 
  name | setting | unit ---------------------------------+---------+------ pg_prewarm.autoprewarm | on | pg_prewarm.autoprewarm_interval | 300 | s (2 rows) 

 postgres$ ps -o pid,command --ppid `head -n 1 /var/lib/postgresql/11/main/postmaster.pid` | grep prewarm 
 10436 postgres: 11/main: autoprewarm master 

الآن لا توجد طاولة كبيرة في ذاكرة التخزين المؤقت:

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 0 (1 row) 

إذا افترضنا أن جميع محتوياته مهمة جدًا ، فيمكننا قراءتها في ذاكرة التخزين المؤقت المخزن المؤقت عن طريق استدعاء الوظيفة التالية:

 => SELECT pg_prewarm('big'); 
  pg_prewarm ------------ 4097 (1 row) 

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 4097 (1 row) 

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

 => SELECT autoprewarm_dump_now(); 
  autoprewarm_dump_now ---------------------- 4340 (1 row) 

يزيد عدد الصفحات المهملة عن 4097 - ويشمل ذلك صفحات كائنات كتالوج النظام التي تمت قراءتها بالفعل بواسطة الخادم. وهنا الملف:

 postgres$ ls -l /var/lib/postgresql/11/main/autoprewarm.blocks 
 -rw------- 1 postgres postgres 102078  29 15:51 /var/lib/postgresql/11/main/autoprewarm.blocks 

الآن أعد تشغيل الخادم مرة أخرى.

 student$ sudo pg_ctlcluster 11 main restart 

مباشرة بعد الإطلاق ، تظهر طاولتنا مرة أخرى في ذاكرة التخزين المؤقت.

 => SELECT count(*) FROM pg_buffercache WHERE relfilenode = pg_relation_filenode('big'::regclass); 
  count ------- 4097 (1 row) 

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

أن تستمر .

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


All Articles