一、问题背景:为啥要调Raft参数?
很多做业务系统的朋友都知道,数据库得搞高可用,不然哪天服务器崩了,业务直接停摆,损失大得很。openGauss的双机热备就是这么个高可用方案,它核心用的是Raft协议——简单说就是俩机器,一个当主库干活,一个当备库同步数据,主库挂了备库能顶上。但实际用的时候,总会出问题,比如俩机器之间的网络突然断了(专业点叫网络分区),这时候就容易乱:备库觉得主库死了,自己要当主库,结果原来的主库还在接业务,两边都写数据,最后数据就乱了;要么就是备库等半天也不出来,业务卡着没法动。这俩问题,一个是选举慢,一个是数据不安全,咱们今天就盯着这俩来调参数。
二、核心原理:先搞懂Raft的基本逻辑
要调参数,得先知道Raft是咋干活的,不然调错了反而更糟。Raft里有三个关键角色:主库(Leader)、备库(Follower)、候选者(Candidate)。正常情况下,主库管写数据,备库跟着同步;主库每隔一段时间会给备库发“心跳”,告诉备库“我还活着”。如果备库太久没收到心跳,就会觉得主库死了,自己变成候选者,发起选举,谁拿到的票数够,谁就当新主库。
这里有两个最核心的时间参数,也是咱们调优的重点:一个是“选举超时时间”(Election Timeout),就是备库多久没收到心跳就发起选举;另一个是“心跳间隔”(Heartbeat Interval),就是主库多久发一次心跳。这俩参数得配合好,要是心跳间隔比选举超时还长,备库肯定会误以为主库死了,乱选举。
三、网络分区下的典型问题分析
咱们先拿实际场景说问题,这样好懂。假设现在有俩机器,主库在机房A,备库在机房B,俩机房之间的网络延迟是200毫秒(就是说主库发的包,备库要200毫秒才能收到)。
3.1 选举延迟问题
原来的参数可能设的是:选举超时1000毫秒,心跳间隔500毫秒。正常情况下没问题,主库500毫秒发一次心跳,备库每1000毫秒检查一次,能及时收到。但如果网络突然断了,主库的心跳发不过去,备库1000毫秒后发起选举,结果网络又恢复了,原来的主库还在,新的候选者出来,这时候俩主库?不对,双机热备的话,候选者发起选举后,得等另一个节点的回应,要是另一个节点没回应,它就会再等一段时间(一般是随机的,防止俩节点同时发起选举),再发起选举。比如第一次选举没结果,等了1500毫秒,第二次又没结果,再等2000毫秒,这加起来都快5秒了,业务停了5秒,用户肯定受不了。
3.2 数据安全问题
再比如,主库刚写完一条数据,还没来得及同步给备库,网络就断了。这时候备库觉得主库死了,自己当主库,开始写新数据。过了一会儿网络恢复了,原来的主库还在,俩主库都有自己的数据,最后合并的时候就会出现冲突,比如用户的订单被重复创建,或者金额对不上,这就是数据不一致,严重的话业务数据全乱了。
四、调优方案:参数怎么改才对?
咱们得针对上面的问题,改对应的参数,同时还要考虑网络的实际情况。首先得明确双机热备的场景,只有两个节点,所以选举的时候,候选者只要拿到自己的票,再加上另一个节点的票,就能当主库。但如果网络分区了,另一个节点收不到票,那候选者就没法拿到多数票,所以得调参数让它能快速判断,同时保证数据不丢。
4.1 先测实际网络延迟
调参数之前,得先知道俩节点之间的网络延迟是多少,这个是基础,不能瞎设。咱们用ping命令测,连续测几次,取最大值,比如测出来最大延迟是300毫秒,那参数就得比这个大。
技术栈:Linux Shell(因为openGauss一般装在Linux服务器上)
# 连续ping备库IP 10次,取最大延迟
ping -c 10 备库的IP地址 | awk -F '/' '{print $4}' | awk '{print $1}'
# 比如输出是300.2 ms,那最大延迟就是300毫秒
4.2 调整选举超时时间
原来的选举超时可能设的是固定值,比如1000毫秒,其实应该设成“最大网络延迟的2-3倍”,这样既能防止正常的网络波动导致误判,又能让备库及时发现主库真的死了。比如刚才测的最大延迟是300毫秒,那选举超时设成900毫秒(3倍)就合适。
那怎么改openGauss的参数呢?openGauss的参数配置文件在/gaussdb/data/datanode/postgresql.conf里,咱们要改的参数是raft_election_timeout,单位是毫秒。
技术栈:Linux Shell
# 先备份原来的配置文件,防止改坏了
cp /gaussdb/data/datanode/postgresql.conf /gaussdb/data/datanode/postgresql.conf.bak
# 用sed命令修改选举超时时间为900毫秒,注意要先找到原来的行,修改掉
sed -i 's/^raft_election_timeout = .*/raft_election_timeout = 900/' /gaussdb/data/datanode/postgresql.conf
# 然后重启数据库生效(或者重载,重载的话用pg_ctl reload)
su - omm -c "gs_ctl restart -D /gaussdb/data/datanode"
4.3 调整心跳间隔
心跳间隔得比选举超时小很多,一般是选举超时的1/3左右,这样主库能及时给备库发心跳,备库也不会频繁检查。比如选举超时是900毫秒,那心跳间隔设成300毫秒就合适。对应的参数是raft_heartbeat_interval,单位也是毫秒。
技术栈:Linux Shell
# 修改心跳间隔为300毫秒
sed -i 's/^raft_heartbeat_interval = .*/raft_heartbeat_interval = 300/' /gaussdb/data/datanode/postgresql.conf
# 重启数据库生效
su - omm -c "gs_ctl restart -D /gaussdb/data/datanode"
4.4 解决数据安全的参数调整
刚才说的问题,主库刚写完数据还没同步就断网,备库当主库,导致数据不一致。这个问题可以改raft_synchronous_mode参数,把同步模式改成“强同步”,就是主库写完数据,必须等备库确认收到了,才会给客户端返回成功。这样的话,主库写完的数据,备库肯定有,就算断网了,备库当主库,数据也是全的。
但强同步有个问题,就是如果备库同步慢,主库会卡住,所以得配合raft_synchronous_commit_timeout参数,就是主库等备库确认的时间,要是超过这个时间,主库就会报错,不让客户端写数据,防止数据不一致。
比如咱们设强同步模式,等待时间设成500毫秒(比最大网络延迟大一点)。
技术栈:Linux Shell
# 修改同步模式为强同步(on表示强同步)
sed -i 's/^raft_synchronous_mode = .*/raft_synchronous_mode = on/' /gaussdb/data/datanode/postgresql.conf
# 修改同步提交超时为500毫秒
sed -i 's/^raft_synchronous_commit_timeout = .*/raft_synchronous_commit_timeout = 500/' /gaussdb/data/datanode/postgresql.conf
# 重启数据库生效
su - omm -c "gs_ctl restart -D /gaussdb/data/datanode"
4.5 双机热备的特殊调整:防止双主
双机热备只有两个节点,网络分区的时候,很容易出现两个主库,因为候选者拿不到另一个节点的票,所以可以加个参数raft_pre_vote,开启预选举。预选举的意思是,备库在发起正式选举之前,先给另一个节点发个预请求,问“我能不能当主库”,如果另一个节点还活着,就会拒绝预请求,备库就不会发起正式选举,这样就能防止网络恢复后出现双主。
技术栈:Linux Shell
# 开启预选举(on表示开启)
sed -i 's/^raft_pre_vote = .*/raft_pre_vote = on/' /gaussdb/data/datanode/postgresql.conf
# 重启数据库生效
su - omm -c "gs_ctl restart -D /gaussdb/data/datanode"
五、调优后的验证:怎么知道调对了?
改完参数得验证,不能直接上线,得模拟场景测。
5.1 模拟网络分区测试选举速度
咱们用iptables命令模拟网络断网,测备库多久能完成选举。
技术栈:Linux Shell
# 先看当前的主库和备库状态,确认主库在A节点
su - omm -c "gs_ctl state -D /gaussdb/data/datanode"
# 输出应该显示A节点是Leader,B节点是Follower
# 在A节点上执行,禁止B节点的网络包,模拟网络分区
iptables -A INPUT -s 备库IP -j DROP
iptables -A OUTPUT -d 备库IP -j DROP
# 同时在B节点上计时,看多久能变成Leader
# 可以写个简单的脚本,连续检查状态
su - omm -c "
start_time=\$(date +%s)
while true; do
state=\$(gs_ctl state -D /gaussdb/data/datanode | grep 'State' | awk '{print \$2}')
if [ \"\$state\" = \"Leader\" ]; then
end_time=\$(date +%s)
echo \"选举完成,耗时\$((end_time - start_time))秒\"
break
fi
sleep 1
done
"
# 测试完后,恢复A节点的网络
iptables -D INPUT -s 备库IP -j DROP
iptables -D OUTPUT -d 备库IP -j DROP
正常情况下,调优后的选举耗时应该在1-2秒以内,比原来的5秒快很多。
5.2 模拟数据同步中断测试数据安全
咱们可以在主库上写一条数据,然后马上断网,看备库有没有这条数据。
技术栈:Linux Shell
# 在主库上连接数据库,写一条测试数据
su - omm -c "gsql -d postgres -p 26000 -c \"INSERT INTO test_table (id, name) VALUES (1, 'test');\""
# 马上在主库上断网(用刚才的iptables命令)
iptables -A INPUT -s 备库IP -j DROP
iptables -A OUTPUT -d 备库IP -j DROP
# 然后在备库上变成主库后,连接数据库查这条数据
su - omm -c "gs_ctl state -D /gaussdb/data/datanode"
# 等备库变成Leader后,执行查询
su - omm -c "gsql -d postgres -p 26000 -c \"SELECT * FROM test_table WHERE id = 1;\""
# 应该能查到这条数据,说明数据安全
# 测试完恢复网络
iptables -D INPUT -s 备库IP -j DROP
iptables -D OUTPUT -d 备库IP -j DROP
六、应用场景、优缺点、注意事项
6.1 应用场景
这些调优方案适合所有用openGauss双机热备的场景,尤其是对业务连续性要求高的,比如电商的订单系统、金融的交易系统,还有跨机房部署的场景(跨机房网络延迟大,更需要调参数)。另外,对数据一致性要求高的场景,比如医疗数据、政务数据,强同步模式的调整特别适用。
6.2 调优的优缺点
优点很明显:一是选举速度快,网络分区后业务能快速恢复,减少 downtime;二是数据更安全,不会出现双主导致的数据不一致;三是预选举开启后,能有效防止网络波动导致的不必要选举,稳定系统。
缺点也有:强同步模式下,如果备库同步慢,主库会卡住,影响写性能;另外,参数调整得根据实际网络情况来,要是网络延迟变了,参数还得重新调,不然可能出问题。
6.3 注意事项
第一,调参数之前一定要备份配置文件,改坏了能恢复;第二,一定要先测实际的网络延迟,不能瞎设参数,比如网络延迟本来是500毫秒,你设选举超时600毫秒,还是会误判;第三,调完参数一定要做模拟测试,不能直接上线,不然出问题没人担责;第四,跨机房部署的话,还要考虑机房之间的网络稳定性,比如是不是有专线,专线的延迟波动大不大;第五,双机热备的场景,要是网络真的断了很久,就算调了参数,备库变成主库,业务也只能在备库上跑,等网络恢复后,要检查数据一致性,没问题再切回主库。
七、总结
openGauss双机热备的Raft参数调优,核心就是围绕“选举快”和“数据安全”这两个目标,先搞懂Raft的基本逻辑,再测实际的网络情况,然后针对性调整选举超时、心跳间隔、同步模式这些参数,最后还要验证调优效果。整个过程不难,只要按步骤来,就能解决网络分区下的问题。不过要注意,参数不是一成不变的,要是业务场景变了,或者网络情况变了,就得重新调整参数,保证系统一直稳定运行。
Comments