Autovacuum เชิงรุกบน PostgreSQL


42

ฉันกำลังพยายามให้ PostgreSQL ทำการดูดฐานข้อมูลของฉันโดยอัตโนมัติ ฉันได้กำหนดค่าสูญญากาศอัตโนมัติดังนี้:

  • autovacuum_vacuum_cost_delay = 0 # ปิดสูญญากาศที่ใช้ต้นทุน
  • autovacuum_vacuum_cost_limit = 10,000 # ค่าสูงสุด
  • autovacuum_vacuum_threshold = 50 # ค่าเริ่มต้น
  • autovacuum_vacuum_scale_factor = 0.2 # ค่าเริ่มต้น

ฉันสังเกตว่าสูญญากาศอัตโนมัติจะเริ่มขึ้นเฉพาะเมื่อฐานข้อมูลไม่ได้โหลดดังนั้นฉันจึงเข้าสู่สถานการณ์ที่มีสิ่งอันดับที่ตายแล้วมากกว่าสิ่งอันดับสด ดูตัวอย่างภาพหน้าจอที่แนบมา หนึ่งในตารางมี 23 tuples สด แต่ 16845 tuples ตายรอสูญญากาศ นั่นมันบ้า!

Tuples ที่ตายแล้วมากมาย

Auto vacuum kicks เมื่อการทดสอบเสร็จสิ้นและเซิร์ฟเวอร์ฐานข้อมูลไม่ได้ใช้งานซึ่งไม่ใช่สิ่งที่ฉันต้องการเพราะฉันต้องการให้สูญญากาศอัตโนมัติเตะเมื่อใดก็ตามที่จำนวนของ tuples ที่ตายเกินกว่า 20% tuples สด + 50 เนื่องจากฐานข้อมูล การกำหนดค่า สูญญากาศอัตโนมัติเมื่อเซิร์ฟเวอร์ไม่ได้ใช้งานไม่มีประโยชน์สำหรับฉันเนื่องจากเซิร์ฟเวอร์ที่ใช้งานจริงคาดว่าจะมีการอัปเดต 1000 วินาที / วินาทีเป็นระยะเวลานานซึ่งเป็นสาเหตุที่ฉันต้องใช้ระบบสูญญากาศอัตโนมัติในการทำงานแม้ว่าเซิร์ฟเวอร์จะโหลด

มีอะไรที่ฉันขาดหายไปไหม ฉันจะบังคับให้สูญญากาศอัตโนมัติทำงานในขณะที่เซิร์ฟเวอร์มีภาระมากได้อย่างไร

ปรับปรุง

นี่อาจเป็นปัญหาการล็อคหรือไม่ ตารางที่ต้องสงสัยคือตารางสรุปที่บรรจุผ่านทริกเกอร์การแทรกหลัง ตารางเหล่านี้ถูกล็อคในโหมด SHARE ROW EXCLUSIVE เพื่อป้องกันการเขียนพร้อมกันในแถวเดียวกัน

คำตอบ:


40

Eelke เกือบจะถูกต้องแล้วว่าการล็อคของคุณกำลังปิดกั้น autovacuum Autovacuum ได้รับการออกแบบมาเพื่อให้ผู้ใช้สามารถเข้าถึงกิจกรรมของผู้ใช้ได้อย่างจงใจ หากตารางเหล่านั้นถูกล็อคการลบอัตโนมัติจะไม่สามารถดูดฝุ่นได้

อย่างไรก็ตามสำหรับลูกหลานฉันต้องการยกตัวอย่างชุดการตั้งค่าสำหรับการเติมข้อความอัตโนมัติที่มีความก้าวร้าวมากเกินไปเนื่องจากการตั้งค่าที่คุณให้ไม่ได้ทำเช่นนั้น โปรดทราบว่าการทำให้การป้อนอัตโนมัติเชิงรุกมากขึ้นนั้นไม่น่าจะแก้ปัญหาของคุณได้ นอกจากนี้โปรดทราบว่าการตั้งค่า autovacuum เริ่มต้นนั้นใช้การทดสอบมากกว่า 200 ครั้งโดยใช้ DBT2 เพื่อหาการรวมกันที่ดีที่สุดของการตั้งค่าดังนั้นค่าเริ่มต้นควรจะดีถ้าคุณไม่มีเหตุผลที่จะคิดอย่างอื่นหรือฐานข้อมูลของคุณอยู่นอก กระแสหลักสำหรับฐานข้อมูล OLTP (เช่นฐานข้อมูลขนาดเล็กซึ่งได้รับการอัพเดท 10K ต่อวินาทีหรือคลังข้อมูล 3TB)

ขั้นแรกให้เปิดการบันทึกเพื่อที่คุณจะได้สามารถตรวจสอบได้ว่าการทำ autovacuum ทำในสิ่งที่คุณคิดหรือไม่:

log_autovacuum_min_duration = 0

จากนั้นให้คนทำงาน autovac มากขึ้นและให้พวกเขาตรวจสอบตารางบ่อยขึ้น:

autovacuum_max_workers = 6
autovacuum_naptime = 15s

ลดเกณฑ์สำหรับการดูดฝุ่นอัตโนมัติและการวิเคราะห์อัตโนมัติเพื่อทริกเกอร์ให้เร็วขึ้น:

autovacuum_vacuum_threshold = 25
autovacuum_vacuum_scale_factor = 0.1

autovacuum_analyze_threshold = 10
autovacuum_analyze_scale_factor = 0.05 

จากนั้นให้ลดขนาดของ Autovacuum ให้น้อยลงดังนั้นจึงเสร็จสมบูรณ์เร็วขึ้น แต่ด้วยค่าใช้จ่ายในการส่งผลกระทบต่อกิจกรรมผู้ใช้พร้อมกันมากขึ้น:

autovacuum_vacuum_cost_delay = 10ms
autovacuum_vacuum_cost_limit = 1000

มีโปรแกรมเต็มรูปแบบของคุณสำหรับ autovacuum ที่ก้าวร้าวโดยทั่วไปซึ่งอาจเป็นประโยชน์สำหรับฐานข้อมูลขนาดเล็กที่มีอัตราการอัปเดตสูงมาก แต่อาจส่งผลกระทบต่อกิจกรรมของผู้ใช้พร้อมกันมากเกินไป

นอกจากนี้โปรดทราบว่าพารามิเตอร์ autovacuum สามารถปรับได้ต่อตารางซึ่งเป็นคำตอบที่ดีกว่าสำหรับการปรับพฤติกรรมของ autovacuum

อย่างไรก็ตามอีกครั้งก็ไม่น่าจะแก้ไขปัญหาที่แท้จริงของคุณ


36

เพียงเพื่อดูว่าตารางใดที่มีคุณสมบัติเหมาะสมสำหรับการบันทึกอัตโนมัติการสืบค้นต่อไปนี้อาจใช้ (ขึ้นอยู่กับhttp://www.postgresql.org/docs/current/static/routine-vacuuming.html ) อย่างไรก็ตามโปรดทราบว่าแบบสอบถามไม่ได้ค้นหาการตั้งค่าเฉพาะของตาราง:

 SELECT psut.relname,
     to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') as last_vacuum,
     to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') as last_autovacuum,
     to_char(pg_class.reltuples, '9G999G999G999') AS n_tup,
     to_char(psut.n_dead_tup, '9G999G999G999') AS dead_tup,
     to_char(CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
         + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
            * pg_class.reltuples), '9G999G999G999') AS av_threshold,
     CASE
         WHEN CAST(current_setting('autovacuum_vacuum_threshold') AS bigint)
             + (CAST(current_setting('autovacuum_vacuum_scale_factor') AS numeric)
                * pg_class.reltuples) < psut.n_dead_tup
         THEN '*'
         ELSE ''
     END AS expect_av
 FROM pg_stat_user_tables psut
     JOIN pg_class on psut.relid = pg_class.oid
 ORDER BY 1;

11

ใช่มันเป็นปัญหาการล็อค ตามหน้านี้(ไม่เต็ม) สูญญากาศต้องการการอัปเดตแบ่งปันเป็นพิเศษซึ่งถูกบล็อกโดยระดับล็อคที่คุณใช้

คุณแน่ใจหรือว่าต้องการล็อคนี้ PostgreSQL เป็นไปตามข้อกำหนดของ ACID ดังนั้นการเขียนพร้อมกันในกรณีส่วนใหญ่จะไม่เกิดปัญหาเนื่องจาก PostgreSQL จะยกเลิกการทำรายการใดรายการหนึ่งหากการละเมิดซีเรียลไลซ์เซชั่นเกิดขึ้น

นอกจากนี้คุณยังสามารถล็อคแถวโดยใช้SELECT FORDATEเพื่อล็อคแถวแทนทั้งตาราง

ทางเลือกอื่นที่ไม่มีการล็อกคือการใช้ระดับการแยกธุรกรรมแบบอนุกรม อย่างไรก็ตามสิ่งนี้อาจส่งผลต่อประสิทธิภาพของการทำธุรกรรมอื่น ๆ และคุณควรเตรียมพร้อมสำหรับความล้มเหลวในการทำให้เป็นอนุกรมมากขึ้น


นี่เป็นเพราะการล็อคของตารางสรุปเนื่องจากสิ่งเหล่านี้ถูกล็อคโดยใช้ SHARE ROW EXCLUSIVE MODE การเขียนพร้อมกันโดยไม่มีการล็อคอาจสำเร็จ แต่แน่นอนว่าพวกเขาจะจบลงด้วยค่าที่ผิด ลองนึกภาพฉันยังคงนับ N ของแถวประเภท X ถ้าฉันแทรก 2 แถวของประเภท X พร้อมกันโดยไม่ล็อคฉันจะจบลงด้วย N + 1 ในตารางสรุปของฉันแทน N + 2 โซลูชั่นที่ฉันนำมาใช้ คือการมีงาน cron ที่ vacuums ตารางสรุปในฐานข้อมูลของฉันด้วยตนเอง มันใช้งานได้ดีและดูเหมือนจะเป็นแนวทางที่แนะนำ แต่รู้สึกเหมือนแฮ็คกับฉันมากเกินไป
CadentOrange

6

การเพิ่มจำนวนกระบวนการ autovacuum และการลด naptime อาจช่วยได้ นี่คือการกำหนดค่าสำหรับ PostgreSQL 9.1 ที่ฉันใช้บนเซิร์ฟเวอร์ที่เก็บข้อมูลสำรองและด้วยเหตุนี้จึงมีกิจกรรมแทรกจำนวนมาก

http://www.postgresql.org/docs/current/static/runtime-config-autovacuum.html

autovacuum_max_workers = 6              # max number of autovacuum subprocesses
autovacuum_naptime = 10         # time between autovacuum runs
autovacuum_vacuum_cost_delay = 20ms     # default vacuum cost delay for

ฉันจะลองลดระดับลงcost_delayเพื่อให้การดูดฝุ่นรุนแรงยิ่งขึ้น

ฉันยังสามารถทดสอบการบันทึกอัตโนมัติโดยใช้ pgbench

http://wiki.postgresql.org/wiki/Pgbenchtesting

ตัวอย่างการต่อสู้สูง:

สร้างฐานข้อมูล bench_replication

pgbench -i -p 5433 bench_replication

เรียกใช้ pgbench

pgbench -U postgres -p 5432 -c 64 -j 4 -T 600 bench_replication

ตรวจสอบสถานะการบันทึกอัตโนมัติ

psql
>\connect bench_replicaiton
bench_replication=# select schemaname, relname, last_autovacuum from pg_stat_user_tables;
 schemaname |     relname      |        last_autovacuum        
------------+------------------+-------------------------------
 public     | pgbench_branches | 2012-07-18 18:15:34.494932+02
 public     | pgbench_history  | 
 public     | pgbench_tellers  | 2012-07-18 18:14:06.526437+02
 public     | pgbench_accounts | 

6

สคริปต์ "ที่มีคุณสมบัติสำหรับ autovacuum" ที่มีอยู่มีประโยชน์มาก แต่ (ตามที่ระบุไว้อย่างถูกต้อง) ไม่มีตัวเลือกเฉพาะของตาราง นี่คือเวอร์ชันดัดแปลงของมันที่คำนึงถึงตัวเลือกเหล่านั้น:

WITH rel_set AS
(
    SELECT
        oid,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_threshold=', 2), ',', 1)::BIGINT
        END AS rel_av_vac_threshold,
        CASE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)
            WHEN '' THEN NULL
        ELSE split_part(split_part(array_to_string(reloptions, ','), 'autovacuum_vacuum_scale_factor=', 2), ',', 1)::NUMERIC
        END AS rel_av_vac_scale_factor
    FROM pg_class
) 
SELECT
    PSUT.relname,
    to_char(PSUT.last_vacuum, 'YYYY-MM-DD HH24:MI')     AS last_vacuum,
    to_char(PSUT.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum,
    to_char(C.reltuples, '9G999G999G999')               AS n_tup,
    to_char(PSUT.n_dead_tup, '9G999G999G999')           AS dead_tup,
    to_char(coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples, '9G999G999G999') AS av_threshold,
    CASE
        WHEN (coalesce(RS.rel_av_vac_threshold, current_setting('autovacuum_vacuum_threshold')::BIGINT) + coalesce(RS.rel_av_vac_scale_factor, current_setting('autovacuum_vacuum_scale_factor')::NUMERIC) * C.reltuples) < PSUT.n_dead_tup
        THEN '*'
    ELSE ''
    END AS expect_av
FROM
    pg_stat_user_tables PSUT
    JOIN pg_class C
        ON PSUT.relid = C.oid
    JOIN rel_set RS
        ON PSUT.relid = RS.oid
ORDER BY C.reltuples DESC;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.