zoukankan      html  css  js  c++  java
  • PostgreSQL的MVCC(7)--Autovacuum

    我们已经提到过,通常(当没什么应用长时间持有事务时)VACUUM会执行其工作。问题是多久调用一次。

    如果我们很少对不断修改的表执行vacuum,那么表的大小将超出预期。此外,如果进行了太多更改,则下一个vacuum操作可能需要多次操作索引。

    如果我们过于频繁地对表执行vacuum,服务器将不断进行维护的工作-这也不是一件好事。

    请注意,定期启动VACUUM绝对不能解决问题,因为工作量会随着时间而变化。如果表开始更频繁地更改,则必须更频繁地对其进行清理。

    Autovacuum正是使我们能够根据表更改的强度启动vacuuming的技术。

    打开Autovacuum(设置Autovacuum配置参数)后,将启动自Autovacuum守护进程,按计划工作。清理本身是由Autovacuum worker进程完成的,该进程的多个实例可以并行运行。

    autovacuum活动发生时会处理实例中一组数据库, autovacuum活动是根据统计信息确定的,要收集统计信息,必须设置track_counts参数。切勿关闭autovacuum和track_counts,否则autovacuum功能将无法使用。

    每隔autovacuum_naptime秒,autovacuum启动程序将启动(使用postmaster进程)列表中每个数据库的辅助进程。换句话说,如果数据库中存在某些活动,则将以autovacuum_naptime秒为间隔向其发送工作进程。为此,如果有几个(N)个活动数据库可用,则工作进程每隔autovacuum_naptime秒被启动N次。但是,同时运行的工作进程总数受autovacuum_max_workers参数限制。

    启动时,worker进程将连接到为其分配的数据库,工作列表中包含以下内容:

    ·需要清理(require vacuuming)的所有表,物化视图和TOAST表。 ·所有需要分析(require analysis)的表和物化视图(不分析TOAST表,因为它们始终通过索引访问来访问)。

    然后,worker进程vacuum和/或分析列表中的对象。

    如果该进程无法在autovacuum_naptime秒内完成所有计划的工作,则autovacuum启动器进程将向该数据库发送另一个工作进程,并且它们将一起工作。 一起工作仅意味着第二个worker进程将建立自己的列表并进行处理。因此,将仅并行处理不同的表,但是在一个表的级别上没有并行性-如果一个工作进程已经在处理一个表,则另一个进程将跳过该表并继续进行。

    现在让我们更详细地说明“需要清理(require vacuuming)”和“需要分析(require analysis)”的含义。

    哪些表需要vacuum?

    如果死(即过时的)元组的数量超过指定的阈值,则认为需要进行清理。统计信息收集器将永久跟踪存储在pg_stat_all_tables表中的无效元组数。有两个参数指定阈值:

    ·autovacuum_vacuum_threshold定义一个绝对值(元组数)。 ·autovacuum_vacuum_scale_factor定义是否发生自动vacuum操作,表中行的百分比。

    总之:如果pg_stat_all_tables.n_dead_tup> = autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor * pg_class.reltupes,则需要进行清理。

    使用默认设置,autovacuum_vacuum_threshold = 50和autovacuum_vacuum_scale_factor = 0.2。当然,这里的autovacuum_vacuum_scale_factor是最重要的-对于大表,此参数至关重要。 20%的值似乎过高,很可能需要将其大大降低。

    对于不同的表,参数的最佳值可能会有所不同,并且取决于表的大小和更新的细节。设置一般合适的值是有意义的,并且如果需要,可以通过存储参数在某些表的级别上对参数进行特殊调整:

    ·autovacuum_vacuum_threshold和toast.autovacuum_vacuum_threshold. ·autovacuum_vacuum_scale_factor和toast.autovacuum_vacuum_scale_factor.

    为避免混淆,仅对少数几个按更改量和更改强度在表上进行合理处理,并且仅当全局设置值无法正常工作时才这样做。

     

    此外,可以在表格级别关闭autovacuum功能(尽管我们几乎无法想到有必要这样做的原因):

    ·autovacuum_enabled和toast.autovacuum_enabled。

    例如,上次我们创建的vac表中的autovacuum已关闭,以便出于演示目的手动控制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');
    

     

    哪些表需要analysis?

    自动分析的情况与autovacuum类似。更新(自上次分析以来)元组的数量超过了两个相似参数指定的阈值被认为是需要分析的:pg_stat_all_tables.n_mod_since_analyze> = autovacuum_analyze_threshold + autovacuum_analyze_scale_factor * pg_class.reltupes。

    自动分析的默认设置有所不同:autovacuum_analyze_threshold = 50和autovacuum_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'; -- to aviod waiting long
    => ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.03;  -- 3%
    => ALTER SYSTEM SET autovacuum_vacuum_threshold = 0;
    => ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.02; -- 2%
    => ALTER SYSTEM SET autovacuum_analyze_threshold = 0;
    

      

    => SELECT pg_reload_conf();
     pg_reload_conf
    ----------------
     t
    (1 row)
    

    现在,让我们创建一个与上次使用的表类似的表,并在其中插入1000行。autovacuum在表级别是关闭的,我们将手动打开它。如果不这样做,这些示例将无法重现,因为autovacuum可能在错误的时间触发。

    => 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尽管1000行中的3%构成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)
    

    让我们回到autovacuum:

    => 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。统计数据表明该表没有死元组...,这是事实。还没有对表执行vacuum。仅仅以append-only模式插入的表不会被vacuum,visibility map也不会被更新。但是这使得index-only扫描变得不可能。

    经验教训:如果index-only扫描至关重要,则可能需要手动调用vacuum过程。

    现在,让我们再次关闭autovacuum并更新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)
    

    满足了vacuum触发的条件。让我们打开autovacuum,在短暂的暂停后,我们会看到表已经被处理:

    => 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)
    

    Load throttling

    由于vacuum是一页一页地工作,所以不会阻塞其他进程,但是它会在系统上产生额外的负载,并会显著影响性能。

    Throttling for VACUUM

    为了能够控制vacuum强度,从而控制对系统的影响,该过程需要交替进行工作执行和等待。vacuum过程累积的代价超过参数vacuum_cost_limit指定的结果后,睡眠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个带逐出的页面。显然,这些数字是暂定的,但是选择更准确的数字是没有意义的。

    Throttling for autovacuuming

    对于vacuum过程,Load throttling的工作方式与VACUUM相同。但是对于autovacuum处理和手动启动的VACUUM以不同的强度工作,autovacuum具有其自己的参数:autovacuum_vacuum_cost_limit和autovacuum_vacuum_cost_delay。如果这些参数的值为-1,则使用vacuum_cost_limit和/或vacuum_cost_delay的值。

    默认情况下,autovacuum_vacuum_cost_limit = -1(即使用vacuum_cost_limit = 200的值),而autovacuum_vacuum_cost_delay = 20 ms。在现代硬件上,autovacuum将非常缓慢。

    在版本12中,autovacuum_vacuum_cost_delay的值减小为2 ms。

    此外,我们应该注意,这些设置指定的限制对于所有worker进程都是通用的。换句话说,当同时进行的工作进程数改变时,总负载保持不变。因此,为提高自动清理性能,在添加工作进程时,还应增加autovacuum_vacuum_cost_limit。

    内存的使用和监控

    上次我们观察到VACUUM如何使用大小为maintenance_work_mem的RAM来存储要清理的tid。

    Autovacuum的作用完全相同。但是,如果将autovacuum_max_workers设置为较大的值,则可以同时执行多个工作进程。此外,所有内存都可以一次分配,而不是根据需要分配。因此,对于工作进程,可以通过autovacuum_work_mem参数设置其自身的限制。此参数的默认值为-1,即不使用。

    如前所述,VACUUM也可以使用最小的内存大小来工作。但是,如果在表上创建索引,则一小部分的maintenance_work_mem可能需要重复进行索引扫描。autovacuum也是如此。理想情况下,autovacuum_work_mem最小值是不会发生重复扫描。

    我们已经看到,要监视VACUUM,可以使用VERBOSE选项(不能为autovacuum指定)或pg_stat_progress_vacuum视图(但是仅显示当前信息)。因此,监视自动清理的主要方法是使用log_autovacuum_min_duration参数,该参数将信息输出到服务器消息日志。默认情况下处于关闭状态(设置为-1)。合理地打开此参数(值为0,将输出所有autovacuum运行的信息)并观察。

    输出信息如下所示:

    => 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
    

      

    所有必要的信息都可以在这里找到。

    提醒,降低vacuum触发的阈值通常是有意义的:一次处理较少的数据,而不是增加内存大小。

    使用上述视图来监视需要清理的表列表的长度也是合理的。 列表长度的增加将表明autovacuum处理过程没有时间来完成其工作,需要更改设置。

     

     

    本文地址:

    https://habr.com/en/company/postgrespro/blog/486104/

      

      

     

  • 相关阅读:
    HDU 3466(01背包变种
    HDU 2639(01背包第K大)
    POJ 2184(01背包)(负体积)
    UVA 562(01背包)
    UVA 624(01背包记录路径)
    SQL总结二
    oracle--知识点汇总1
    时间日期----java
    字符串、数值----转换
    字符串反转----示例
  • 原文地址:https://www.cnblogs.com/abclife/p/13662484.html
Copyright © 2011-2022 走看看