让我提醒您,我们从与
隔离相关的问题入手
,对低级组织数据进行了讨论,详细
讨论了行版本以及如何从版本中获取
快照 。
然后,我们研究了
页内清洁 (和HOT更新),
常规清洁 ,但今天我们讨论了自动清洁。
自动清洁(自动真空)
我们已经说过,在正常条件下(当没有人长期持有交易期限时)的普通清洗必须应付其工作。 问题是多久调用一次。
如果您很少更换表,它将变得比您想要的更大。 此外,对于下一次清洁,如果积累了太多更改,则可能需要多次通过索引。
如果您经常清理表,那么服务器将不停地进行维护工作,而不是进行有用的工作-也不好。
请注意,开始定期进行的定期清洁不能解决问题,因为负载会随着时间而变化。 如果开始更积极地更新表,则应更频繁地对其进行清理。
自动清理只是允许您开始清理的机制,具体取决于表中的更改活动。
启用
自动清理 (
autovacuum配置参数)后,系统将始终存在计划进行工作的系统中的autovacuum启动器进程,而autovacuum worker工作流程将参与实际清理,其多个实例可以并行工作。
自动真空启动器进程会编译其中有任何活动的数据库列表。 活动由统计信息确定,要收集活动,必须设置
track_counts参数。 切勿关闭
autovacuum和
track_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_threshold和toast.autovacuum_vacuum_threshold ,
- autovacuum_vacuum_scale_factor和toast.autovacuum_vacuum_scale_factor 。
为避免混淆,仅应在少数表中以变化的数量或强度来脱颖而出,并且仅在全局设置值不合适的情况下才可以这样做。
此外,可以在表级别关闭自动清理(尽管很难想到这样做的必要性):
- autovacuum_enabled和toast.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';
=> 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_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 = 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
所有必要的信息都在这里显示。
回想一下,通常您不应该增加内存大小,而应降低清理阈值,以便一次处理较少的数据。
使用以上视图监视需要清除的表列表的长度也可能是有意义的。 列表长度的增加将表明自动清洁没有时间来完成其工作,并且需要更改设置。
待续 。