لقد بدأنا بمشكلات متعلقة
بالعزلة ، وقمنا باستنباط حول
تنظيم البيانات على مستوى منخفض ، ثم تحدثنا بالتفصيل
عن إصدارات الصف وكيف يتم الحصول على
لقطات من الإصدارات.
آخر مرة تحدثنا فيها عن تحديثات HOT والتنظيف في الصفحة ، واليوم سنلقي نظرة على التنظيف العادي المعروف ،
المبتذلة . نعم ، لقد كُتب الكثير عنها بالفعل ، ومن غير المحتمل أن أقول شيئًا جديدًا ، لكن اكتمال الصورة يتطلب التضحية. كن صبورا.
تنظيف عادي (فراغ)
ماذا التنظيف تفعل
التنظيف داخل الصفحة سريع ، لكنه لا يحرر سوى جزء بسيط من المساحة. إنه يعمل داخل نفس الصفحة الجدولية ولا يؤثر على الفهارس.
تتم عملية التنظيف الرئيسية "العادية" بواسطة أمر VACUUM وسنسميها ببساطة التنظيف (وسوف نتحدث عن التنظيف التلقائي بشكل منفصل).
لذلك ، تنظيف يعالج الجدول بالكامل. ينظف ليس فقط الإصدارات غير الضرورية من السلاسل ، ولكن أيضًا يشير إليها من جميع المؤشرات.
تحدث المعالجة بالتوازي مع الأنشطة الأخرى في النظام. في هذه الحالة ، يمكن استخدام الجدول والفهارس بالطريقة المعتادة للقراءة والتغيير (ومع ذلك ، فإن التنفيذ المتزامن لأوامر مثل CREATE INDEX و ALTER TABLE وبعضها الآخر سيكون مستحيلاً).
يتم عرض فقط الصفحات التي حدث فيها نشاط ما في الجدول. لهذا الغرض ، يتم استخدام خريطة رؤية (أذكرك بأن الصفحات التي تحتوي على إصدارات قديمة فقط من الصفوف مضمونة أن تكون مرئية في جميع لقطات البيانات تم وضع علامة عليها). تتم معالجة الصفحات التي لم يتم تمييزها على الخريطة فقط ، بينما يتم تحديث الخريطة نفسها.
في هذه العملية ، يتم تحديث خريطة المساحة الحرة لتعكس المساحة الحرة التي تظهر في الصفحات.
كالعادة ، قم بإنشاء جدول:
=> CREATE TABLE vac( id serial, s char(100) ) WITH (autovacuum_enabled = off); => CREATE INDEX vac_s ON vac(s); => INSERT INTO vac(s) VALUES ('A'); => UPDATE vac SET s = 'B'; => UPDATE vac SET s = 'C';
باستخدام المعلمة
autov Vacuum_enabled ، نطفئ التنظيف التلقائي. سنتحدث عن ذلك في المرة القادمة ، ولكن في الوقت الحالي - من أجل التجارب - من المهم بالنسبة لنا إدارة التنظيف يدويًا.
يوجد الآن ثلاثة إصدارات من الصف في الجدول ، ويتم ربط كل إصدار من فهرس:
=> SELECT * FROM heap_page('vac',0);
ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+----------+-----+-----+-------- (0,1) | normal | 4000 (c) | 4001 (c) | | | (0,2) (0,2) | normal | 4001 (c) | 4002 | | | (0,3) (0,3) | normal | 4002 | 0 (a) | | | (0,3) (3 rows)
=> SELECT * FROM index_page('vac_s',1);
itemoffset | ctid ------------+------- 1 | (0,1) 2 | (0,2) 3 | (0,3) (3 rows)
بعد التنظيف ، تختفي الإصدارات "الميتة" ولا يوجد سوى نسخة واحدة ذات صلة. وللمؤشر أيضًا رابط واحد متبقي:
=> VACUUM vac; => SELECT * FROM heap_page('vac',0);
ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+-------+-----+-----+-------- (0,1) | unused | | | | | (0,2) | unused | | | | | (0,3) | normal | 4002 (c) | 0 (a) | | | (0,3) (3 rows)
=> SELECT * FROM index_page('vac_s',1);
itemoffset | ctid ------------+------- 1 | (0,3) (1 row)
يرجى ملاحظة أن أول مؤشرين قد تلقيا حالة غير مستخدمة ، ولم يمت ، كما هو الحال مع التنظيف داخل الصفحة.
ومرة أخرى عن أفق الصفقة
كيف يحدد PostgreSQL إصدارات الصف التي يمكن اعتبارها "ميتة"؟ لقد درسنا بالفعل مفهوم أفق المعاملات عندما تحدثنا
عن لقطات
البيانات ، ولكن هذا موضوع مهم لدرجة أنه ليس خطيئة لتكرارها.
لنبدأ التجربة السابقة مرة أخرى.
=> TRUNCATE vac; => INSERT INTO vac(s) VALUES ('A'); => UPDATE vac SET s = 'B';
ولكن قبل تحديث السطر مرة أخرى ، دع معاملة أخرى تبدأ (ولكن لا تنتهي). في مثالنا ، ستعمل على مستوى "قراءة ملتزم" ، لكن يجب أن تحصل على رقم معاملة حقيقي (غير افتراضي). على سبيل المثال ، يمكن أن تتغير أو حتى تقفل بعض الصفوف في أي جدول ، وليس بالضرورة في vac:
| => BEGIN; | => SELECT s FROM t FOR UPDATE;
| s | ----- | FOO | BAR | (2 rows)
=> UPDATE vac SET s = 'C';
هناك ثلاثة صفوف في الجدول وثلاثة ارتباطات في الفهرس. ماذا يحدث بعد التنظيف؟
=> VACUUM vac; => SELECT * FROM heap_page('vac',0);
ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+----------+-----+-----+-------- (0,1) | unused | | | | | (0,2) | normal | 4005 (c) | 4007 (c) | | | (0,3) (0,3) | normal | 4007 (c) | 0 (a) | | | (0,3) (3 rows)
=> SELECT * FROM index_page('vac_s',1);
itemoffset | ctid ------------+------- 1 | (0,2) 2 | (0,3) (2 rows)
هناك إصداران من الصف المتبقي في الجدول: قرر التنظيف عدم إمكانية حذف الإصدار (0.2) حتى الآن. السبب ، بالطبع ، هو في أفق معاملة قاعدة البيانات ، والتي يتم تحديدها في مثالنا بمعاملة غير كاملة:
| => SELECT backend_xmin FROM pg_stat_activity WHERE pid = pg_backend_pid();
| backend_xmin | -------------- | 4006 | (1 row)
يمكنك طلب التنظيف للتحدث عما يحدث:
=> VACUUM VERBOSE vac;
INFO: vacuuming "public.vac" INFO: index "vac_s" now contains 2 row versions in 2 pages DETAIL: 0 index row versions were removed. 0 index pages have been deleted, 0 are currently reusable. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. INFO: "vac": found 0 removable, 2 nonremovable row versions in 1 out of 1 pages DETAIL: 1 dead row versions cannot be removed yet, oldest xmin: 4006 There were 1 unused item pointers. Skipped 0 pages due to buffer pins, 0 frozen pages. 0 pages are entirely empty. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM
يرجى ملاحظة:
- إصداران غير قابلين للإزالة - تم العثور على نسختين في الجدول لا يمكن حذفهما ،
- لا يمكن إزالة إصدارات الصفوف النهائية بعد - بما في ذلك إصدار "ميت" ،
- أقدم xmin يظهر الأفق الحالي.
نكرر الاستنتاج مرة أخرى: إن وجود معاملات طويلة الأمد في قاعدة البيانات (غير مكتملة أو طويلة بالفعل) يمكن أن يؤدي إلى توسيع جداول (bloat) ، بغض النظر عن عدد مرات إجراء التنظيف. لذلك ، في PostgreSQL ، يتم دمج أحمال عمل OLTP و OLAP بشكل سيئ في قاعدة بيانات واحدة: لن تسمح التقارير التي يتم تشغيلها لساعات بمسح الجداول التي يتم تحديثها بشكل متكرر في الوقت المحدد. قد يكون الحل المحتمل هو إنشاء نسخة متماثلة منفصلة "لإعداد التقارير".
بعد الانتهاء من الصفقة المفتوحة ، يتحول الأفق وتصحيح الموقف:
| => COMMIT;
=> VACUUM VERBOSE vac;
INFO: vacuuming "public.vac" INFO: scanned index "vac_s" to remove 1 row versions DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s INFO: "vac": removed 1 row versions in 1 pages DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s INFO: index "vac_s" now contains 1 row versions in 2 pages DETAIL: 1 index row versions were removed. 0 index pages have been deleted, 0 are currently reusable. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. INFO: "vac": found 1 removable, 1 nonremovable row versions in 1 out of 1 pages DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 4008 There were 1 unused item pointers. Skipped 0 pages due to buffer pins, 0 frozen pages. 0 pages are entirely empty. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM
تحتوي الصفحة الآن على أحدث إصدار حالي من السطر:
=> SELECT * FROM heap_page('vac',0);
ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+-------+-----+-----+-------- (0,1) | unused | | | | | (0,2) | unused | | | | | (0,3) | normal | 4007 (c) | 0 (a) | | | (0,3) (3 rows)
يوجد أيضًا إدخال واحد فقط في الفهرس:
=> SELECT * FROM index_page('vac_s',1);
itemoffset | ctid ------------+------- 1 | (0,3) (1 row)
ما يجري في الداخل
يجب أن يعالج التنظيف كلا من الجدول والفهارس في نفس الوقت ، ويفعل ذلك بطريقة لا تمنع تشغيل العمليات الأخرى. كيف تفعل ذلك؟
كل شيء يبدأ
بمسح جدول (مع مراعاة خريطة الرؤية ، كما ذكر بالفعل). في صفحات القراءة ، يتم تحديد الإصدارات غير الضرورية من السلاسل ويتم كتابة معرفاتها (tid) إلى صفيف خاص. تقع المجموعة في الذاكرة المحلية لعملية التنظيف ؛ يتم تخصيص جزء من حجم
maintenance_work_mem له. القيمة الافتراضية لهذه المعلمة هي 64 ميغابايت. لاحظ أنه يتم تخصيص هذه الذاكرة على الفور بالكامل ، وليس حسب الحاجة. صحيح ، إذا كان الجدول صغيرًا ، فسيتم تخصيص الجزء الأقل.
بعد ذلك ، أحد شيئين: إما سنصل إلى نهاية الجدول ، أو ستنتهي الذاكرة المخصصة للصفيف. في أي من الحالتين ، تبدأ
مرحلة تنظيف الفهرس . للقيام بذلك ، يتم
فحص كل من الفهارس التي تم إنشاؤها على الجدول بشكل
كامل للبحث عن السجلات التي تشير إلى الإصدارات المخزنة من الصفوف. يتم مسح السجلات التي تم العثور عليها من صفحات الفهرس.
في هذه المرحلة ، نحصل على الصورة التالية: في الفهارس لم تعد هناك روابط لإصدارات غير ضرورية من الصفوف ، لكنها لا تزال موجودة في الجدول. هذا لا يتعارض مع أي شيء: عند تنفيذ استعلام ، إما أننا لا نصل إلى إصدارات ميتة من الصفوف على الإطلاق (مع إمكانية الوصول إلى الفهرس) ، أو وضع علامة عليها عند التحقق من الرؤية (عند مسح جدول).
بعد ذلك ، تبدأ
مرحلة تنظيف الطاولة . يتم فحص الجدول مرة أخرى لقراءة الصفحات الضرورية ، لمسح الإصدارات المخزنة من الخطوط منها ، ولتحرير المؤشرات. يمكننا القيام بذلك لأنه لا توجد روابط من الفهارس بعد الآن.
إذا لم يتم قراءة الجدول بالكامل في التمريرة الأولى ، فسيتم مسح المصفوفة ويتكرر كل شيء من المكان الذي توقفنا فيه.
بهذه الطريقة:
- يتم دائما مسح الجدول مرتين.
- إذا تم حذف العديد من إصدارات الصفوف أثناء التنظيف بحيث لا يمكن دمجها جميعًا في ذاكرة maintenance_work_mem ، فسيتم مسح جميع الفهارس بالكامل عدة مرات حسب الحاجة.
على الجداول الكبيرة ، يمكن أن يستغرق هذا وقتًا كبيرًا وإنشاء حمل كبير على النظام. بالطبع ، لن يتم حظر الطلبات ، لكن الإدخال / الإخراج "الإضافي" غير سار أيضًا.
لتسريع العملية ، من المنطقي إما الدعوة إلى التنظيف أكثر من مرة (بحيث لا يتم مسح عدد كبير جدًا من إصدارات الصف في كل مرة) ، أو تخصيص المزيد من الذاكرة.
ألاحظ بين قوسين أنه ابتداءً من الإصدار 11 ،
يمكن لـ PostgreSQL
تخطي عمليات مسح الفهرسة إذا لم يكن ذلك ضروريًا تمامًا. هذا يجب أن يجعل الحياة أسهل لأصحاب الجداول الكبيرة التي يتم فيها إضافة صفوف فقط (ولكن لا يتم تغييرها).
مراقبة
كيف نفهم أن التنظيف لا يتماشى مع العمل مرة واحدة؟
لقد رأينا بالفعل الطريقة الأولى: يمكنك استدعاء أمر VACUUM باستخدام VERBOSE. ثم سيتم عرض المعلومات على مراحل العمل على وحدة التحكم.
ثانياً ، بدءًا من الإصدار 9.6 ، هناك طريقة عرض pg_stat_progress_v Vacuum ، والتي تحتوي أيضًا على جميع المعلومات اللازمة.
(هناك طريقة ثالثة - لعرض المعلومات في سجل الرسائل ، ولكن هذا يعمل فقط للتنظيف التلقائي ، والتي ستتم مناقشتها في المرة القادمة.)
سنقوم بإدراج المزيد من الصفوف في الجدول حتى يستغرق التنظيف وقتًا ملحوظًا ، وسوف نقوم بتحديثها كلها حتى يكون هناك شيء يتعلق بالتنظيف.
=> TRUNCATE vac; => INSERT INTO vac(s) SELECT 'A' FROM generate_series(1,500000); => UPDATE vac SET s = 'B';
تقليل حجم الذاكرة المخصصة لصفيف المعرف:
=> ALTER SYSTEM SET maintenance_work_mem = '1MB'; => SELECT pg_reload_conf();
نبدأ التنظيف ، وبينما يعمل ، سننتقل إلى طريقة العرض pg_stat_progress_v Vacuum عدة مرات:
=> VACUUM VERBOSE vac;
| => SELECT * FROM pg_stat_progress_vacuum \gx
| -[ RECORD 1 ]------+------------------ | pid | 6715 | datid | 41493 | datname | test | relid | 57383 | phase | vacuuming indexes | heap_blks_total | 16667 | heap_blks_scanned | 2908 | heap_blks_vacuumed | 0 | index_vacuum_count | 0 | max_dead_tuples | 174762 | num_dead_tuples | 174480
| => SELECT * FROM pg_stat_progress_vacuum \gx
| -[ RECORD 1 ]------+------------------ | pid | 6715 | datid | 41493 | datname | test | relid | 57383 | phase | vacuuming indexes | heap_blks_total | 16667 | heap_blks_scanned | 5816 | heap_blks_vacuumed | 2907 | index_vacuum_count | 1 | max_dead_tuples | 174762 | num_dead_tuples | 174480
هنا نرى على وجه الخصوص:
- اسم المرحلة الحالية (المرحلة) - تحدثنا عن ثلاث مراحل رئيسية ، ولكن بشكل عام هناك المزيد منها ؛
- إجمالي عدد صفحات الجدول (heap_blks_total) ؛
- عدد الصفحات التي تم الزحف إليها (heap_blks_scanned) ؛
- عدد الصفحات التي تم محوها بالفعل (heap_blks_v Vacuumed) ؛
- عدد مرات المرور حسب الفهرس (index_vacuum_count).
يتم تحديد التقدم الكلي حسب نسبة heap_blks_v Vacuumed إلى heap_blks_total ، لكن ضع في اعتبارك أن هذه القيمة لا تتغير بسلاسة ، ولكن "jerkily" بسبب عمليات مسح الفهرسة. ومع ذلك ، يجب إيلاء الاهتمام الرئيسي لعدد دورات التنظيف - قيمة أكبر من 1 تعني أن الذاكرة المخصصة لم تكن كافية لإكمال التنظيف في مسار واحد.
يُظهر إخراج الأمر VACUUM VERBOSE ، الذي تم إكماله بحلول هذا الوقت ، الصورة الكبيرة:
INFO: vacuuming "public.vac"
INFO: scanned index "vac_s" to remove 174480 row versions DETAIL: CPU: user: 0.50 s, system: 0.07 s, elapsed: 1.36 s INFO: "vac": removed 174480 row versions in 2908 pages DETAIL: CPU: user: 0.02 s, system: 0.02 s, elapsed: 0.13 s
INFO: scanned index "vac_s" to remove 174480 row versions DETAIL: CPU: user: 0.26 s, system: 0.07 s, elapsed: 0.81 s INFO: "vac": removed 174480 row versions in 2908 pages DETAIL: CPU: user: 0.01 s, system: 0.02 s, elapsed: 0.10 s
INFO: scanned index "vac_s" to remove 151040 row versions DETAIL: CPU: user: 0.13 s, system: 0.04 s, elapsed: 0.47 s INFO: "vac": removed 151040 row versions in 2518 pages DETAIL: CPU: user: 0.01 s, system: 0.02 s, elapsed: 0.08 s
INFO: index "vac_s" now contains 500000 row versions in 17821 pages DETAIL: 500000 index row versions were removed. 8778 index pages have been deleted, 0 are currently reusable. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. INFO: "vac": found 500000 removable, 500000 nonremovable row versions in 16667 out of 16667 pages DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 4011 There were 0 unused item pointers. 0 pages are entirely empty. CPU: user: 1.10 s, system: 0.37 s, elapsed: 3.71 s. VACUUM
هنا يمكنك أن ترى أنه في المجمل كانت هناك ثلاث تمريرات من خلال المؤشرات ، كل منها قام بمسح 174،480 مؤشرًا إلى الإصدارات الميتة من السلاسل. من أين يأتي هذا الرقم؟ يستغرق ارتباط واحد (tid) 6 بايت ، و 1024 * 1024/6 = 174762 هو الرقم الذي نراه في pg_stat_progress_vacuum.max_dead_tuples. في الواقع ، يمكن استخدامه أقل من ذلك بقليل: من المضمون أنه عند قراءة الصفحة التالية ، فإن جميع المؤشرات إلى الإصدارات "الميتة" سوف تتلاءم تمامًا في الذاكرة.
تحليل
التحليل ، أو بمعنى آخر ، جمع المعلومات الإحصائية لمخطط الاستعلام ، غير مرتبط رسميًا بعملية التنظيف. ومع ذلك ، يمكننا إجراء التحليل ليس فقط مع فريق ANALYZE ، ولكن أيضًا دمج التنظيف مع التحليل: VACUUM ANALYZE. في هذه الحالة ، يتم إجراء التنظيف أولاً ، ثم التحليل - لا تحدث وفورات.
ولكن ، كما سنرى لاحقًا ، يتم إجراء التنظيف التلقائي والتحليل التلقائي في عملية واحدة وتتم إدارتها بطريقة مماثلة.
التنظيف الكامل (فراغ كامل)
كما رأينا ، يحرر التنظيف التقليدي مساحة أكبر من التنظيف داخل الصفحة ، ولكن حتى لا يحل المشكلة تمامًا دائمًا.
إذا زاد حجم الجدول أو الفهرس ، لسبب ما ، بشكل كبير ، فسوف يؤدي التنظيف المنتظم إلى توفير مساحة داخل الصفحات الحالية: سوف تحتوي على "فتحات" ، والتي سيتم استخدامها بعد ذلك لإدراج إصدارات جديدة من الصفوف. لكن عدد الصفحات لن يتغير ، وبالتالي ، من وجهة نظر نظام التشغيل ، ستشغل الملفات نفس المساحة التي شغلتها بالضبط قبل التنظيف. وهذا سيء بسبب:
- الفحص الكامل لجدول (أو فهرس) يبطئ ؛
- قد تكون هناك حاجة إلى ذاكرة تخزين مؤقت أكبر (لأنه يتم تخزين الصفحات ، وانخفاض كثافة المعلومات المفيدة) ؛
- قد يظهر مستوى "إضافي" في شجرة الفهرس ، مما يؤدي إلى إبطاء الوصول إلى الفهرس ؛
- تشغل الملفات مساحة إضافية على القرص والنسخ الاحتياطي.
(الاستثناء الوحيد هو الصفحات التي تم تنظيفها بالكامل في نهاية الملف - مثل هذه الصفحات "تعض" الملف وتعود إلى نظام التشغيل.)
إذا كانت مشاركة المعلومات المفيدة في الملفات أقل من الحد المعقول ، فيمكن للمسؤول إجراء تنظيف جدول كامل. في الوقت نفسه ، يتم إعادة بناء الجدول وجميع مؤشراته بالكامل من نقطة الصفر ، ويتم تعبئة البيانات بأكبر قدر ممكن من الضغط (بالطبع ، مع الأخذ في الاعتبار المعلمة fillfactor). عند إعادة البناء ، يقوم PostgreSQL بإعادة إنشاء الجدول أولاً ، ثم كل من فهارسه. يتم إنشاء ملفات جديدة لكل كائن ، وفي نهاية عملية إعادة الإنشاء ، يتم حذف الملفات القديمة. يرجى ملاحظة أنه في عملية العمل على القرص سيتطلب مساحة إضافية.
للتوضيح ، أدخل عددًا من الصفوف في الجدول مرة أخرى:
=> TRUNCATE vac; => INSERT INTO vac(s) SELECT 'A' FROM generate_series(1,500000);
كيفية تقييم كثافة المعلومات؟ للقيام بذلك ، من المريح استخدام الملحق الخاص:
=> CREATE EXTENSION pgstattuple; => SELECT * FROM pgstattuple('vac') \gx
-[ RECORD 1 ]------+--------- table_len | 68272128 tuple_count | 500000 tuple_len | 64500000 tuple_percent | 94.47 dead_tuple_count | 0 dead_tuple_len | 0 dead_tuple_percent | 0 free_space | 38776 free_percent | 0.06
تقوم الوظيفة بقراءة الجدول بأكمله وتظهر إحصائيات حول مقدار المساحة التي تشغلها البيانات في الملفات. المعلومات الرئيسية التي نهتم بها الآن هي الحقل tuple_percent: النسبة المئوية التي تشغلها البيانات المفيدة. إنه أقل من 100 بسبب الحمل الزائد لمعلومات الخدمة داخل الصفحة ، ولكنه مع ذلك مرتفع للغاية.
بالنسبة إلى الفهرس ، يتم عرض معلومات أخرى ، ولكن حقل avg_leaf_density له نفس المعنى: النسبة المئوية للمعلومات المفيدة (في الصفحات الورقية).
=> SELECT * FROM pgstatindex('vac_s') \gx
-[ RECORD 1 ]------+--------- version | 3 tree_level | 3 index_size | 72802304 root_block_no | 2722 internal_pages | 241 leaf_pages | 8645 empty_pages | 0 deleted_pages | 0 avg_leaf_density | 83.77 leaf_fragmentation | 64.25
وهنا حجم الجدول والفهرس:
=> SELECT pg_size_pretty(pg_table_size('vac')) table_size, pg_size_pretty(pg_indexes_size('vac')) index_size;
table_size | index_size ------------+------------ 65 MB | 69 MB (1 row)
الآن حذف 90 ٪ من جميع الخطوط. نختار الصفوف للحذف بشكل عشوائي ، بحيث يظل صف واحد على الأقل في كل صفحة ذات الاحتمال الكبير:
=> DELETE FROM vac WHERE random() < 0.9;
DELETE 450189
ما حجم الأشياء التي سيكون عليها بعد التنظيف العادي؟
=> VACUUM vac; => SELECT pg_size_pretty(pg_table_size('vac')) table_size, pg_size_pretty(pg_indexes_size('vac')) index_size;
table_size | index_size ------------+------------ 65 MB | 69 MB (1 row)
نرى أن الحجم لم يتغير: التنظيف العادي لا يمكن أن يقلل من حجم الملفات بأي شكل من الأشكال. على الرغم من أن كثافة المعلومات انخفضت بشكل واضح بنحو 10 مرات:
=> SELECT vac.tuple_percent, vac_s.avg_leaf_density FROM pgstattuple('vac') vac, pgstatindex('vac_s') vac_s;
tuple_percent | avg_leaf_density ---------------+------------------ 9.41 | 9.73 (1 row)
تحقق الآن مما يحدث بعد التنظيف الكامل. فيما يلي الملفات المستخدمة بواسطة الجدول والفهارس الآن:
=> SELECT pg_relation_filepath('vac'), pg_relation_filepath('vac_s');
pg_relation_filepath | pg_relation_filepath ----------------------+---------------------- base/41493/57392 | base/41493/57393 (1 row)
=> VACUUM FULL vac; => SELECT pg_relation_filepath('vac'), pg_relation_filepath('vac_s');
pg_relation_filepath | pg_relation_filepath ----------------------+---------------------- base/41493/57404 | base/41493/57407 (1 row)
الآن يتم استبدال الملفات بأخرى جديدة. انخفض حجم الجدول والفهرس بشكل كبير ، وزادت كثافة المعلومات وفقًا لذلك:
=> SELECT pg_size_pretty(pg_table_size('vac')) table_size, pg_size_pretty(pg_indexes_size('vac')) index_size;
table_size | index_size ------------+------------ 6648 kB | 6480 kB (1 row)
=> SELECT vac.tuple_percent, vac_s.avg_leaf_density FROM pgstattuple('vac') vac, pgstatindex('vac_s') vac_s;
tuple_percent | avg_leaf_density ---------------+------------------ 94.39 | 91.08 (1 row)
يرجى ملاحظة أن كثافة المعلومات في الفهرس قد ازدادت مقارنة بالأصل. تعد إعادة إنشاء فهرس (شجرة B) من البيانات المتاحة أكثر ربحية من إدراج البيانات في سطر فهرس موجود سطراً.
وظائف التمديد
pgstattuple استخدمنا قراءة الجدول بأكمله. إذا كان الجدول كبيرًا ، فهذا غير مريح ، وبالتالي هناك أيضًا دالة pgstattuple_approx ، التي تتخطى الصفحات التي تم وضع علامة عليها في خريطة الرؤية وتظهر الأرقام التقريبية.
الطريقة الأسرع ، لكن الأقل دقة ، هي تقدير نسبة حجم البيانات إلى حجم الملف في دليل النظام. يمكن العثور
على خيارات لمثل هذه الاستعلامات
على الويكي .
لا يتطلب التنظيف الكامل استخدامًا منتظمًا ، لأنه يحظر تمامًا كل العمل بالجدول (بما في ذلك الاستعلام عنه) طوال مدة عمله. من الواضح أن هذا قد لا يكون مقبولًا على أي نظام مستخدم بنشاط. سيتم النظر في الأقفال بشكل منفصل ، ولكن في الوقت الحالي
سنقتصر على ذكر امتداد
pg_repack ، الذي
يغلق الطاولة لفترة قصيرة فقط في نهاية العمل.
فرق مماثلة
هناك العديد من الأوامر التي تقوم أيضًا بإعادة إنشاء الجداول والفهارس بالكامل ، وهذا يشبه التنظيف الكامل. جميعهم يحظرون العمل تمامًا مع الجدول ، وكلهم يحذفون ملفات البيانات القديمة ويقومون بإنشاء ملفات جديدة.
يشبه الأمر CLUSTER في كل شيء إلى VACUUM FULL ، لكن بالإضافة إلى ذلك يرتب ماديًا إصدار السلاسل وفقًا لأحد المؤشرات المتوفرة. هذا يعطي المجدول القدرة على استخدام الوصول إلى الفهرس بشكل أكثر فعالية في بعض الحالات. ومع ذلك ، يجب فهم أن التجميع غير مدعوم: مع التغييرات اللاحقة على الجدول ، سيتم انتهاك الترتيب الفعلي لإصدارات الصف.
يعيد الأمر REINDEX إنشاء فهرس واحد على جدول. في الواقع ، يستخدم VACUUM FULL و CLUSTER هذا الأمر لإعادة إنشاء الفهارس.
يعمل الأمر TRUNCATE منطقياً مثل DELETE - يحذف جميع صفوف الجدول. لكن DELETE ، كما تمت مناقشته بالفعل ، لا يمثل سوى إصدار الصفوف المحذوفة ، مما يتطلب مزيدًا من التنظيف. يقتصر TRUNCATE على إنشاء ملف جديد ونظيف. كقاعدة عامة ، يعمل هذا بشكل أسرع ، ولكن ضع في اعتبارك أن TRUNCATE سيمنع العمل تمامًا مع الجدول طوال الوقت حتى نهاية المعاملة.
أن تستمر .