MVCC-7。 自动清洁

让我提醒您,我们从与隔离相关的问题入手,对低级组织数据进行了讨论,详细讨论了行版本以及如何从版本中获取快照

然后,我们研究了页内清洁 (和HOT更新), 常规清洁 ,但今天我们讨论了自动清洁。

自动清洁(自动真空)


我们已经说过,在正常条件下(当没有人长期持有交易期限时)的普通清洗必须应付其工作。 问题是多久调用一次。

如果您很少更换表,它将变得比您想要的更大。 此外,对于下一次清洁,如果积累了太多更改,则可能需要多次通过索引。

如果您经常清理表,那么服务器将不停地进行维护工作,而不是进行有用的工作-也不好。

请注意,开始定期进行的定期清洁不能解决问题,因为负载会随着时间而变化。 如果开始更积极地更新表,则应更频繁地对其进行清理。

自动清理只是允许您开始清理的机制,具体取决于表中的更改活动。

启用自动清理autovacuum配置参数)后,系统将始终存在计划进行工作的系统中的autovacuum启动器进程,而autovacuum worker工作流程将参与实际清理,其多个实例可以并行工作。

自动真空启动器进程会编译其中有任何活动的数据库列表。 活动由统计信息确定,要收集活动,必须设置track_counts参数。 切勿关闭autovacuumtrack_counts ,否则自动 清理将不起作用。

进入autovacuum_naptime后, autovacuum启动器进程将启动(使用postmaster进程)列表中每个数据库的工作流。 换句话说,如果数据库中有任何活动,则工作流将以autovacuum_naptime间隔进入其中。 为此,如果有几个活动数据库(N个),则启动工作进程的次数是autovacuum_naptime的 N倍。 但是同时,同时工作的工作流程总数受autovacuum_max_workers参数限制。

启动后,工作流将连接到它指定的数据库,并从构建列表开始:

  • 需要清除的所有表,实例化视图和吐司表,
  • 所有需要分析的表和实例化表示(不分析Toast表,因为它们始终通过索引访问)。

此外,工作流依次清洗和/或分析选定的对象,并在清洗完成时结束。

如果该进程尚未完成autovacuum_naptime的所有预期工作,则autovacuum启动器进程会将另一个工作流发送到同一数据库,并且它们将一起工作。 “在一起”仅表示第二个过程将建立并遵循其表列表。 因此,将并行处理不同的表,但是在一个表的级别上没有并行性-如果一个工作进程已经在该表上工作,则另一个将跳过它并继续进行。

长时间以来一直在讨论是否需要并行处理,但是该补丁尚未被采用。

现在,让我们仔细看看什么是“需要清洁”和“需要分析”。

哪些桌子需要清洁


据信,如果“死”的数目,即无关的弦的版本超过设定的阈值,则有必要进行清洁。 失效版本的数量由统计信息收集器不断收集并存储在pg_stat_all_tables表中。 阈值由两个参数设置:

  • autovacuum_vacuum_threshold定义绝对值(单位),
  • autovacuum_vacuum_scale_factor确定表中行的比例。

最终公式是:如果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_thresholdtoast.autovacuum_vacuum_threshold
  • autovacuum_vacuum_scale_factortoast.autovacuum_vacuum_scale_factor

为避免混淆,仅应在少数表中以变化的数量或强度来脱颖而出,并且仅在全局设置值不合适的情况下才可以这样做。

此外,可以在表级别关闭自动清理(尽管很难想到这样做的必要性):

  • autovacuum_enabledtoast.autovacuum_enabled

例如,上次我们创建了一个禁用自动清理的vac表,以进行演示(用于演示)来管理手动清理。 可以如下更改存储参数:

=> ALTER TABLE vac SET (autovacuum_enabled = off); 

为了使以上所有内容正式化,我们将创建一个视图,显示当前需要清除哪些表。 假设可以在表级别覆盖该参数,它将使用返回参数当前值的函数:

 => CREATE FUNCTION get_value(param text, reloptions text[], relkind "char") RETURNS float AS $$ SELECT coalesce( --    ,    (SELECT option_value FROM pg_options_to_table(reloptions) WHERE option_name = CASE --  toast-    WHEN relkind = 't' THEN 'toast.' ELSE '' END || param ), --      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> = 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

由于不分析吐司表,因此没有相应的参数。

让我们创建一个视图进行分析:

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

现在,创建一个与上次使用的表相似的表,然后在其中插入一千行。 在表级别禁用了自动清理,我们将自己打开它。 如果不这样做,则示例将无法重现,因为自动清洁可能会在错误的时间进行。

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

您应注意两点。 首先,尽管1000行中的3%是30行,但max_dead_tup = 0。 事实是我们还没有该表的统计信息,因为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); 

短暂的停顿后,我们看到该表已被分析,而不是max_mod_tup中的零,我们看到了正确的20行:

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

让我们回到自动清洁:

 => 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。统计数据表明表中没有行的失效版本……这是事实。 我们的桌子上还没有东西需要清洁。 因此,仅在仅追加模式下使用的任何表都不会被清除,因此不会为其更新可见性图。 这使得不可能仅使用索引扫描(仅索引扫描)。

(下一次,我们将看到迟早会清理仅附加表,但这很少发生。)

实用结论:如果仅使用索引扫描很重要,则可能需要致电进行手动清洁。

现在,再次关闭自动清洁并更新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) 

现在,满足触发自动清洁的条件。 打开自动清理,稍停片刻后,我们将看到该表已被处理:

 => 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_cost_limit工作单位,然后在vacuum_cost_delay ms上入睡。

默认设置为vacuum_cost_limit = 200, vacuum_cost_delay =0。最后一个零实际上意味着(正常)清洁操作不会进入睡眠状态,因此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页。 显然,这些是相当任意的数字,但是更精确地选择它们是没有意义的。

自动清洁规定


自动清洁过程中的负载控制与常规清洁相同。 但是,为了使手动清洁自动清洁可以在不同的强度下工作, 自动处理具有自己的参数: autovacuum_vacuum_cost_limitautovacuum_vacuum_cost_delay 。 如果这些参数的值为-1,那么将使用vacuum_cost_limit和/或vacuum_cost_delay中的值

默认情况下, autovacuum_vacuum_cost_limit = -1(即,使用值vacuum_cost_limit = 200)和autovacuum_vacuum_cost_delay = 20ms。 在具有这些编号的现代设备上,自动清洁将非常非常缓慢地工作。

在版本12中, autovacuum_vacuum_cost_delay的值将减小为2ms,可以认为这是更合适的一阶近似值。

此外,应注意,这些参数设置的限制对于所有工作流程都是通用的。 换句话说,随着并发工作流程数量的变化,总负载将保持恒定。 因此,如果任务是提高自动清理的性能,那么在添加工作流时,值得增加autovacuum_vacuum_cost_limit

内存使用和监控


上次,我们研究了清理如何使用大小为maintenance_work_mem的 RAM存储器来存储要清理的行的版本标识符。

自动清洁功能完全相同。 但是,如果将autovacuum_max_workers设置为较大的值,则可能有许多并发进程。 此外,所有内存均立即且完全分配,而不是必须分配的。 因此,对于自动清理工作流程,您可以使用autovacuum_work_mem参数设置自己的限制。 默认情况下,此参数为-1,即不使用。

如前所述,清理可以使用最少的内存进行。 但是,如果在表上创建索引,则较小的maintenance_work_mem值可能导致重复进行索引扫描。 自动清洁也是如此。 理想情况下,应选择一个不会发生重复扫描的最小值autovacuum_work_mem

我们看到了要监视清洗的情况,可以使用VERBOSE参数(但不能为自动清洗指定该参数)或pg_stat_progress_vacuum视图(但仅显示当前信息)。 因此,监视自动清除的主要方法是log_autovacuum_min_duration参数,该参数在服务器的消息日志中显示信息。 默认情况下,它是关闭的(设置为-1)。 启用此参数是有原因的(值为0时,将显示有关所有自动清洁开始的信息)并观察数字。

输出如下所示:

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

所有必要的信息都在这里显示。

回想一下,通常您不应该增加内存大小,而应降低清理阈值,以便一次处理较少的数据。

使用以上视图监视需要清除的表列表的长度也可能是有意义的。 列表长度的增加将表明自动清洁没有时间来完成其工作,并且需要更改设置。

待续

Source: https://habr.com/ru/post/zh-CN452762/


All Articles