首页 > 系统相关 >autovacuum进程

autovacuum进程

时间:2023-09-20 23:35:23浏览次数:41  
标签:autovacuum txid cost xmax vacuum 进程 元组

autovacuum 是 postgresql 里非常重要的一个服务端进程,能够自动地执行,在一定条件下自动地对 dead tuples 进行清理并对表进行分析。

目录

1.1 什么是autovacuum?

autocuum是启动postgresql时自动启动的后台实用程序之一。

在生产环境中不应该将其关闭

autovacuum=on #默认on
track_counts=on #默认on

1.2 为什么需要autovacuum

  • 需要vacuum来移除死元组
  • 防止死元组膨胀
  • 更新表的统计信息进行分析,以便提供优化器使用
  • autovacuum launcher使用Stats Collector的后台进程收集的信息来确定autovacuum的候选表列表

1.3 记录autovacuum

log_autovacuum_min_duration=-1 # -1表示不记录,0表示记录所有的,大于0(如250ms,1s,1min,1h,1d)表示真空操作大于此值的操作

1.4 什么时候做autovacuum

Autovacuum实际操作的内容

  1. Autovacuum

    触发条件(如果由于更新或删除,表中的实际死元组数超过此有效阈值,则该表将成为autovacuum的候选表)

  2. Autovacuum ANALYZE

    thresold a table = autovacuum_analyze_scale_factor * number of tuples + autovacuum_analyze_threshold

autovacuum_vacuum_threshold = 50       # min number of row updates before vacuum
autovacuum_analyze_threshold = 50      # min number of row updates before analyze
autovacuum_vacuum_scale_factor = 0.2   # fraction of table size before vacuum
autovacuum_analyze_scale_factor = 0.1  # fraction of table size before analyze

举个例子

test表有1000行记录。

上面的公式作为参考:

当test表成为autovacuum vacuum的候选者,满足的条件如下:

更新/删除的记录:0.2*1000 + 50 = 250

插入/更新/删除的记录:0.1*1000 + 50 = 150

1.5 这是不是一个问题

table1=100行

其触发analyze和vacuum的阈值分别是:60和70。

table2=100w行

其触发analyze和vacuum的阈值分别是:100050和200050.

1.6 如何确定需要调整其autovacuum setting的表

为了单独调整表的autovacuum,必须知道一段时间内表上的插入/删除/更新数。

postgres=# select n_tup_ins,n_tup_upd,n_tup_del,n_live_tup,n_dead_tup from pg_stat_user_tables where schemaname = 'public' and relname = 'test';
 n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup 
-----------+-----------+-----------+------------+------------
      1000 |         0 |       249 |        751 |          0
(1 row)

表autovacuum setting的设置

可以通过设置单个表的存储参数来重写此行为,这样会忽略全局设置。

postgres=# alter table test set (autovacuum_vacuum_cost_limit=500);
ALTER TABLE
postgres=# alter table test set (autovacuum_vacuum_cost_delay=10);
ALTER TABLE
postgres=# \d+ test
                         Table "public.test"
 Column |  Type   | Modifiers | Storage  | Stats target | Description 
--------+---------+-----------+----------+--------------+-------------
 id     | integer |           | plain    |              | 
 info   | text    |           | extended |              | 
Has OIDs: no
Options: autovacuum_vacuum_cost_limit=500, autovacuum_vacuum_cost_delay=10

1.7 一次可以运行多个autovacuum过程

在可能包含多个数据库的实例/集群上,一次运行的autovacuum进程数不能超过下面参数设置的值:

autovacuum_max_workers = 3             # max number of autovacuum subprocesses
                                        # (change requires restart)
autovacuum_naptime = 1min         # time between autovacuum runs

启动下一个autovacuum之前的等待时间:

autovacuum_naptime/N,其中N是实例中数据库的总数。真空是IO密集型操作,设置了一些参数来最小化真空对IO的影响。

# 可达到的总成本限制,结合所有的autovacuum作业
autovacuum_vacuum_cost_limit = -1      # default vacuum cost limit for
                                        # autovacuum, -1 means use
                                        # vacuum_cost_limit
# 当一个清理工作达到autovacuum_vacuum_cost_limit指定的成本限制时,autovacuum将休眠数毫秒
autovacuum_vacuum_cost_delay = 20ms    # default vacuum cost delay for
                                        # autovacuum, in milliseconds;
                                        # -1 means use vacuum_cost_delay
                                        
# - Cost-Based Vacuum Delay -
#vacuum_cost_delay = 0                  # 0-100 milliseconds
# 读取已在共享缓冲区中且不需要磁盘读取的页的成本
vacuum_cost_page_hit = 1               # 0-10000 credits
# 获取不在共享缓冲区中的页的成本
vacuum_cost_page_miss = 10             # 0-10000 credits
# 在每一页中发现死元组时写入该页的成本
vacuum_cost_page_dirty = 20            # 0-10000 credits
#vacuum_cost_limit = 200                # 1-10000 credits                                        

1秒(1000ms)中会发生什么

在读取延迟为0毫秒的最佳情况下,autovacuum可以唤醒并进入睡眠50次(1000毫秒/20毫秒),因为唤醒之间的延迟需要20毫秒。

由于在共享缓冲区中每次读取一个页面的相关成本时1,因此在每个唤醒中可以读取200个页面(因为上面把总成本限制设置为200),在50个唤醒中可以读取50*200个页面。

如果在共享缓冲区中找到了所有具有死元组的页,并且autovacuum代价延迟为20毫秒,则它可以在每一轮中读取:((200/vacuum_cost_page_hit)*8)kb,这需要等待autovacuum代价延迟时间量。

因此,考虑到块大小为8192字节,autovacuum最多可以读取:50 * 200 * 8kb=78.13 MB/s。

如果块不在共享缓冲区中,需要从磁盘读取,则autovacuum最多可以读取:50 * 200 * 8kb=7.81 MB/s。

现在,为了从页/块中删除死元组,写操作的开销是:vacuum_cost_page_dirty,默认20。

一个autovacuum每秒最多可以写/脏:50 * (200/vacuum_cost_page_dirty) * 8kb=3.9 MB/s。

谨慎设置autovacuum_max_workers

通常,此成本平均分配给实例中运行的所有autovacuum过程的autovacuum_max_workers数。因此增加vacuum_max_workers可能会延迟当前运行的autovacuum workers的autovacuum执行。而增加autovacuum_vacuum_cost_limit可能会导致IO瓶颈。可以通过设置单个表的存储参数来重写此行为,这样会忽略全局设置。

1.8 冻结txids

Transaction ID

  • 每当事物开始时,事务管理器就会分配一个唯一的标识符,称为事物id(txid);

  • PostgreSQL的txid是一个32位无符号整数,大约42亿;

  • 冻结txid的目的就是为了行可见性;

    1、删除指向相应死元组的死元组和索引元组;

    2、清楚CLOG中不必要的部分。

    3、冻结旧txids;

    4、更新FSM,VM和统计信息。

  • 请注意,BEGIN命令没有指定txid。在PostgreSQL中,当第一个命令在BEGIN命令执行后执行时,事务管理器会分配一个txid,然后分配它的事物开始。

postgres=# begin;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
         100
(1 row)
  • 0表示无效的 txid。
  • 1表示Bootstrap txid,仅在数据库集群初始化时使用。
  • 2表示Frozen txid。
  • Txid 可以相互比较。例如,在 txid 100 的观点上,大于 100 的 txid 是“未来”,从 txid 100 看是不可见的;小于 100 的 txid 是“过去的”并且可见(图 5.1 a))。

PostgreSQL 中的事务 ID

image

元组结构

image

HeapTupleHeaderData 结构在src/include/access/htup_details.h中定义。

  • t_xmin保存插入此元组的事务的 txid。
  • t_xmax保存删除或更新此元组的事务的 txid。如果此元组尚未删除或更新,则 t_xmax 设置为0,表示无效。
  • t_cid保存的是命令id(cid),表示从0开始在当前事务中执行该命令之前执行了多少条SQL命令。例如,假设我们在单个事务中执行了三个INSERT命令:’BEGIN; 插入; 插入; 插入; 犯罪;’。如果第一个命令插入此元组,则 t_cid 设置为 0。如果第二个命令插入此元组,则 t_cid 设置为 1,依此类推。
  • t_ctid保存指向自身或新元组的元组标识符(tid)。tid用于标识表中的元组。当这个元组更新时,这个元组的t_ctid指向新的元组;否则, t_ctid 指向它自己。

插入、删除和更新元组

为了专注于元组,下面没有表示页眉和行指针。

元组的表示

image

插入

通过插入操作,一个新的元组被直接插入到目标表的一页中。

元组插入

image

假设一个 tuple 被一个 txid 为 99 的事务插入到一个页面中。在这种情况下,插入的 tuple 的 header 字段设置如下。

  • 元组_1:
    • t_xmin设置为 99,因为这个元组是由 txid 99 插入的。
    • t_xmax设置为 0,因为该元组尚未被删除或更新。
    • t_cid设置为 0,因为这个元组是 txid 99 插入的第一个元组。
    • t_ctid设置为 (0,1),指向自身,因为这是最新的元组。

删除

在删除操作中,目标元组被逻辑删除。执行 DELETE 命令的 txid 的值设置为元组的 t_xmax。

元组删除

image

假设 Tuple_1 被 txid 111 删除,此时 Tuple_1 的头部字段设置如下。

  • 元组_1:
    • t_xmax设置为 111。

如果提交 txid 111,则不再需要 Tuple_1。通常,不需要的元组在 PostgreSQL 中被称为死元组。

死元组最终应该从页面中删除。清理死元组被称为VACUUM处理。

更新

在更新操作中,PostgreSQL 在逻辑上删除最新的元组并插入一个新元组。

更新该行两次

image

假设已经被 txid 99 插入的行被 txid 100 更新了两次。

当执行第一个 UPDATE 命令时,通过将 txid 100 设置为 t_xmax 逻辑删除Tuple_1,然后插入 Tuple_2。然后,将Tuple_1的 t_ctid 重写为指向 Tuple_2。Tuple_1 和 Tuple_2 的头字段如下。

  • 元组_1:
    • t_xmax设置为 100。
    • t_ctid从 (0, 1) 重写为 (0, 2)。
  • 元组_2:
    • t_xmin设置为 100。
    • t_xmax设置为 0。
    • t_cid设置为 0。
    • t_ctid设置为 (0,2)。

当执行第二个 UPDATE 命令时,与第一个 UPDATE 命令一样,逻辑上删除 Tuple_2 并插入 Tuple_3。Tuple_2 和 Tuple_3 的头字段如下。

  • 元组_2:
    • t_xmax设置为 100。
    • t_ctid从 (0, 2) 重写为 (0, 3)。
  • 元组_3:
    • t_xmin设置为 100。
    • t_xmax设置为 0。
    • t_cid设置为 1。
    • t_ctid设置为 (0,3)。

与删除操作一样,如果提交 txid 100,则 Tuple_1 和 Tuple_2 将是死元组,如果中止 txid 100,则 Tuple_2 和 Tuple_3 将是死元组。

页面检查

PostgreSQL 提供了一个扩展pageinspect,它是一个贡献模块,用于显示数据库页面的内容。

testdb=# CREATE EXTENSION pageinspect;
CREATE EXTENSION
testdb=# CREATE TABLE tbl (data text);
CREATE TABLE
testdb=# INSERT INTO tbl VALUES('A');
INSERT 0 1
testdb=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page('tbl', 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
-------+--------+--------+-------+--------
     1 |     99 |      0 |     0 | (0,1)
(1 row)
postgres=# delete from tbl;
DELETE 1
postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page('tbl', 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
-------+--------+--------+-------+--------
     1 |   4965 |   4966 |     0 | (0,1)
(1 row)

postgres=# truncate table tbl;
TRUNCATE TABLE
postgres=# INSERT INTO tbl VALUES('A');
INSERT 0 1
postgres=# begin;
BEGIN
postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page('tbl', 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
-------+--------+--------+-------+--------
     1 |   4979 |      0 |     0 | (0,1)
(1 row)

postgres=# update tbl set data = 'B';
UPDATE 1
postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page('tbl', 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
-------+--------+--------+-------+--------
     1 |   4979 |   4980 |     0 | (0,2)
     2 |   4980 |      0 |     0 | (0,2)
(2 rows)

postgres=# update tbl set data = 'C';
UPDATE 1
postgres=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page('tbl', 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
-------+--------+--------+-------+--------
     1 |   4979 |   4980 |     0 | (0,2)
     2 |   4980 |   4980 |     0 | (0,3)
     3 |   4980 |      0 |     1 | (0,3)
(3 rows)

标签:autovacuum,txid,cost,xmax,vacuum,进程,元组
From: https://www.cnblogs.com/jl1771/p/17718803.html

相关文章

  • 前后台进程、孤儿进程和 daemon 类进程的父子关系
    回到Shell系列文章大纲前后台进程、孤儿进程和daemon类进程的父子关系前台进程、后台进程和进程父子关系前台进程是占用当前终端的进程,只有该进程执行完成或被终止之后,才会释放终端并将终端交还给shell进程。例如:$sleep30执行该命令后,将创建sleep进程,sleep进程是当前ba......
  • 进程注入之ListPlanting——滥用listview控件的消息回调函数
    效果:注入代码到“注册表编辑器”(当然,必须是要有listview这种列表显示才可以执行)  ProcessInjection: ListPlanting Othersub-techniquesofProcessInjection(12)看看官方的介绍Adversariesmayabuselist-viewcontrolstoinjectmaliciouscodeinto......
  • 25_linux c 多进程
    linuxc多进程什么时候用进程&线程?1、需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。2、线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应。3、多进程可以使用在多机分布式系统,需要扩展......
  • MySQL高级11-后台进程
    一、前言MySQL的服务实现通过后台多个线程、内存池、文件交互来实现对外服务的,不同线程实现不同的资源操作,各个线程相互协助,共同来完成数据库的服务。MySQL常用的后台线程概括如下,分为MasterThread,IOThread,PurgeThread,PageCleanerThread二、MasterThread在MySQL......
  • MSSQL 维护小记(清理进程、重建索引)
    ------------------------------清理进程----------------------------------- declare@deleteSleepSessionnvarchar(100)--申明一个变量declaretablelistcursorlocal--申明一个本地游标forselect'kill'+rtrim(spid)frommaster.dbo.sysprocesses--数据库系统进......
  • 【逆向专题】【危!!!刑】(一)使用c#+Win32Api实现进程注入到wechat
    引言自从上篇使用Flaui实现微信自动化之后,这段时间便一直在瞎研究微信这方面,目前破解了Window微信的本地的Sqlite数据库,使用Openssl,以及Win32Api来获取解密密钥,今天作为第一张,先简单写一下,获取微信的一些静态数据,以及将自己写的c语言dll通过Api注入到微信进程里面去,最后......
  • 目标进程已退出,但未引发 CoreCLR 启动事件
    百度之多数是说运行库没有安装。https://www.cnblogs.com/lingxi-ljl/p/17082020.html运行以下命令都能找到相关版本的内容dotnet--list-sdksdotnet--list-runtimes重新创建相同版本的一个项目,可以正常启动。怀疑是net6依赖记录的json文件有问题,于是改一下项目的名称,使生......
  • Linux 安装守护进程supervisor
    1.使用yum安装yuminstallsupervisor2.开机自启动systemctlenablesupervisord.service3.启动supervisorsystemctlstartsupervisord4.导航至相应目录cd/etc/supervisord.d/5.新建进程文件viApp.ini6.修改进程文件配置如下:[program:App]......
  • 线程劫持-进程注入C++示例和检测思考
    线程劫持:运行方法C:\Users\l00379637\source\repos\thread_hijack\x64\Release\thread_hijack.exe18132C:\Users\l00379637\source\repos\injected_dll\x64\Release\injected_dll.dllProcessID:18132Injected!劫持效果: 劫持代码如下:#include<iostream......
  • 【原创】BGP协议的主要进程
    BGP的主要进程在思科路由器上,我们查看对应的BGP协议使用的进程导致CPU及内存利用率,可以用如下命令进行查看(锐捷路由器上不适用) 分别有Scanner进程、I/O进程、Router进程以及其他Scheduler进程、Event进程和Task这三个进程。前三个进程的主要作用如下:Scanner进程:主要是对BG......