完美无缺的系统是不存在的,或者认为某个系统很完美的你,不完美,今天就的开始talk一下Postgresql 的一个“不可回避的问题”, FREEZEN.
POSTGRESQL FREEZEN 到底是什么,为什么提到这个事情心里总有些惴惴不安,的从POSTGRESQL 的原理开始,POSTGRESQL 中的事务ID 是32位组成的,也就是说系统分配的事务的ID是有限的.
在一个繁忙的系统中,事务的ID被用完怎么办,系统就停止了? POSTGRESQL 通过HALF BY HALF 的方式来解决这个问题,就是一半可用一半不可用,将可用的事务的ID 变成2.1亿.
通过下面粗略的比喻的图, 我们这样讲解,如果我就有12个事务的ID, 那么我不能说我把事务号都分了,然后我的事务分到12,下面就没有号了,我就的想着我需要将这些号分成两个部分, 一部分为不能见人的(不让你用), 一部分是被使用的.
然后我们通过增加的模式,我当前的事务号如果用到了2 ,下一个就是1 ,但是原来7 号可见, 那如果1号可见了,7号就必须要回收,变成不可见.
那么这里就会产生我从数字1 开始使用依次为 1 12 11 10 9 8 , 当我用到 7 的时,我已经达到一半一半的状态了, 此时我就需要回收的TIX 事务号了, 1 12 11 10 9 8 这些数字我的回收了.
听上去回收事务号并不太难,但这些事务的ID 在哪里,在每行tuple 上, 那21亿个事务,并且使用这些事务的行,都要对回收的这个事情进行响应. 每行上都有 t_xmin 和 t_xmax 两个值并且这些值都存储了事务ID
那么此时系统是不是应该处于这个状态.
PostgreSQL 有三个特殊的事务的ID ,
0 是不可见的事务ID
1 是数据库初始化时的事务ID
2 是冻结的事务的ID ,这个ID 比任何的事务的ID 都老,也就是不可见,并且POSTGRESQL 会掌握处于"冻结"的
那么此时有同学会问,虽然2.1一个亿的数字也是蛮大的,但是我还是不想去脑瘫,那我应该怎么办.
POSTGRESQL 这么聪明的数据库当然有办法解决问题,怎么解决, 一个字, "滚". 滚动的滚.
vacuum_freeze_min_age 这个参数将帮助你,解决上面的问题, 默认是5000万,当你的事务的ID 使用的数量达到5000万的时候, 那么frozen的动作就开始了,
大白话就是我如果从7开始使用 我不会说用到2的时候,在进行FROZEN,而是我在用到4的时候我就将6 ,7 就变成不可见,将 1 ,12 变成可见.
那么接下来的问题是,他怎么进行冻结的活动,也就是 frozen processing, forzen 的方式有两种, 我们可以叫他们 lazy 和 eager 两种模式,这两种模式中的区别是,前者lazy模式中,是通过vacuum process的方式来处理,扫描dead tuple 来进行冻结的活动,而采用eager的方式将扫描所有的PAGE来进行FROZEN的活动, 并且更新相关系统的VIEW,和清理不在使用的CLOG文件。
怎么计算那些事务的ID 是可以被回收的
可以回收的TXID = 当前最老的xmin - vacuum_freeze_min_age,普通的vacuum进程逐个扫描页面,同时与vm可见性映射协作跳过没有死元组的页面,并将元组t_infomask (xmin小于freezelimit_txid)设置为XMIN_FROZEN。清理完成后,n_live_tuple、n_dead_tuple、vacuum_count、autovacuum_count、last_autovacuum、last_vacuum等统计信息将被更新。
对于表来说,通过PG_CLASS 中的 relfrozenxid来鉴别表中的最小的xmin id ,对于数据库也是一样通过pg_database 的字段datfrozenxid 来判别。
触发数据库的急冻datfrozenxid < oldestxmin-vacuum_freeze_table_age,
怎么监控
代码语言:sql复制WITH max_age AS (
SELECT 2000000000 as max_old_txid
, setting AS autovacuum_freeze_max_age
FROM pg_catalog.pg_settings
WHERE name = 'autovacuum_freeze_max_age' ),
per_database_stats AS (
SELECT datname ,
m.max_old_txid::int , m.autovacuum_freeze_max_age::int ,
age(d.datfrozenxid) AS oldest_current_txid
FROM pg_catalog.pg_database d
JOIN max_age m ON (true)
WHERE d.datallowconn )
SELECT max(oldest_current_txid) AS oldest_current_txid ,
max(ROUND(100*(oldest_current_txid/max_old_txid::float))) ASconsumed_txid_pct ,
max(ROUND(100*(oldest_current_txid/autovacuum_freeze_max_age::float)))AS consumed_autovac_max_age
FROM per_database_stats;
oldest_current_txid 表示为当前所有数据库中最老的事务ID,
asconsumed_trix_pct 为当前消耗了多少事务ID 占据的百分比,数字越大,就说明距离危险越近了。
consumed_autovac_max_age 参数说明你距离触发FREAZON的距离还有多远
那如果发现了上面的情况不大对头的情况下,怎么办
代码语言:sql复制SELECT datname, age(datfrozenxid) AS frozen_xid_age , ROUND(100*(age(datfrozenxid)/2000000000.0::float)) consumed_txid_pct , current_setting('autovacuum_freeze_max_age')::int - age(datfrozenxid) AS remaining_aggressive_vacuum FROM pg_database;
赶紧针对数据库去查看那个数据库要出事. 最后去定位那个表要出事,然后下一步就是解决他
实际上POSTGRESQLVACUUM 以及 AUTOVUCUUM 和事务ID内卷化的参数调整,还需要继续一段文字的描述, 不过首先我们先认识他,以及他的危害,并通过手段来监控,是第一步.