集群客户端要么自己路由,要么每次都被服务器「指路」。指路要多花一次往返。这篇把三种常见策略在同一个集群上对比:完全不缓存槽位表、只在收到 MOVED 时记一个槽、启动时拉整张槽位表;然后在线迁移 1000 个槽,看手里是旧表的客户端要被指多少次路,才能恢复。
策略 请求数 MOVED 每请求往返数 平均耗时 µs 没有槽位表,只会跟 MOVED,不学习 30000 19987 1.67 47.7 没有槽位表,每次 MOVED 学一个槽 30000 9507 1.32 37.9 启动时 CLUSTER SLOTS 拉全表 30000 0 1.00 28.8
「无表、不学习」的客户端固定连第一个节点,遇到 MOVED 就换节点重发,但下次仍从原点出发:19987 次 MOVED,约 2/3(3 个主库里只有 1/3 的 key 恰好在这个节点上),每个请求平均 1.67 次往返。「每次 MOVED 学一个槽」把指过的路记下来,MOVED 降到 9507 次,但 30000 个 key 散在很多槽上,仍然要被指很多次。「启动时拉全表」用 CLUSTER SLOTS 一次拿到整张槽位表,之后 MOVED 为 0,每个请求恰好一次往返,平均耗时最低(28.8 µs)。本机回环上,每多一次往返约多 28 µs;跨机器网络时代价更大。
reshard 耗时 1.0 s;槽 0–999 中现在属于第二个主库的:1000 个
redis-cli --cluster reshard --cluster-from A --cluster-to B --cluster-slots 1000 用 1.0 s 把槽 0–999 从第一个主库搬到第二个,期间集群一直可用(这里每个槽里的 key 很少)。迁移结束后有两个客户端手里还是迁移之前拉的槽位表。
策略 请求数 MOVED 每请求往返数 平均耗时 µs 旧表 + 每次 MOVED 只更新那一个槽 20000 789 1.04 29.7 旧表 + 第一次 MOVED 就重新拉全表 20000 1 1.00 28.9 (2000 个 key 覆盖了 789 个不同的槽)
只更新被指路的那一个槽:20000 次读里有 789 次 MOVED——2000 个 key 覆盖了 789 个不同的槽,每个槽被纠正一次,1.04 次往返/请求。收到第一次 MOVED 就重新拉整张表:只有 1 次 MOVED,之后全部命中。MOVED 说明「地图变了」,而不仅仅是「这一个槽变了」——一次迁移常常一次搬很多槽,整表刷新更划算。(代价是刷新时多一次 CLUSTER SLOTS 调用;槽位变化频繁时要防止「MOVED 风暴」引起的反复刷新,我没有测这个场景。)