相信用过oracle的用户,对这个错误并不陌生,很早之前写过关于oracle的相关文章。
今天介绍一下,pg同样也一定的条件下也会报相关错误。
pg中是通过vacuum回收脏数据,也就是我们常说的dead tuple。但是有时候长事物会持有一些dead tuple很长时间,这样就不能及时做vacuum回收脏数据。这样就使得数据库中的表很容易膨胀,并占用额外的存储空间。从而还会导致相关SQL操作性能下降。所以为了避免以上结果,从9.6引入了old_snapshot_threshold参数。
当old_snapshot_threshold默认值为-1,是关闭的。当我们设置该值为具体时间的时候,vacuum 就不会等待长时间的查询结束,会继续对dead tuple进行处理,如果刚好有事物正好在使用这些dead tuple,那么就会报错snapshot too old。
PostgreSQL14版本中添加一个插件,可以在old_snapshot_threshold关闭的时候(设置为-1),查看映射的XID。
举个例子:
postgres=# select version();
version
—————————————————————————–
PostgreSQL 14devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5), 64-bit
(1 row)
postgres=# create extension old_snapshot;
CREATE EXTENSION
#设置old_snapshot_threshold为一分钟
old_snapshot_threshold = 1min # 1min-60d; -1 disables; 0 is immediate
#生成一些脏数据
postgres=# update tbl_test set b=’hank′;
UPDATE 1000000
#使用pg_sleep函数
select *, pg_sleep(50) from tbl_test;
#在另一个session中一分钟内做vacuum
vacuum verbose tbl_test;
#当我们以上的查询还在进行的时候,时间到达old_snapshot_threshold的时候,就会收到一个报错ERROR: snapshot too old,日志里面也会记录
2021-05-30 15:26:19.302 UTC [2055] ERROR: snapshot too old
那么在14版本中,我们提供了一个插件old_snapshot,可以查看old_snapshot_threshold的状态。
如下语句可查看:
postgres=# select * from pg_old_snapshot_time_mapping();
array_offset | end_timestamp | newest_xmin
--------------+------------------------+-------------
0 | 2020-11-30 14:03:00+00 | 533
1 | 2020-11-30 14:04:00+00 | 533
2 | 2020-11-30 14:05:00+00 | 534
3 | 2020-11-30 14:06:00+00 | 534
4 | 2020-11-30 14:07:00+00 | 534
5 | 2020-11-30 14:08:00+00 | 534
6 | 2020-11-30 14:09:00+00 | 534
7 | 2020-11-30 14:10:00+00 | 534
8 | 2020-11-30 14:11:00+00 | 534
9 | 2020-11-30 14:12:00+00 | 534
10 | 2020-11-30 14:13:00+00 | 534
#如下示例,可以获取更详细的内容
postgres=# select *, age(newest_xmin), clock_timestamp() from
postgres-# pg_old_snapshot_time_mapping();
array_offset | end_timestamp | newest_xmin | age | clock_timestamp
--------------+------------------------+-------------+-----+-------------------------------
4 | 2020-11-30 14:18:00+00 | 542 | 0 | 2020-11-30 14:27:20.198123+00
5 | 2020-11-30 14:19:00+00 | 542 | 0 | 2020-11-30 14:27:20.198134+00
6 | 2020-11-30 14:20:00+00 | 542 | 0 | 2020-11-30 14:27:20.198139+00
7 | 2020-11-30 14:21:00+00 | 542 | 0 | 2020-11-30 14:27:20.19814+00
8 | 2020-11-30 14:22:00+00 | 542 | 0 | 2020-11-30 14:27:20.198141+00
9 | 2020-11-30 14:23:00+00 | 542 | 0 | 2020-11-30 14:27:20.198141+00
10 | 2020-11-30 14:24:00+00 | 542 | 0 | 2020-11-30 14:27:20.198142+00
0 | 2020-11-30 14:25:00+00 | 542 | 0 | 2020-11-30 14:27:20.198143+00
1 | 2020-11-30 14:26:00+00 | 542 | 0 | 2020-11-30 14:27:20.198144+00
2 | 2020-11-30 14:27:00+00 | 542 | 0 | 2020-11-30 14:27:20.198145+00
3 | 2020-11-30 14:28:00+00 | 542 | 0 | 2020-11-30 14:27:20.198146+00
(11 rows)
参考:
https://www.postgresql.org/docs/14/oldsnapshot.html
https://postgreshelp.com/postgresql-14-snapshot-too-old/