openGauss/MogDB的hot_standby_feedback参数研究

2023年 10月 30日 53.1k 0

原作者:何放

关于hot_standby_feedback参数信息

  1. 我对hot_standby_feedback参数的认知是源自文章的参考,《postgresql 查询冲突》,这篇文章可以让我们了解到什么是查询冲突,我们细想一下,比如说备库正在执行基于某个表的查询(这个查询可能是应用产生的,也可能是手动连接进行的查询),这时主库执行了drop table操作,该操作写入wal日志后传至备库进行应用,为了保证数据一致性,postgresql必然会迅速回放数据,这时drop table和select就会形成冲突。为了能避免一部分查询冲突,引入了一个参数hot_standby_feedback,这个参数是查询冲突这个话题中提到最多的参数,下面我们详细探讨一下。我们假设在没有备库的情况下,会话1查询某行数据,会话2删除该数据,然后commit,此时会话2执行一次vacuum,我们知道这次vacuum并不会删除该行数据,因为会话1的事务还需要使用该元组,所以不会清理该元组。那么如果是主从呢?主库在准备进行vacuum时怎么知道从库还在进行查询,这就是设置该参数的意义,设置hot_standby_feedback参数之后备库会定期向主库通知最小活跃事务id(xmin)值,这样使得主库vacuum进程不会清理大于xmin值的事务。这个参数有利于减少冲突的发生,但并不能完全避免冲突,其实细想一下,这个参数只是减少了由于主库vacuum死亡元组造成的冲突,并不能解决排他锁造成的冲突。或者由于网络中断造成的冲突,假如主备之间的网络中断后,备库就无法向主库正常发送xmin值,如果时间够长,主库在这段时间内还是会清理无用元组,这样网络恢复后就可能发生上面的vacuum造成的冲突。
  2. 然后再查询openGauss官网参数介绍

hot_standby_feedback参数说明: 设置是否允许将备机上执行查询的结果反馈给主机,这可以避免查询冲突。

  • on表示允许将备机上执行查询的最小事务号反馈给主机。
  • off表示不允许将备机上执行查询的最小事务号反馈给主机。
  • 通过查找资料,hot_standby_feedback参数开启后,xmin可以从主库的pg_replication_slots表的xmin字段得到备库反馈给主库的xmin,可以更直观的看到这个数值是如何推进的。

得出猜想

从以上的信息中得出,可以猜想在开启hot_standby_feedback参数后的会有哪些情况发生。

  1. xmin是如何才会推进?是备库一有查询就推进,还是由事务推进的。
  2. hot_standby_feedback能解决哪些查询冲突?
  3. 由于openGauss/MogDB没有old_snapshot_threshold参数来强行删除旧版本的数据,只要xmin长时间不推进的话主库就会发生膨胀。

验证猜想 xmin如何推进的

--主库查询当前的xmin
MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 17288 | | 0/40106C8 | f
(2 rows)
--备库执行查询
MogDB=# begin ;
BEGIN
MogDB=# select * from t;
id
----
(0 rows)

MogDB=# select * from t;
id
----
(0 rows)

MogDB=# end;
COMMIT
--主库再查询一次xmin
MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 17288 | | 0/4010908 | f
(2 rows)

结论:如果备库做查询,并不会有xmin返回给主库。

--主库进行事务操作
MogDB=# create table t1(id int );
CREATE TABLE
MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 17643 | | 0/4014DB8 | f
(2 rows)

MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 17998 | | 0/4015198 | f
(2 rows)

MogDB=# select txid_current();
txid_current
--------------
18354
(1 row)

MogDB=# select txid_current();
txid_current
--------------
18355
(1 row)

MogDB=# select txid_current();
txid_current
--------------
18356
(1 row)

MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 18354 | | 0/4015518 | f
(2 rows)

MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 18356 | | 0/4015518 | f
(2 rows)

MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 18356 | | 0/4015518 | f
(2 rows)

结论:主库执行事务之后,会延迟一会儿收到备库传来的xmin,这个xmin更像是备库进行了事务回放之后返回的值,如果主库没有事务id推进,备库也不会有事务id同步返回

验证猜测 hot_standby_feedback能解决的查询冲突

首先不开启参数的时候,会产生的查询冲突

  1. 备库在查询的表,主库把这张表给drop会发生查询冲突。
  2. 备库在查询count(*)表时,主库把这张表的数据delete会发生查询冲突。
  3. 备库在查询的表,主库把表进行了锁表操作会发生查询冲突。
  4. 备库正在查询的数据,主库把这行数据update之后又vacuum了会发生查询冲突。
  5. 备库查询正在使用的表空间,被主库drop了会发生查询冲突。
  6. 备库查询正在使用的数据库,被主库drop了会发生查询冲突。

结论:hot_standby_feedback参数无法解决ddl引起的查询冲突,但是主库对数据进行的是删除类型操作(delete,update)就能够避免查询冲突。

验证猜测 开启参数在什么情况下会影响主库的xmin不推进

结论:备库模拟执行长查询,主库模拟进行了update或delete xid是在推进的,此时不管主库xid推进到多少备库都不会返回xmin给主库,当备库的长查询结束后,就会立刻返回给主库xmin。根据xmin推进测试,长查询的过程中备库不会发送主库xmin,主库也就不会清理xmin之后的dead tuple,如果对于过长的查询性能肯定是有影响的。

--备库执行
MogDB=# select pg_sleep(60);

--主库执行
MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 18356 | | 0/4019828 | f
(2 rows)

MogDB=# select txid_current();
txid_current
--------------
18357
(1 row)

MogDB=# select txid_current();
txid_current
--------------
18358
(1 row)

MogDB=# select txid_current();
txid_current
--------------
18359
(1 row)

MogDB=# select txid_current();
txid_current
--------------
18360
(1 row)

MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 18356 | | 0/4019C88 | f
(2 rows)
---一分钟后
MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | t | 18359 | | 0/401A5A0 | f
(2 rows)

所以这里要研究一下,什么会影响这个xmin不推进

  1. 备库查询如果abort了会不会影响xmin不再推进了。
  2. 由于在单机环境中,如果有一个会话长期持有xid,在这个xid之后产生的dead tuple是不能被清理的,所以如果备库长期持有xid会不会影响xmin不推进。
  3. 备库在做查询的时候down了,无法返回给主库xmin了,这个xmin会不会一直不推进。

测试过程一

简写:备库执行select pg_sleep(60); 查询gsql进程 ps -ef|grep gsql; kill掉gsql进程 kill -9 ; 主库查询xmin值,再执行select txid_current(); 再查询xmin的值,会发现xmin会继续推进。

结论:所以备库查询abort不会影响xmin的推进。

测试过程二

简写:备库执行begin; select * from t; select * from t1; 主库查询xmin值,再执行select txid_current(); 再查询xmin的值,会发现xmin会继续推进。

结论:所以备库长期持有xid不会影响xmin的推进。

测试过程三

---备库执行
MogDB=# select pg_sleep(60);

[omm@node2 ~]$ ps -ef|grep mogdb
omm 2638 1 2 14:19 ? 00:03:37 /opt/software/mogdb/bin/mogdb -D /dbdata/data/db1 -M standby
omm 12984 11935 0 16:30 pts/1 00:00:00 grep --color=auto mogdb
[omm@node2 ~]$ kill -9 2638
---主库执行
MogDB=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | f | 18371 | | 0/401CA38 | f
(2 rows)

MogDB=# update t set id = generate_series(1001,2000);
UPDATE 1000
MogDB=# update t set id = generate_series(2001,3000);
UPDATE 1000
MogDB=# \x
Expanded display is on.
MogDB=# select * from pg_stat_user_tables where relname = 't';
-[ RECORD 1 ]-----+------------------------------
relid | 16394
schemaname | public
relname | t
seq_scan | 2
seq_tup_read | 2000
idx_scan |
idx_tup_fetch |
n_tup_ins | 1000
n_tup_upd | 2000
n_tup_del | 0
n_tup_hot_upd | 0
n_live_tup | 1000
n_dead_tup | 2000
last_vacuum |
last_autovacuum |
last_analyze |
last_autoanalyze |
vacuum_count | 0
autovacuum_count | 0
analyze_count | 0
autoanalyze_count | 0
last_data_changed | 2023-03-22 16:36:32.055455+08

MogDB=# vacuum VERBOSE t;
INFO: vacuuming "public.t"(primary pid=2216)
INFO: "t": found 0 removable, 3000 nonremovable row versions in 14 out of 14 pages(primary pid=2216)
DETAIL: 2000 dead row versions cannot be removed yet. There were 0 unused item pointers. 0 pages are entirely empty. CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
MogDB=# select * from pg_stat_user_tables where relname = 't';
-[ RECORD 1 ]-----+------------------------------
relid | 16394
schemaname | public
relname | t
seq_scan | 2
seq_tup_read | 2000
idx_scan |
idx_tup_fetch |
n_tup_ins | 1000
n_tup_upd | 2000
n_tup_del | 0
n_tup_hot_upd | 0
n_live_tup | 1000
n_dead_tup | 2000
last_vacuum | 2023-03-22 16:37:26.322396+08
last_autovacuum |
last_analyze |
last_autoanalyze |
vacuum_count | 5
autovacuum_count | 0
analyze_count | 0
autoanalyze_count | 0
last_data_changed | 2023-03-22 16:36:32.055455+08

MogDB=# \x
Expanded display is off.
MogDB=# select * from pg_replication_slots ;
slot_name | plugin | slot_type | datoid | database | active | xmin | catalog_xmin | restart_lsn | dummy_standby
-------------------------------+--------+-----------+--------+----------+--------+-------+--------------+-------------+---------------
standby_192.168.134.134_26001 | | physical | 0 | | f | | | 0/4001F08 | f
primary_192.168.134.134_26001 | | physical | 0 | | f | 18371 | | 0/401CA38 | f
(2 rows)

结论:如果备库down了之后,会影响xmin不推进,并且在xid之后产生的dead tuple不会被清理。

测试hot_standby_feedback结果

在性能上发现hot_standby_feedback开启在openGauss/MogDB中并不完美,如果一主一备的情况备库down的时候没有别及时发现,主库会性能影响,不像pg中有old_snapshot_threshold参数来强制删除超时的脏数据。另外hot_standby_feedback参数是随主库状态改变,一主一备的情况下,备库重新加入集群后xmin会继续推进。粗体

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论