最近在测试openGauss主从复制时发现一个问题:当备机落后主机很多时(比如停了一段时间后再启动),启动后会自动的追数,追数的过程状态是catchup,而在catchup的过程中,主库上的写入会全部阻塞,当然经过进一步验证,如果存在其他正常的备库(状态是normal),那么其中一个备库catchup不会阻塞主库。
下面我们来复现一下这个问题,由于openGauss主从搭建会自动创建物理复制槽,所以备库需要的xlog主库不会自动清理,那么我们就可以先把两个备库都停掉(我这里的环境是一主两备),然后在主库插入大量数据,产生大量xlog的堆积,然后过一段时间再挨个开启备库,查看追数过程中tps的影响。
停止两个备库
[omm@db02 ~]$ gs_ctl stop
[2020-09-16 14:03:56.724][89873][][gs_ctl]: gs_ctl stopped ,datadir is (null)
waiting for server to shut down.... done
server stopped
[omm@db03 ~]$ gs_ctl stop
[2020-09-16 14:04:03.525][60271][][gs_ctl]: gs_ctl stopped ,datadir is (null)
waiting for server to shut down.... done
server stopped
使用压测工具对主库进行并发插入,并观察xlog个数
可以看到xlog在逐步堆积
199
199
199
203
210
217
224
231
238
245
253
260
267
274
tps比较稳定
14:04:58 22254
14:04:59 23582
14:05:00 23937
14:05:01 23316
14:05:02 22994
14:05:03 23452
14:05:04 23920
14:05:05 24097
14:05:06 23886
14:05:07 21578
14:05:08 22731
14:05:09 23315
14:05:10 24749
此时启动第一个备库,观察状态
[omm@db02 ~]$ gs_ctl start -M standby
[omm@db02 ~]$ gs_ctl query
[2020-09-16 14:07:48.392][108727][][gs_ctl]: gs_ctl query ,datadir is (null)
HA state:
local_role : Standby
static_connections : 2
db_state : Catchup
detail_information : Normal
Senders info:
No information
Receiver info:
receiver_pid : 108470
local_role : Standby
peer_role : Primary
peer_state : Normal
state : Catchup
sender_sent_location : 2B/B6800000
sender_write_location : 2D/A2F5B7D8
sender_flush_location : 2D/A2F5B7D8
sender_replay_location : 2D/A2F5B7D8
receiver_received_location : 2B/B6800000
receiver_write_location : 2B/B6000000
receiver_flush_location : 2B/B6000000
receiver_replay_location : 2B/B4C1E548
sync_percent : 95%
channel : 192.168.1.2:38782<--192.168.1.1:5533
tps如下:
14:06:14 19037
14:06:15 18776
14:06:16 19302
14:06:17 11734
14:06:18 0
14:06:19 0
14:06:20 0
14:06:21 0
14:06:22 0
14:06:23 0
14:06:24 0
14:06:25 0
14:06:26 0
14:06:27 0
14:06:28 0
14:06:29 0
14:06:30 0
14:06:31 0
14:06:32 0
14:06:33 0
14:06:34 0
14:06:35 0
14:06:36 0
14:06:37 0
14:06:38 0
14:06:39 0
14:06:40 0
14:06:41 0
14:06:42 0
14:06:43 0
14:06:44 0
14:06:45 0
14:06:46 0
14:06:47 0
14:06:48 0
14:06:49 0
14:06:50 7753
14:06:51 12308
14:06:52 10988
14:06:53 12337
直到catchup状态变为normal时tps才恢复正常
启动第二个备库:
[omm@db03 ~]$ gs_ctl start -M standby
观察状态,虽然sync_percent没有完全同步完,状态是catchup,(有时候也有可能是normal,可能和主备启动时间间隔有关),因为此时已经有一个正常同步的备库,这时第二个备库在追数过程中不会影响主库。
[omm@db03 ~]$ gs_ctl query
[2020-09-16 14:09:19.995][74488][][gs_ctl]: gs_ctl query ,datadir is (null)
HA state:
local_role : Standby
static_connections : 2
db_state : Normal
detail_information : Normal
Senders info:
No information
Receiver info:
receiver_pid : 73921
local_role : Standby
peer_role : Primary
peer_state : Normal
state : Catchup
sender_sent_location : 2C/3F800000
sender_write_location : 2D/D5F901D8
sender_flush_location : 2D/D5F901D8
sender_replay_location : 2D/D5F901D8
receiver_received_location : 2C/3F800000
receiver_write_location : 2C/3D000000
receiver_flush_location : 2C/3D000000
receiver_replay_location : 2C/2B070088
sync_percent : 96%
channel : 192.168.1.3:30200<--192.168.1.1:5533
tps一直比较稳定
14:07:14 12551
14:07:15 11853
14:07:16 12530
14:07:17 12432
14:07:18 12680
14:07:19 12036
14:07:20 11758
14:07:21 11242
14:07:22 11206
14:07:23 11607
14:07:24 11926
14:07:25 11494
14:07:26 11804
14:07:27 12842
14:07:28 12613
14:07:29 12188
14:07:30 13310
14:07:31 15119
14:07:32 15165
14:07:33 14045
为了找到catchup过程中阻塞主机的根本原因,看了相关代码。
在openGauss-server/src/bin/pg_ctl/pg_ctl.cpp中有如下代码:
if (beforeStat.st_mtim.tv_sec != afterStat.st_mtim.tv_sec ||
beforeStat.st_mtim.tv_nsec != afterStat.st_mtim.tv_nsec) {
nRet = memset_s(&state, sizeof(state), 0, sizeof(state));
securec_check_c(nRet, "\0", "\0");
ReadDBStateFile(&state);
switch (state.state) {
case NORMAL_STATE:
case NEEDREPAIR_STATE:
case WAITING_STATE:
case DEMOTING_STATE:
case PROMOTING_STATE:
case BUILDING_STATE:
case CATCHUP_STATE:
return PQPING_OK;
case COREDUMP_STATE:
pg_log(PG_WARNING, _(" gaussDB state is %s\n"), get_string_by_state(state.state));
return PQPING_NO_RESPONSE;
case STARTING_STATE:
case UNKNOWN_STATE:
default:
/* nothing to do */
break;
}
可以看到,如果数据库状态是catchup,那么代表PGPING_OK,也就是代表备机正常,那么主库收到该备机正常的信号后会去向备机同步,但是备机同步该条变更的前提是之前的xlog已经接收完成,但是当前还是catchup状态,依旧在发送日志,所以此时主库的变更都会陷入等待。PINGOK只能代表主备的连通性正常,不代表备机可以立刻提供服务,所以catchup这段时间不能认为该备机是一个正常的备机,除非当时有其他normal状态的备机。
那么如果第一个备机已经完成catchup,第二个备机再启动然后catchup追日志为什么不阻塞呢?因为synchronous_standby_names设置的是*,已经有一个备机能够同步了,不需要等待另一个备机同步了。
该问题需要提交华为解决。