لتذكيرك ، بدأنا بمشاكل تتعلق
بالعزلة ، وقمنا باستكشاف حول
بنية البيانات منخفضة المستوى ، وناقشنا
إصدارات الصف بالتفصيل ولاحظنا كيفية الحصول على
لقطات البيانات من إصدارات الصفوف.
ثم استكشفنا
الفراغ في الصفحة (والتحديثات الساخنة) والفراغ . الآن سوف ننظر إلى autov Vacuum.
Autovacuum
لقد سبق أن ذكرنا ذلك عادة (أي عندما لا يوجد شيء يحمل أفق الصفقة لفترة طويلة) تقوم VACUUM عادة بعملها. المشكلة هي كم مرة نسميها.
إذا قمنا بتفريغ طاولة تغيير بشكل نادر جدًا ، فإن حجمها سيزداد أكثر من المطلوب. إلى جانب ذلك ، قد تتطلب العملية الفراغية التالية العديد من التمريرات خلال الفهارس إذا تم إجراء الكثير من التغييرات.
إذا قمنا بتفريغ الطاولة بشكل متكرر ، فسيعمل الخادم باستمرار على الصيانة بدلاً من العمل المفيد - وهذا ليس جيدًا أيضًا.
لاحظ أن تشغيل VACUUM في الموعد المحدد لا يحل المشكلة لأن عبء العمل يمكن أن يتغير مع مرور الوقت. إذا بدأ الجدول في التغيير بشكل مكثف ، فيجب أن يتم تفريغه أكثر من مرة.
Autov Vacuum هو بالضبط التقنية التي تمكننا من إطلاق كنس استنادًا إلى مدى تغير الجدول.
عند تشغيل
ميزة التعبئة التلقائية ( مجموعة معلمات ضبط
الفراغ التلقائي ) ،
تبدأ عملية البرنامج الخفي
لقاذفة autov Vacuum ، والتي تخطط للعمل. يتم كنس نفسه عن طريق عمليات
عامل autov Vacuum ، والتي يمكن تشغيل العديد من الحالات في نفس الوقت.
تتكون عملية
إطلاق autov Vacuum من قائمة قواعد البيانات التي يحدث فيها أي نشاط. يتم تحديد النشاط من الإحصاءات ،
ولجمعه ، يجب تعيين المعلمة
track_counts . لا تقم مطلقًا بإيقاف
تشغيل autov Vacuum و
track_counts ، وإلا فلن تعمل ميزة autov Vacuum.
مرة واحدة كل ثانية
autov Vacuum_naptime ، يبدأ
قاذفة autov Vacuum (باستخدام عملية
مدير مكتب البريد ) عملية
مناولة لكل قاعدة بيانات في القائمة. بمعنى آخر ، إذا كان هناك بعض النشاط في قاعدة البيانات ، فسيتم إرسال العمليات
المنفذة إليها في فترة زمنية من
autov Vacuum_naptime ثانية. تحقيقًا لهذه الغاية ، في حالة توفر بعض قواعد البيانات النشطة (N) ، يتم تشغيل العمليات
المنفذة N مرات مثل كل ثانية
autov Vacuum_naptime . ولكن العدد الإجمالي لعمليات التشغيل المتزامنة محدود في نفس الوقت بواسطة المعلمة
autov Vacuum_max_workers .
عند البدء ، تتصل العملية المنفذة بقاعدة البيانات المعينة لها وتبدأ بتكوين قائمة من:
- جميع الجداول ، وجهات النظر تتحقق والجداول TOAST التي تتطلب فراغ.
- جميع الجداول وطرق العرض الملموسة التي تتطلب التحليل (لا يتم تحليل جداول TOAST حيث يتم الوصول إليها دائمًا باستخدام الوصول إلى الفهرس).
ثم تقوم العملية المنفصلة بإفراغ و / أو تحليل الأشياء الموجودة في القائمة واحدة في وقت واحد ويكتمل عند الانتهاء من الفراغ.
إذا لم تنجح العملية في تنفيذ جميع الأعمال المخطط لها في ثوانٍ
autov Vacuum_naptime ، فإن عملية
إطلاق autov Vacuum ستقوم بإرسال عامل آخر إلى قاعدة البيانات هذه ، وسوف تعمل معًا. "معا" يعني فقط أن العملية الثانية ستنشئ قائمتها الخاصة وستعمل من خلالها. لذلك ، ستتم معالجة الجداول المختلفة فقط بالتوازي ، لكن لا يوجد تواز على مستوى جدول واحد - إذا كانت إحدى العمليات المنفذة تتعامل بالفعل مع جدول ، فستتخطى عملية أخرى وتواصل أكثر.
الآن دعونا نوضح بمزيد من التفصيل ما المقصود ب "يتطلب كنس" و "يتطلب تحليل".
في الآونة الأخيرة ، تم الالتزام بالتصحيح الذي يسمح للفراغ بمعالجة الفهارس بالتوازي مع العاملين في الخلفية.
ما الجداول تتطلب كنس؟
يعتبر الفراغ ضروريًا إذا تجاوز عدد الوفيات الميتة (أي القديمة) العتبة المحددة. يقوم جامع الإحصائيات بتتبع عدد
pg_stat_all_tables
بشكل دائم ، والذي يتم تخزينه في جدول
pg_stat_all_tables
. وتحدد المعلمتان العتبة:
- يحدد autov Vacuum_vacuum_threshold القيمة المطلقة (عدد المجموعات).
- يحدد autov Vacuum_vacuum_scale_factor حصة الصفوف في الجدول.
باختصار: كنس هو مطلوب إذا
pg_stat_all_tables.n_dead_tup
> =
autovacuum_vacuum_threshold +
autov Vacuum_v Vacuum_scale_factor *
pg_class.reltupes
.
باستخدام الإعدادات الافتراضية ،
autov Vacuum_vacuum_threshold = 50 و
autov Vacuum_v Vacuum_scale_factor = 0.2.
autov Vacuum_vacuum_scale_factor هو الأهم هنا ، بالتأكيد - هذه المعلمة هي أمر بالغ الأهمية بالنسبة للجداول الكبيرة (وهي التي ترتبط بها المشكلات المحتملة). تبدو قيمة 20٪ مرتفعة بشكل غير ضروري ، وعلى الأرجح ستحتاج إلى تقليل كبير.
قد تختلف القيم المثلى للمعلمات في جداول مختلفة وتعتمد على أحجام الجدول وخصائص التغييرات. من المنطقي تعيين قيم مناسبة بشكل عام ، وإذا دعت الحاجة إلى ذلك ، فقم بتبديل خاص للمعلمات على مستوى بعض الجداول عن طريق معلمات التخزين:
- autov Vacuum_vacuum_threshold and toast.autov Vacuum_vacuum_threshold .
- autov Vacuum_v Vacuum_scale_factor and toast.autov Vacuum_v Vacuum_scale_factor .
لتجنب حدوث حيرة ، من المنطقي القيام بذلك فقط لعدد قليل من الجداول التي يتم تمييزها بين البقية بحجم التغييرات وشدتها وفقط عندما لا تعمل القيم المحددة عالميًا بشكل جيد.
علاوة على ذلك ، يمكنك إيقاف تشغيل ميزة التعبئة التلقائية على مستوى الطاولة (على الرغم من أننا بالكاد نفكر في سبب الحاجة إلى ذلك):
- autov Vacuum_enabled و toast.autovacuum_enabled .
على سبيل المثال ، في المرة الأخيرة التي أنشأنا فيها جدول
vac
مع إيقاف تشغيل autov Vacuum من أجل التحكم في التنظيف يدويًا لأغراض العرض التوضيحي. يمكن تغيير معلمة التخزين كما يلي:
=> ALTER TABLE vac SET (autovacuum_enabled = off);
لإضفاء الطابع الرسمي على كل ما سبق ، فلننشئ طريقة عرض تُظهر الجداول التي تحتاج إلى تفريغها في الوقت الحالي. ستستخدم الدالة التي تُرجع القيمة الحالية للمعلمة وتأخذ في الاعتبار أنه يمكن إعادة تعريف القيمة على مستوى الجدول:
=> CREATE FUNCTION get_value(param text, reloptions text[], relkind "char") RETURNS float AS $$ SELECT coalesce( -- if the storage parameter is set, we take its value (SELECT option_value FROM pg_options_to_table(reloptions) WHERE option_name = CASE -- for TOAST tables, the parameter name differs WHEN relkind = 't' THEN 'toast.' ELSE '' END || param ), -- otherwise, we take the value of the configuration parameter current_setting(param) )::float; $$ LANGUAGE sql;
وهذا هو الرأي:
=> CREATE VIEW need_vacuum AS SELECT st.schemaname || '.' || st.relname tablename, st.n_dead_tup dead_tup, get_value('autovacuum_vacuum_threshold', c.reloptions, c.relkind) + get_value('autovacuum_vacuum_scale_factor', c.reloptions, c.relkind) * c.reltuples max_dead_tup, st.last_autovacuum FROM pg_stat_all_tables st, pg_class c WHERE c.oid = st.relid AND c.relkind IN ('r','m','t');
ما الجداول تتطلب التحليل؟
الموقف مع التحليل التلقائي مشابه. تعتبر هذه الجداول تتطلب تحليلًا يتجاوز عدد
pg_stat_all_tables.n_mod_since_analyze
المُحدّثة (منذ آخر تحليل) العتبة المحددة بواسطة معلمتين
pg_stat_all_tables.n_mod_since_analyze
:
pg_stat_all_tables.n_mod_since_analyze
> =
autovacuum_analyze_threshold +
autovacuum_analyze_scale_factor *
pg_class.reltupes
.
تختلف الإعدادات الافتراضية للتحليل التلقائي إلى حد ما:
autov Vacuum_analyze_threshold = 50 و
autov Vacuum_analyze_scale_factor = 0.1. يمكن تعريفها أيضًا على مستوى معلمات التخزين بجداول منفصلة:
- autovacuum_analyze_threshold
- autovacuum_analyze_scale_factor
نظرًا لأن جداول TOAST لا يتم تحليلها ، فهي لا تحتوي على مثل هذه المعلمات.
لنقم أيضًا بإنشاء طريقة عرض للتحليل:
=> CREATE VIEW need_analyze AS SELECT st.schemaname || '.' || st.relname tablename, st.n_mod_since_analyze mod_tup, get_value('autovacuum_analyze_threshold', c.reloptions, c.relkind) + get_value('autovacuum_analyze_scale_factor', c.reloptions, c.relkind) * c.reltuples max_mod_tup, st.last_autoanalyze FROM pg_stat_all_tables st, pg_class c WHERE c.oid = st.relid AND c.relkind IN ('r','m');
مثال
لنقم بتعيين قيم المعلمات التالية للتجارب:
=> ALTER SYSTEM SET autovacuum_naptime = '1s';
=> SELECT pg_reload_conf();
pg_reload_conf ---------------- t (1 row)
الآن ، دعونا ننشئ جدولًا مشابهًا للجدول المستخدم في المرة الأخيرة وأدخل ألف صف فيه. يتم إيقاف تشغيل Autov Vacuum على مستوى الطاولة ، وسوف نقوم بتشغيله بأنفسنا. بدون هذا ، لن تكون الأمثلة قابلة للتكرار حيث يمكن تشغيل فراغ التعبئة التلقائية في وقت سيء.
=> CREATE TABLE autovac( id serial, s char(100) ) WITH (autovacuum_enabled = off); => INSERT INTO autovac SELECT g.id,'A' FROM generate_series(1,1000) g(id);
هذا هو ما سوف تظهره وجهة نظرنا الخاصة بالمكنسة الكهربائية:
=> SELECT * FROM need_vacuum WHERE tablename = 'public.autovac';
tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+----------------- public.autovac | 0 | 0 | (1 row)
ينبغي إيلاء الاهتمام هنا لأمرين. أولاً ،
max_dead_tup
= 0 على الرغم من أن 3٪ من 1000 صف تقدم 30 صفًا. الشيء هو أنه ليس لدينا إحصائيات مطروحة على الطاولة حتى الآن لأن INSERT لا تقوم بتحديثها من تلقاء نفسها. إلى أن يتم تحليل الجدول ، ستبقى الأصفار منذ
pg_class.reltuples
= 0. لكن دعونا ننظر إلى العرض الثاني للتحليل:
=> SELECT * FROM need_analyze WHERE tablename = 'public.autovac';
tablename | mod_tup | max_mod_tup | last_autoanalyze ----------------+---------+-------------+------------------ public.autovac | 1000 | 0 | (1 row)
نظرًا لأنه تم تغيير 1000 صف (مضافة) في الجدول ، وهو أكبر من الصفر ، يجب تشغيل التحليل التلقائي. دعونا التحقق من هذا:
=> ALTER TABLE autovac SET (autovacuum_enabled = on);
بعد توقف قصير ، يمكننا أن نرى أنه قد تم تحليل الجدول وأن 20 صفًا صحيحًا تظهر في
max_dead_tup
بدلاً من الأصفار:
=> SELECT * FROM need_analyze WHERE tablename = 'public.autovac';
tablename | mod_tup | max_mod_tup | last_autoanalyze ----------------+---------+-------------+------------------------------- public.autovac | 0 | 20 | 2019-05-21 11:59:48.465987+03 (1 row)
=> SELECT reltuples, relpages FROM pg_class WHERE relname = 'autovac';
reltuples | relpages -----------+---------- 1000 | 17 (1 row)
دعنا نعود إلى autov Vacuuming:
=> SELECT * FROM need_vacuum WHERE tablename = 'public.autovac';
tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+----------------- public.autovac | 0 | 30 | (1 row)
كما نرى ، تم إصلاح
max_dead_tup
بالفعل. شيء آخر يجب الانتباه إليه هو أن
dead_tup
= 0. تشير الإحصاءات إلى أن الجدول لا يحتوي على tuples ميتة ... وهذا صحيح. لا يوجد شيء للفراغ في الجدول بعد. لن يتم تفريغ أي جدول يتم استخدامه حصريًا في وضع الإلحاق فقط ، وبالتالي لن يتم تحديث خريطة الرؤية له. ولكن هذا يجعل استخدام الفهرس فقط مستحيلاً.
(في المرة القادمة سنرى أن الفراغ سيصل عاجلاً أم آجلاً إلى جدول إلحاقي فقط ، لكن هذا نادر الحدوث.)
درس تم تعلمه: إذا كان الفحص باستخدام الفهرسة ضروريًا ، فقد يلزم الاتصال يدويًا بعملية التفريغ.
الآن ، دعونا نطفئ التعبئة التلقائية مرة أخرى ونحدّث 31 سطرًا ، وهو ما يمثل عتبة سطر واحد أكبر.
=> ALTER TABLE autovac SET (autovacuum_enabled = off); => UPDATE autovac SET s = 'B' WHERE id <= 31; => SELECT * FROM need_vacuum WHERE tablename = 'public.autovac';
tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+----------------- public.autovac | 31 | 30 | (1 row)
الآن يتم استيفاء حالة الفراغ الزناد. لنقم بتشغيل autov Vacuum وبعد فترة توقف قصيرة سنرى أن الجدول قد تمت معالجته:
=> ALTER TABLE autovac SET (autovacuum_enabled = on); => SELECT * FROM need_vacuum WHERE tablename = 'public.autovac';
tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+------------------------------- public.autovac | 0 | 30 | 2019-05-21 11:59:52.554571+03 (1 row)
تحميل الاختناق
لا تمنع VACUUM العمليات الأخرى لأنها تعمل صفحة تلو الأخرى ، ولكنها تنتج حمولة إضافية على النظام ويمكن أن تؤثر بشكل كبير على الأداء.
اختناق للفراغ
لتكون قادرًا على التحكم في شدة الفراغ ، وبالتالي تأثيره على النظام ، تعمل هذه العملية على تغيير العمل والانتظار. ستعمل العملية على
استخدام وحدات العمل التقليدية
vacuum_cost_limit ومن ثم سوف تنام بالنسبة إلى
ms_ vacuum_cost_delay .
الإعدادات الافتراضية هي
vacuum_cost_limit = 200 و
vacuum_cost_delay = 0. الصفر الأخير يعني في الواقع أن VACUUM لا تنام ، لذلك لا يهم قيمة محددة لـ
vacuum_cost_limit على الإطلاق. السبب وراء ذلك هو أنه إذا اضطر المسؤول إلى تشغيل VACUUM يدويًا ، فمن المرجح أن يتم الفراغ في أسرع وقت ممكن.
ومع ذلك ، إذا حددنا وقت النوم ، فإن مقدار العمل المحدد في
vacuum_cost_limit سيتكون من تكاليف العمل مع الصفحات في ذاكرة التخزين المؤقت المخزن المؤقت. يتم تقدير كل وصول للصفحة كالتالي:
- إذا تم العثور على الصفحة في ذاكرة التخزين المؤقت المخزن المؤقت ، vacuum_cost_page_hit = 1.
- إذا لم يتم العثور عليه ، vacuum_cost_page_miss = 10.
- إذا لم يتم العثور عليها وكان لابد من إخراج صفحة متسخة من ذاكرة التخزين المؤقت المؤقتة ، vacuum_cost_page_dirty = 20.
وهذا هو ، مع الإعدادات الافتراضية لـ
vacuum_cost_limit ، يمكن
معالجة 200 صفحة من ذاكرة التخزين المؤقت أو 20 صفحة من صفحات القرص أو 10 صفحات مع الإخلاء دفعة واحدة. من الواضح أن هذه الأشكال مبدئية إلى حد ما ، لكن ليس من المنطقي تحديد أرقام أكثر دقة.
اختناق ل autov Vacuuming
بالنسبة للعمليات الفراغية ، يعمل اختناق الأحمال بنفس طريقة عمل نظام VACUUM. ولكن بالنسبة لعمليات autov Vacuum وإطلاق VACUUM الذي تم تشغيله يدويًا للعمل بكثافة مختلفة ، فإن autov Vacuum له معاييره الخاصة:
autov Vacuum_vacuum_cost_limit و
autov Vacuum_vacuum_cost_delay . إذا كانت هذه المعلمات لها قيمة -1 ،
فسيتم استخدام قيمة
vacuum_cost_limit و / أو
vacuum_cost_delay .
بشكل افتراضي
autov Vacuum_vacuum_cost_limit = -1 (
أي ، يتم استخدام قيمة
vacuum_cost_limit = 200) و
autov Vacuum_vacuum_cost_delay = 20 مللي ثانية. على الأجهزة الحديثة ، فإن autov Vacuum يكون بطيئًا بالفعل.
في الإصدار 12 ، يتم تقليل قيمة
autov Vacuum_vacuum_cost_delay إلى 2 مللي ثانية ، والتي يمكن اتخاذها لتقريب أول أكثر ملاءمة.
إضافة إلى ذلك ، يجب أن نلاحظ أن الحد المحدد بواسطة هذه الإعدادات شائع لجميع العمليات المنفذة. بمعنى آخر ، عند تغيير عدد عمليات العامل المتزامنة ، يظل الحمل الكلي دون تغيير. لذلك ، لزيادة أداء autov Vacuum ، عند إضافة عمليات العامل ، فمن المنطقي أيضًا زيادة
autov Vacuum_v Vacuum_cost_limit .
استخدام الذاكرة والمراقبة
لاحظنا في
المرة الأخيرة كيف استخدم VACUUM ذاكرة الوصول العشوائي ذات الحجم
للصيانة maintenance_work_mem لتخزين المواد التي سيتم تفريغها.
Autov Vacuum يفعل الشيء نفسه تماما. ولكن يمكن أن يكون هناك العديد من عمليات العامل المتزامنة إذا تم ضبط
autov Vacuum_max_workers على قيمة كبيرة. علاوة على ذلك ، يتم تخصيص كل الذاكرة دفعة واحدة بدلاً من الحاجة. لذلك ، بالنسبة للعملية المنفذة ، يمكن تعيين قيودها الخاصة بواسطة المعلمة
autov Vacuum_work_mem . القيمة الافتراضية لهذه المعلمة هي -1 ، أي أنها غير مستخدمة.
كما سبق ذكره ، يمكن أن تعمل VACUUM أيضًا مع الحد الأدنى لحجم الذاكرة. ولكن إذا تم إنشاء فهارس على الجدول ، يمكن أن تنطوي القيمة الصغيرة من
maintenance_work_mem على عمليات مسح فهرس متكررة. وينطبق الشيء نفسه على autov Vacuum. من الناحية المثالية ، يجب أن يكون ل
autov Vacuum_work_mem قيمة الحد الأدنى بحيث لا تحدث عمليات مسح متكررة.
لقد رأينا ذلك لمراقبة VACUUM ، يمكن استخدام خيار VERBOSE (والذي لا يمكن تحديده للفراغ التلقائي) أو عرض
pg_stat_progress_vacuum
(والذي ، مع ذلك ، يعرض المعلومات الحالية فقط). لذلك ، تتمثل الوسيلة الرئيسية لمراقبة التعبئة التلقائية في استخدام المعلمة
log_autov Vacuum_min_duration ، والتي تقوم بإخراج المعلومات إلى سجل رسائل الخادم. يتم إيقاف تشغيله بشكل افتراضي (تعيين إلى -1). من المعقول تشغيل هذه المعلمة (بقيمة 0 ، سيتم إخراج المعلومات عن جميع عمليات autov Vacuum) ومشاهدة الأرقام.
هذا هو ما تبدو عليه معلومات الإخراج:
=> ALTER SYSTEM SET log_autovacuum_min_duration = 0; => SELECT pg_reload_conf();
pg_reload_conf ---------------- t (1 row)
=> UPDATE autovac SET s = 'C' WHERE id <= 31;
student$ tail -n 7 /var/log/postgresql/postgresql-11-main.log
2019-05-21 11:59:55.675 MSK [9737] LOG: automatic vacuum of table "test.public.autovac": index scans: 0 pages: 0 removed, 18 remain, 0 skipped due to pins, 0 skipped frozen tuples: 31 removed, 1000 remain, 0 are dead but not yet removable, oldest xmin: 4040 buffer usage: 78 hits, 0 misses, 0 dirtied avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s 2019-05-21 11:59:55.676 MSK [9737] LOG: automatic analyze of table "test.public.autovac" system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
جميع المعلومات اللازمة متاحة هنا.
لتذكيرك ، غالبًا ما يكون من المنطقي خفض عتبة التسبب في الفراغ من أجل معالجة بيانات أقل في وقت واحد بدلاً من زيادة حجم الذاكرة.
قد يكون من المعقول أيضًا استخدام المشاهدات أعلاه لمراقبة طول قائمة الجداول التي تتطلب التنظيف بالمكنسة الكهربائية. ستشير زيادة طول القائمة إلى أن عمليات التعبئة التلقائية تفتقر إلى الوقت الكافي للقيام بعملها وتحتاج الإعدادات إلى التغيير.
أن تستمر.