本次测试数据库版本为MogDB/openGauss3.0,测试undo相关的参数会怎样影响undo目录中旧版本数据自动回收的能力,强制回收会不会丢数据。
undo目录结构图
数据目录中的undo目录下有三个目录和一个undometa文件,undometa是undo的元数据文件,permanent是存储undo的持久化数据的,这部分数据丢失,会导致ustore表数据的不一致。因此$PGDATA/undo目录不能随便删除。
测试undo参数
-
undo_retention_time
参数说明: 设置undo旧版本保留时间,默认不保留旧版本。 -
undo_space_limit_size
参数说明: 用于控制undo强制回收阈值,达到阈值的80%启动强制回收。 -
undo_limit_size_per_transaction
参数说明: 用于控制单事务undo分配空间阈值,达到阈值时事务报错回滚。
undo_retention_time的测试
- undo_retention_time值为0,undo最大空间800MB,对ustore表做增删改操作,同时undo统计信息
select gs_stat_undo();
通过undo系统函数 gs_stat_undo() 可以看到在ustore表数据修改时undo目录就一直在增大,事务提交,执行成功就不再增大。大概过了5秒 undo目录里的旧数据就被清理,又回到原先值。
查看undo目录大小
查看这时间段表的CSN是不存在的
SELECT * FROM gs_txn_snapshot where snptime between '2022-08-16 09:00:00' and '2022-08-16 10:00:00';
snptime | snpxmin | snpcsn | snpsnapshot
---------+---------+--------+-------------
(0 rows)
undo_retention_time参数起到了及时清理旧版本数据的作用,设置为0s undo目录清理旧版本数据的特别快,约等于不存储旧版本数据,空间上节省了不少。但是旧版本数据删除了,基于MVCC的闪回表和闪回查询就无法做到,所以这样设置不利于基于MVCC的闪回恢复。
- undo_retention_time值为60s,undo最大空间800MB,对ustore表做DML
MogDB=# create table t1 (id int , name text);
CREATE TABLE
MogDB=# insert into t1 values (generate_series(1,1000000),md5(random()::text));
INSERT 0 1000000
MogDB=# select gs_stat_undo();
gs_stat_undo
-------------------------------------------------------------
(4,"1 : 40, 0 : 0, 0 : 0",45,640,42576,42618,0,0,3053,3004)
(1 row)
-- 隔半分钟再执行
MogDB=# insert into t1 values (generate_series(1,1000000),md5(random()::text));
INSERT 0 1000000
MogDB=# select gs_stat_undo();
gs_stat_undo
-------------------------------------------------------------
(4,"1 : 80, 0 : 0, 0 : 0",85,640,42594,42638,0,0,3093,3004)
(1 row)
-- 第一次插入后的一分钟空间被回收了
MogDB=# select gs_stat_undo();
gs_stat_undo
-------------------------------------------------------------
(4,"1 : 40, 0 : 0, 0 : 0",44,640,42621,42662,0,0,3093,3045)
(1 row)
-- 再过半分钟第二次insert的空间又被回收
MogDB=# select gs_stat_undo();
gs_stat_undo
-----------------------------------------------------------
(4,"0 : 0, 0 : 0, 0 : 0",4,640,42652,42690,0,0,3093,3085)
(1 row)
上面测试可以看出,多久提交是按照事务提交的时间计算,待够了60s的事务就会释放它的旧版本数据。时间短也不利于做基于MVCC的闪回表闪回查询。
- undo_retention_time值为30000s,undo最大空间800MB,对ustore表做DML
MogDB=# insert into t1 values (generate_series(1,2000000),md5(random()::text));
INSERT 0 2000000
MogDB=# insert into t1 values (generate_series(1,2000000),md5(random()::text));
INSERT 0 2000000
MogDB=# insert into t1 values (generate_series(1,2000000),md5(random()::text));
INSERT 0 2000000
MogDB=# delete t1 where id < 1000000;
DELETE 2999997
MogDB=# select gs_stat_undo();
gs_stat_undo
-----------------------------------------------------------
(4,"0 : 0, 0 : 0, 0 : 0",4,640,42920,44007,0,0,3093,3085)
(1 row)
MogDB=# select gs_stat_undo();
gs_stat_undo
---------------------------------------------------------------
(4,"1 : 224, 0 : 0, 0 : 0",228,640,42920,44365,0,0,3317,3085)
(1 row)
MogDB=# select gs_stat_undo();
gs_stat_undo
---------------------------------------------------------------
(4,"1 : 454, 0 : 0, 0 : 0",458,640,42920,44397,0,0,3547,3085)
(1 row)
MogDB=# select gs_stat_undo();
gs_stat_undo
---------------------------------------------------------------
(4,"1 : 592, 0 : 0, 0 : 0",596,640,42920,44397,0,0,3685,3085)
(1 row)
MogDB=# select gs_stat_undo();
gs_stat_undo
---------------------------------------------------------------
(4,"1 : 397, 0 : 0, 0 : 0",401,640,42920,44397,0,0,3691,3286)
(1 row)
因为回收时间还比较久,undo目录为了保证旧版本的还能写入,设置了最大目录的80%作为阈值,可以看到在接近640MB的时候undo空间有自动清理机制,保证事务还能写入undo目录。
undo_limit_size_per_transaction测试1
- 设置undo_limit_size_per_transaction为30M,事务大于这个值会是什么结果。
MogDB=# select COUNT(*) from t1 ;
count
---------
3800001
(1 row)
MogDB=# insert into t1 values (generate_series(1,2000000),md5(random()::text));
ERROR: [RollbackIfUndoExceeds:71]xid 47550, the undo size 31457286 of the transaction exceeds the threshold 31457280.
MogDB=# select COUNT(*) from t1 ;
count
---------
3800001
(1 row)
MogDB=# update t1 set id = 10;
ERROR: [RollbackIfUndoExceeds:71]xid 47620, the undo size 31457316 of the transaction exceeds the threshold 31457280.
MogDB=# select COUNT(*) from t1 where id = 10;
count
-------
4
(1 row)
MogDB=# delete t1 ;
ERROR: [RollbackIfUndoExceeds:71]xid 47677, the undo size 31457376 of the transaction exceeds the threshold 31457280.
MogDB=# select COUNT(*) from t1;
count
---------
3800001
(1 row)
单事务最大阈值的设置,可以让超过值的事务回滚,不影响到原来的数据。
undo_limit_size_per_transaction测试2
undo_space_limit_size是undo目录可使用的最大值,我们把undo_limit_size_per_transaction设置比undo最大可用空间都大进行测试。那么undo_limit_size_per_transaction=800MB,undo_limit_size_per_transaction=2G,执行一条超过800MB的事务。
有的时候undo目录会写到640MB开始清零,然后继续进行写入,事务没有被中断,也没有影响表更新的数据,有的时候会如下,到最大undo目录大小值还是没有清理。
虽然undo空间满了,但不影响其他事务的进行,其他事务会触发undo目录全部清理。
报错表进行查询报如下错,数据已经丢失。
openGauss=# select count(*) from t1;
ERROR: snapshot too old! the undo record has been force discard
undo_limit_size_per_transaction的值一定比undo_space_limit_size要小,在openGauss官方的默认配置里undo_limit_size_per_transaction比undo_space_limit_size更小。