Redis 的复制是异步的:主库写完就回复客户端,从库晚一点才收到。WAIT 命令让客户端可以「等一等」:这条写入至少被 N 个从库收到了再往下走。这篇验证它能保证什么、不能保证什么:超时怎么算、返回值代表什么、写入会不会回滚,以及它和 WAITAOF 的区别。
200 次:返回值集合 [2];耗时 中位 0.14 ms,最大 13.98 ms
SET 之后立刻 WAIT 2 100,200 次全部返回 2,耗时中位 0.14 ms、最大 13.98 ms。复制通道本身很快,WAIT 通常不会明显增加延迟。
WAIT 2 200 → 1,耗时 207 ms WAIT 1 200 → 1,耗时 0.21 ms
冻结从库 B 之后,WAIT 2 200 只能等满 200 ms 超时,返回 1(耗时 207 ms);WAIT 1 200 则在 0.21 ms 就返回 1。第一个参数是「至少要几个」,够数立即返回,不够就等到超时,返回的是超时那一刻实际确认的从库数。
主库 GET k = during-freeze2 从库 C GET k = during-freeze2 B 恢复后 GET k = during-freeze2
WAIT 返回 1(没有达到 2)之后,主库上的这次写入已经生效,C 能读到,冻结的 B 解冻后也追上了。所以 WAIT 只是「问一问有几个从库收到了」,不是分布式事务:凭返回值没达标就认为「写失败」是错的,该怎么处理要由应用决定。
WAIT 2 3000: 返回 [2],耗时 最小 0.14 中位 0.29 最大 5.74 ms WAITAOF 1 0 3000(仅本地 fsync) 返回 [[1, 0], [1, 0], [1, 0]]… 耗时 最小 1014 中位 1018 最大 1105 ms WAITAOF 1 1 3000 返回 [[1, 1], [1, 1], [1, 1]]… 耗时 最小 1017 中位 1018 最大 1323 ms WAITAOF 1 2 3000 返回 [[1, 2], [1, 2], [1, 2]]… 耗时 最小 1017 中位 1018 最大 1762 ms
WAIT 只保证从库把数据收进了内存。WAITAOF numlocal numreplicas timeout 等的是 AOF 的 fsync。在 everysec 下,WAITAOF 1 0 / 1 1 / 1 2 的耗时都是约 1 秒(最小 1017 ms、中位 1018 ms),对应一次 everysec 的 fsync 周期,返回值 [本地已 fsync 数, 从库已 fsync 数]。我第一次跑用的超时是 1000 ms,恰好和这个周期一样长,所以拿到了不完整的结果——超时要比 fsync 周期留出余量。
WAIT 1 300 → 0,耗时 368 ms
两个从库都冻结时,WAIT 1 300 等到超时(368 ms)返回 0。给 WAIT 设的超时,就是你为「从库确认」愿意付出的最坏延迟。(超时参数为 0 表示无限等待——这一点没有在这里验证,不建议用。)