前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >POSTGRESQL 差点发生一次冻结炸弹后的反思

POSTGRESQL 差点发生一次冻结炸弹后的反思

作者头像
AustinDatabases
发布2022-07-13 15:01:56
7080
发布2022-07-13 15:01:56
举报
文章被收录于专栏:AustinDatabases

POSTGRESQL 的冻结炸弹💣,大多是只听说过,没有遇到过,实际上想遇到冻结炸弹也是不容易。最近差点发生一次冻结炸弹,惊险之余的总结一下怎么不在差点发生这个问题。

关于冻结炸弹的原理,文章很多,这里不进行赘述,先说说事情发生的情况,收到报警,发现数据库最大age xid 的告警,随即通过pg_stat_activity查询发现有大量的查询语句运行时间过长,并且一直未停止,随即进行查杀,将这些语句查杀后,报警停止。

随即开始反思到底有哪里没有做到位

在梳理之前我们需要在简单的重复一下,PG 这部分的原理。

1 PG MVCC 寄生与行中,行中有每个行属于哪个事务的标记,以及行在后期经过delete 或 update 后的事务标记, xmin, xmax 字段。事务ID 本身是 2的 31次方,大约可以使用的事务ID 是 42亿, 而事务ID 是有数据量的,不能一直使用下去,所以必须进行回收,回收与AUTVACUUM 和 VACUUM 有关。

其中通过参数vaccum_freeze_min_age 来设置回收的工作点,凡是死行,同时死行中的事务ID对于 vacuum_freeze_min_age 设置的参数对比后老于这个值,则这个事务ID 进入了 FrozenXID.

另一个问题是什么时候触发这个工作的进行,也就是将老的XID 进行 Frozenxid 的操作,我们还有一个参数autovacuum_freeze_max_age

触发操作 举例如果我们的 autovacuum_freeze_max_age 设置的为2亿

而我们的vacuum_freeze_min_age 为 5000万,那么每次当两个值相减后与当前的事务ID 比较超过 1.5亿就要开始进行 Frozen 的工作了。

实际上对于动态表,经常进行Autovacuum 这个事情并不重要,而对于静态表来说,这才是重要的问题,基于autovacuum永远不会扫到这样的表。

1 从参数看,是否有改进的地方

1.1 vacuum_freeze_min_age

这个 vacuum_freeze_min_age 的功能可以通过一个图来进行表述,整体我们有42亿的txid, 我们有一半的TXID 是被隐藏的,一半是使用的,在这样的状态下,我们需要不断的回收 TXID,在这样的情况下我们不断回收的标准是什么,这是一个问题。每张表都需要回收TXID ,那么距离上次回收多长时间会进行下一次的TXID 的回收。如图,一个圆里面的开口的那段就是这一次和上一次之间的距离,而 vacuum_freeze_min_age 就是开口的可以承受的最大的距离。

提高这个值,保证预留的TXID 会更多,避免发生FREEZMING BOOM 的风险。

同时用FROZENXID 替换记录中的xmin

#vacuum_freeze_min_age = 50000000 默认值为 5000万,这个值设置的越小,针对I/O 和 CPU的影响会越大,但对于系统来说更积极的回收有利于尽快回收事务ID

2 老生长谈的 autovacuum 触发机制的参数,说这方面的文字很多,这里不赘述简单的几条来概括

2.1 必须对系统中的大表有认识,并且针对大表的autovacuum 的每个参数进行特殊的设定,必须及时的进行 autovacuum 的操作,而不是使用系统默认的设置。

3 在系统不繁忙的情况下,一定要定期的针对系统进行VACUUM 的手动操作,尤其针对分析出的系统中的大表进行操作。

4 针对超级大表,可以进行分区的操作,这样有利于autovacuum 的运行和效率。

5 针对大表的DDL 操作对于AUTOVACUUM 的影响的问题,这点也是需要重视的,DDL 操作会导致autvacuum 操作被推迟,所以针对大表的DDL 尽量在业务不繁忙时间进行,并且在针对某些大表操作后,需要尽快进行VACUUM 的操作。

6 积极发现系统中存在的长时间无法工作完的语句,举例发现长达5分钟还未完毕的语句,针对超过10分钟的没有完成的语句进行记录和展示

SELECT datname, pid, client_addr,

now() - xact_start AS duration,

now() - state_change AS time_idle,

query

FROM pg_stat_activity

WHERE now() - state_change > INTERVAL '10 minutes'

AND now() - xact_start > INTERVAL '10 minutes';

7 检测库中的 frozen_xid_age ,以及consumed_txid_pct 的消耗的情况,和将要发生 aggressive_vacuum的 情况等

SELECT

datname,

age(datfrozenxid) AS frozen_xid_age,

ROUND(

100 *(

age(datfrozenxid)/ 2146483647.0 :: float

)

) consumed_txid_pct,

current_setting('autovacuum_freeze_max_age'):: int - age(datfrozenxid) AS remaining_aggressive_vacuum

FROM

pg_database

WHERE

datname NOT IN (

'cloudsqladmin', 'template0', 'template1'

);

同时也可以监控当前那些表正在进行autovacuum

代码语言:javascript
复制
SELECT p.pid,
      p.datname,
      p.query,
      p.backend_type,
      a.phase,
      a.heap_blks_scanned / a.heap_blks_total::float * 100 AS "% scanned",
      a.heap_blks_vacuumed / a.heap_blks_total::float * 100 AS "% vacuumed",
      pg_size_pretty(pg_table_size(a.relid)) AS "table size",
pg_size_pretty(pg_indexes_size(a.relid)) AS "indexes size",
      pg_get_userbyid(c.relowner) AS owner
 FROM pg_stat_activity p
 JOIN pg_stat_progress_vacuum a ON a.pid = p.pid
 JOIN pg_class c ON c.oid = a.relid
WHERE p.query LIKE 'autovacuum%';

最后简单总结一下,POSTGRESQL 冻结炸弹如果发生是一件很可怕的事情,实际上如果对运行的SQL 进行时间的检测和控制,尤其不要在一个系统中进行大量,频繁的OLAP 操作,POSTGRESQL 本身也不容易发生冻结炸弹。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AustinDatabases 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档