最近在测试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.... doneserver 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.... doneserver stopped
使用压测工具对主库进行并发插入,并观察xlog个数
可以看到xlog在逐步堆积
199199199203210217224231238245253260267274
tps比较稳定
14:04:58 2225414:04:59 2358214:05:00 2393714:05:01 2331614:05:02 2299414:05:03 2345214:05:04 2392014:05:05 2409714:05:06 2388614:05:07 2157814:05:08 2273114:05:09 2331514: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设置的是*,已经有一个备机能够同步了,不需要等待另一个备机同步了。