暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

openGauss/MogDB的hot_standby_feedback参数研究

原创 由迪 2023-10-30
469

原作者:何放

关于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会继续推进。粗体

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论

目录
  • 关于hot_standby_feedback参数信息
  • 得出猜想
  • 验证猜想 xmin如何推进的
  • 验证猜测 hot_standby_feedback能解决的查询冲突
  • 验证猜测 开启参数在什么情况下会影响主库的xmin不推进
  • 测试hot_standby_feedback结果