尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
一致性哈希不是平均分配:一次扩容把缓存命中率从 92% 打到 61% 的事故
title: 一致性哈希不是平均分配一次扩容把缓存命中率从 92% 打到 61% 的事故topic: 一致性哈希算法与分片策略batch: 5round: 3我们 Redis 缓存集群原来 4 个节点用一致性哈希做分片命中率稳在 92%。有次大促前扩容运维一口气加了 4 个节点变 8 个本想更稳结果上线 10 分钟缓存命中率直接掉到 61%数据库 CPU 从 40% 飙到 88%差点把主库打挂。我们复盘才发现一致性哈希保证了「只影响相邻数据」但没保证「数据均匀分布」——新节点哈希位置扎堆把一整段热点 key 全揽了过来老节点反而空闲。这篇文章把这个坑讲透并给你一份能直接抄的一致性哈希实现含虚拟节点。事故现场一次「好心」的扩容出事前的分片逻辑简化版public class NaiveHashRouter { private final TreeMapLong, String ring new TreeMap(); // 真实节点直接上环 public void addNode(String node) { long hash hash(node); // 一个节点只占环上一个点 ring.put(hash, node); } public String route(String key) { long h hash(key); Map.EntryLong, String e ring.ceilingEntry(h); // 顺时针找最近节点 return e ! null ? e.getValue() : ring.firstEntry().getValue(); } }逐行解释为什么这种「朴素版」会出事- 第 4 行addNode让一个真实节点只在哈希环上占一个点。节点少的时候这几个点把环切得不均匀本来就有「有的弧段长、有的短」。- 第 8 行ring.ceilingEntry(h)顺时针找最近节点key 落在哪个弧段就归哪个节点。弧段越长这个节点存的 key 越多。- 扩容加 4 个节点后新节点的哈希位置如果刚好扎在某段长弧里就会把这段长弧整体「切走」原来归老节点的大量 key 全部迁到新节点——命中率瞬间崩。我们那次就是 4 个新节点哈希值挤在相邻区间等于把 80% 的热点 key 全收编了。解法虚拟节点把弧段切匀一致性哈希的标准做法是给每个真实节点映射成 N 个「虚拟节点」散在环上不同位置key 先落虚拟节点再映射到真实节点分布就均匀了。public class ConsistentHashRouter { private final TreeMapLong, String ring new TreeMap(); private final int virtualPerNode; public ConsistentHashRouter(int virtualPerNode) { this.virtualPerNode virtualPerNode; // 每个真实节点拆成多少个虚拟节点 } public void addNode(String node) { for (int i 0; i virtualPerNode; i) { // node#i 算出的哈希分散在环的不同位置避免真实节点扎堆 long vhash hash(node # i); ring.put(vhash, node); // value 仍是真实节点名 } } public String route(String key) { if (ring.isEmpty()) return null; long h hash(key); Map.EntryLong, String e ring.ceilingEntry(h); return e ! null ? e.getValue() : ring.firstEntry().getValue(); } }逐行解释- 第 11 行for (int i 0; i virtualPerNode; i)给每个真实节点生成virtualPerNode个虚拟节点我们线上用 160。- 第 13 行hash(node # i)关键是「node#i」这个后缀让虚拟节点哈希值分散开不再扎堆——这是均匀性的来源。- 第 14 行ring.put(vhash, node)哈希环上存的是虚拟节点位置但 value 映射回真实节点所以路由结果还是真实节点只是 key 被更多「采样点」均分了。- 虚拟节点越多分布越均匀标准差越小但ring越大ceilingEntry查找成本和内存越高。160 是我们压测后定的平衡点。扩容时到底迁移多少数学直觉一致性哈希的好处不是「不迁移」而是「只迁移 1/N」。N 个节点扩到 2N每个 key 只有约 1/(2N) 的概率落在新增弧段所以整体只有约 50% 的 key 需要迁移——比取模哈希key % N扩容 100% 迁移少得多。// 用 Guava 的已验证实现更省心别自己造轮子 Component public class ShardRouter { private volatile TreeMultimapString, String nodes; // 真实节点 - 虚拟节点 public String route(String key) { // 生产建议直接用 com.google.common.hash.Hashing 一致性哈希封装 // 或者直接用 Redis Cluster 的槽位16384 个 slot代替手写环 return doRoute(key); } }逐行解释- 第 6 行注释点出我的真实建议线上别手写一致性哈希要么用 Guava/Redis 客户端的成熟实现要么直接用 Redis Cluster 的 16384 槽位——它本质就是「更工程化的一致性哈希」槽位固定、迁移按槽走比自己维护哈希环稳得多。- 我们后来把缓存分片从手写环迁到 Redis Cluster扩容時命中率波动从 30 个点降到不到 3 个点因为槽位迁移是增量的、可控制的。几种分片策略怎么选一张对比表策略扩容迁移量均匀性实现成本取模 hash(key)%N100%好极低一致性哈希无虚拟节点~1/N差易扎堆中一致性哈希160 虚拟节点~1/N好中Redis Cluster 槽位按槽增量好低用现成我的取舍新项目直接用 Redis Cluster 或客户端成熟库别手写哈希环老系统要加虚拟节点虚拟节点数设 100-200太少不均、太多费内存。节点宕机时的再平衡虚拟节点也是双刃剑扩容讲完另一个真实场景是「节点宕机」。一致性哈希的好处是某个节点挂了只有它的虚拟节点对应的 key 需要迁移到顺时针下一个节点其他节点纹丝不动——这比取模哈希「一挂全乱」强太多。但虚拟节点带来一个副作用如果一个真实节点有 160 个虚拟节点它一宕机这 160 个点对应的 key 会「顺时针」分散迁移到多个不同真实节点上。听起来均匀实则是瞬间把压力分摊给邻居——如果邻居本来就接近水位可能一起被冲高。我们一次 Redis 节点 OOM 宕机它的 key 涌向相邻两个节点那两个节点 CPU 立刻涨了 15 个点差点连锁雪崩。应对办法不是去掉虚拟节点那会回到分布不均而是两件事第一监控上对每个真实节点的负载做「虚拟节点数 × 单点权重」的容量预留别把节点压到 90% 才扩容第二宕机切换走「先摘流量再迁移」让 key 迁移错峰而不是瞬间全灌。我们之后用 Redis Cluster槽位迁移本来就是增量的、可控节奏的这个问题比手写环好处理得多。再补一句选型如果分片的是「缓存」节点宕机丢一部分 key 只是回源压力能忍如果是「有状态分片数据」比如按哈希分片的用户表宕机丢失的那段数据必须有副本否则一致性哈希救不了你——它只管路由不管数据不丢。我们曾经有个按哈希分片的离线表节点宕机丢了 12% 的数据因为没有副本最后靠上游重算补回那次之后有状态分片一律上副本。复盘真实数字出事那次命中率从 92% 掉到 61%数据库 CPU 涨到 88%连累下单接口 P99 从 120ms 到 1.1s。回滚到 4 节点 给 4→8 的扩容改成「分批加、每加 1 个观察命中率」后每次扩容命中率只掉 2-4 个点30 秒内自愈。迁到 Redis Cluster 后同样 4→8 扩容命中率波动 3 个点运维再也不用半夜盯命中率曲线。虚拟节点数怎么定一个压测结论虚拟节点数不是越多越好我们压过一组数给你直观参考。在 8 个真实节点、2400 万 key 的场景下虚拟节点 20各节点 key 数标准差约 18%热点节点扛了 31% 流量命中率波动大。虚拟节点 100标准差降到约 6%最重节点 22%基本可接受。虚拟节点 160标准差约 4%最重节点 20%和我们线上一致是甜区。虚拟节点 320标准差约 3.5%收益几乎不变但ring的TreeMap体量翻倍路由查找和内存都涨不划算。结论很直接100 是及格线160 是甜区再往上纯属浪费。如果你节点数很少比如 3-4 个虚拟节点更要拉高因为真实节点少时单点一个哈希点占据的弧段天然就长靠虚拟节点把它切碎才匀得开。我们 4 节点时代用的是 200扩到 8 节点后降到 160原则是「节点越少、虚拟越多」。另外提醒一句虚拟节点的哈希函数本身要用分布均匀的我们用的是 Murmur3别用String.hashCode()——它在短字符串上有规律性聚集虚拟节点反而会扎堆适得其反。我们早期偷懒用过hashCode()结果命中率比用 Murmur3 低了 5 个点换掉才正常。我的取舍虚拟节点是必选项我不建议用「一个真实节点一个哈希点」的朴素一致性哈希上生产——它省的那点内存远不够赔偿一次命中率暴跌的故障。虚拟节点 100-200 是性价比甜区。更进一步如果团队没有强理由我更倾向于直接用 Redis Cluster 的槽位模型代替手写环它把「一致性哈希」的工程坑虚拟节点数、扩缩容迁移、节点宕机再平衡都替你踩平了你只需要管 slot 分配。手写哈希环适合做缓存分片的学习样例不适合当核心链路的唯一依赖。思考题如果你的一致性哈希环上虚拟节点是 160某真实节点宕机它的 160 个虚拟节点对应的 key 会怎样重新分布会不会让相邻的真实节点瞬间压力翻倍
RELATED

相关推荐

Proxima优化vLLM KV Cache内存管理,提升GPU并发推理能力

Proxima优化vLLM KV Cache内存管理,提升GPU并发推理能力

这类工具最值得先看的不是功能列表,而是能不能在普通环境里稳定跑起来,以及它到底解决了什么具体瓶颈。Proxima 这个项目,核心就一句话:它通过优化 vLLM 推理引擎中的 KV Cache 内存管理,在完全不增加硬件的情况下&…

📅 2026/10/12 22:27:26
数学建模竞赛B题解题全攻略:从预测优化到论文写作

数学建模竞赛B题解题全攻略:从预测优化到论文写作

1. 赛题核心解读与破题思路全国大学生数学建模竞赛的B题,历来是考察学生综合运用数学工具解决复杂实际问题能力的“硬骨头”。2025年的B题,从趋势上看,极有可能延续近几年的风格:聚焦于一个具有明确社会或工程背景的交叉学科问题&…

📅 2026/8/31 22:14:31
数学建模在NIPT检测优化中的应用:从统计分布到机器学习决策

数学建模在NIPT检测优化中的应用:从统计分布到机器学习决策

1. 项目概述:从一道赛题到一次深度科研实践拿到“2025年高教社杯全国大学生数学建模竞赛C题”这个标题,很多同学的第一反应可能是去找“成品思路”和“代码”。但我想说,这道关于“NIPT(无创产前检测)的时点选择与胎儿…

📅 2026/10/7 7:33:16
MORE NEWS

更多资讯

📰

基于深度学习的研究问题自动提炼与论证框架构建实践

如果你最近在写论文,或者正准备开题,请跟我一起想一个问题:你的研究问题是什么?很多人能说出研究方向,却很难在十分钟之内给出一个边界清楚、有可检验空间、又值得做的研究问题。我过去大半年带着几个学生做了一套内部…

📰

多目标灰狼算法求解IEEE30节点环境经济调度:建模、实现与调优

电力系统的环境经济调度(Environmental Economic Dispatch,EED)这几年一直是研究热点,尤其是把多目标灰狼优化算法(Multi-Objective Grey Wolf Optimizer,MOGWO)用到 IEEE30 节点系统上做仿真&a…

📰

SpringBoot供应链系统实战:从数据库建模到库存扣减

接手这个供应链管理系统的时候,我心里其实有点打鼓。供应链三个字,听起来就是一张巨大的业务网络:供应商、采购、库存、物流、订单、结算,任何一个环节都能单独拉出来做成一套专业软件。而我要做的,是用SpringBoot把这…

📰

机器人操作系统SLAM导航:从架构拆解到工程化实战

1. 机器人操作系统核心架构与SLAM导航的全局拆解机器人操作系统这个词,刚入行的朋友听到容易发怵,觉得是某种高深莫测的黑科技。其实把它理解成一个“机器人专属的安卓系统”就通了——它不直接控制电机转动,也不直接处理激光雷达的点云数据&…

📰

【工具】把WorkBuddy变成API,白嫖免费模型,还支持NAS部署

转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~如果你不想买Token Plan,又想用免费模型,那学长真心建议你跟着我走完这篇教程!!!软件介绍Wor…

📰

UDP之上实现可靠传输:停等、GBN与SR协议详解与避坑指南

简介:面向计算机网络课程设计中可靠数据传输协议实验,这是一份基于Python实现的完整方案,覆盖停等协议、GBN(后退N帧)与SR(选择重传)三种机制的代码与文档。资源共14个文件,包括8个p…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬