一致性哈希完全指南:从一次"缓存集群加了一台机器、命中率瞬间归零"看懂分布式分片

2020 年我做一个分布式缓存。业务的读压力越来越大单台 Redis 扛不住了我决定加机器上 4 台 Redis 把缓存数据分散到这 4 台上。要解决的核心问题只有一个一个 key 进来我怎么决定它该存到该去哪一台机器上读。第一版我做得很直接把 key 算个哈希值对机器数取模算出 0 1 2 3 就去对应那台。本地一测很顺几百万个 key 均匀地散在 4 台机器上每台四分之一读写都飞快缓存命中率稳定在 95% 以上。可大半年后业务又涨了 4 台又不够了我要加第 5 台。我以为这是件小事加台机器改个配置而已。结果配置一改服务一重启监控瞬间炸了缓存命中率断崖式地从 95% 跌到了个位数几乎所有请求都没命中缓存直接砸到了数据库上数据库 CPU 瞬间打满整个系统濒临瘫痪。我盯着这个监控图想了很久才彻底想明白第一版错在我以为缓存分片就是把 key 哈希一下对机器数取模分到对应的机器上。这个想法在机器数不变时毫无破绽可它藏着一个致命的耦合 hash key 取模 N 这个算式里的 N 就是机器数一个 key 该去哪台机器死死地绑在了当前有几台机器上。机器数从 4 变成 5 几乎每一个 key 算出来的机器号都变了这意味着几乎全部缓存在我加机器的那一刻集体失效了。本文从头梳理为什么 hash 取模一加机器就雪崩哈希环怎么把映射和机器数解耦虚拟节点怎么解决数据倾斜节点增删时为什么只挪一小段数据加权分布怎么做以及哈希函数选择热点 key 迁移期双查这些把分布式分片真正做对要避开的坑。

2020 年我做一个分布式缓存。业务的读压力越来越大,单台 Redis 扛不住了,我决定加机器——上 4 台 Redis,把缓存数据分散到这 4 台上。要解决的核心问题只有一个:一个 key 进来,我怎么决定它该存到、该去哪一台机器上读?第一版我做得很直接:把 key 算个哈希值,对机器数取模——hash(key) % 4,算出 0、1、2、3,就去对应那台。本地一测——很顺:几百万个 key 均匀地散在 4 台机器上,每台四分之一,读写都飞快,缓存命中率稳定在 95% 以上。我心里很踏实:"缓存分片嘛,不就是 hash 一下取个模。"可大半年后,业务又涨了,4 台又不够了,我要加第 5 台。我以为这是件小事——加台机器、改个配置而已。结果配置一改、服务一重启,监控瞬间炸了:缓存命中率断崖式地从 95% 跌到了个位数,几乎所有请求都没命中缓存、直接砸到了数据库上,数据库 CPU 瞬间打满,整个系统濒临瘫痪。我盯着这个监控图想了很久才彻底想明白,第一版错在一个根本的认知上:我以为"缓存分片,就是把 key 哈希一下,对机器数取模,分到对应的机器上"。这个想法在机器数不变毫无破绽。可它藏着一个致命的耦合:hash(key) % N 这个算式里的 N,就是机器数——一个 key 该去哪台机器,死死地绑在了"当前有几台机器"上。机器数从 4 变成 5,% 4 变成 % 5,于是几乎每一个 key,算出来的机器号都变了。这意味着:几乎全部缓存,在我加机器的那一刻,集体失效了。问题的根子,就是 hash 取模这种分片方式,把"key 到机器的映射",和"机器总数"绑死了。要解决它,需要一种加减机器时,绝大多数 key 的归属都不变的分片办法——这就是一致性哈希。这篇文章就把一致性哈希梳理一遍:为什么 hash 取模一加机器就雪崩、哈希环怎么把映射和机器数解耦、虚拟节点怎么解决数据倾斜、节点增删时为什么只挪一小段数据、加权分布怎么做,以及哈希函数选择、热点 key、迁移期双查这些把分布式分片真正做对要避开的坑。

问题背景

先把那次缓存雪崩的现象和我的误判讲清楚,后面所有的设计都是冲着纠正这个误判去的。

现象:一个 4 台机器的分布式缓存,用 hash(key) % 4 决定 key 落在哪台。要扩到 5 台时,只是改了配置重启,缓存命中率从 95% 断崖跌到个位数,请求全部穿透到数据库,数据库 CPU 打满、系统濒临瘫痪。

我当时的错误认知:"缓存分片就是 hash 取模,N 台机器就对 N 取模;加机器无非改个数字。"

真相:hash(key) % N 把"key 到机器的映射"死死绑在了机器总数 N 上。N 一变(4→5),% 4% 5,几乎每个 key 的归属机器都变——等于全部缓存集体失效。正确做法是一致性哈希:把节点和 key 都映射到一个上,key 顺时针归属最近的节点;加减一个节点,只影响环上相邻的一小段,被搬动的 key 只有约 K/N

要把分布式分片做对,需要几块认知:

  • 为什么 hash 取模一加机器就雪崩——映射和机器数被绑死了;
  • 哈希环——把节点和 key 放到同一个环上,解耦机器总数;
  • 虚拟节点——解决环上分布不均导致的数据倾斜;
  • 节点增删——为什么只挪相邻一小段,而不是全量重映射;
  • 加权分布、哈希函数、热点 key、迁移期双查这些工程坑怎么处理。

一、为什么 hash 取模一加机器就雪崩

先把这件最根本的事钉死:hash 取模分片,把"key 落在哪台机器"这件事,和"当前有几台机器"这个数字焊死在了一起;只要机器数一变,几乎每个 key 的归属都会跟着变——在缓存场景里,这等于让全部缓存在同一瞬间集体失效。

下面这段代码,就是我那个"加台机器就雪崩"的第一版——它对机器数取模:

import hashlib


def hash_key(key: str) -> int:
    """把任意字符串映射成一个 32 位整数。"""
    h = hashlib.md5(key.encode("utf-8")).hexdigest()
    return int(h[:8], 16)            # 取前 32 bit,范围 0 ~ 2^32-1


def node_of_key_naive(key: str, nodes: list) -> str:
    # 反面教材:hash 取模分片。N 台机器,就对 N 取模。
    idx = hash_key(key) % len(nodes)
    return nodes[idx]
    # 破绽:返回值【强依赖 len(nodes)】。一旦增删机器,
    # len(nodes) 变了,几乎每个 key 算出的 idx 都跟着变。

这段代码在机器数固定完美无瑕:哈希函数足够散,几百万 key 会均匀地落在每台机器上。它的问题不在代码本身,而在一个被忽略的隐含依赖:返回结果 idx,不只取决于 key,还取决于 len(nodes)。我们来实测一下,从 4 台扩到 5 台,到底有多少 key 被重新映射:

def count_remapped(keys: list, old_nodes: list,
                   new_nodes: list) -> int:
    """统计:节点列表从 old 变到 new,多少 key 的归属节点变了。"""
    moved = 0
    for k in keys:
        old = node_of_key_naive(k, old_nodes)
        new = node_of_key_naive(k, new_nodes)
        if old != new:
            moved += 1
    return moved
    # 实测:4 台扩到 5 台,约 80% 的 key 都被重新映射。
    # 这 80% 的 key 在新机器上全部 miss —— 缓存瞬间失效,
    # 请求穿透到数据库,这就是扩容引发雪崩的根源。

实测结果触目惊心:4 台扩到 5 台,大约 80% 的 key 算出的机器号都变了。从理论上讲,% N 改成 % (N+1) 后,一个 key 还能落回原机器的概率,只有大约 1/(N+1)——也就是说,扩容时被搬动的 key 比例,高达 N/(N+1),机器越多,这个比例越接近 100%。在数据库分片里,这意味着一次扩容要搬走几乎全部数据;在缓存里更要命——缓存不需要"搬",但那 80% 的 key 在新机器上全是 miss,等于80% 的缓存在加机器的一瞬间集体失效,海量请求同时穿透到数据库。问题的根子清楚了:取模分片把映射和机器数绑死了,我们需要一种映射方式,让它在机器数变化时,绝大多数 key 的归属保持不变。

二、哈希环:把节点和 key 放到同一个环上

一致性哈希的核心思路,是不再对机器数取模。它想象出一个首尾相接的环(比如 02^32-1,最大值之后绕回 0)。然后做两件事:第一,把每台机器,用哈希算出一个值,放到环上的某个位置;第二,把每个 key,也用同一个哈希函数算出值,放到环上。一个 key 该归哪台机器?规则极其简单:从 key 的位置出发,顺时针走,遇到的第一台机器,就是它的归属。

import bisect


class ConsistentHashRing:
    """一致性哈希环:节点和 key 都映射到同一个环上。"""

    def __init__(self, replicas: int = 150):
        self.replicas = replicas      # 每个物理节点的虚拟节点数
        self.ring = {}                # 环上坐标 -> 物理节点名
        self.sorted_keys = []         # 环上所有坐标,排好序供二分查找

    def add_node(self, node: str):
        """把一个物理节点,以 replicas 个虚拟节点撒到环上。"""
        for i in range(self.replicas):
            point = hash_key(f"{node}#{i}")
            self.ring[point] = node
            bisect.insort(self.sorted_keys, point)

    def remove_node(self, node: str):
        """把一个物理节点的全部虚拟节点,从环上摘掉。"""
        for i in range(self.replicas):
            point = hash_key(f"{node}#{i}")
            del self.ring[point]
            self.sorted_keys.remove(point)

查询一个 key 落在哪,就是在环上做一次顺时针查找——因为环上坐标是排好序的,这一步可以用二分查找,非常快:

    def get_node(self, key: str) -> str:
        """查 key 归哪个节点:在环上【顺时针】找最近的那个节点。"""
        if not self.sorted_keys:
            raise RuntimeError("环上没有任何节点")
        h = hash_key(key)
        # 二分查找:第一个 >= h 的坐标,就是顺时针遇到的第一个节点
        idx = bisect.bisect(self.sorted_keys, h)
        if idx == len(self.sorted_keys):
            idx = 0                   # 越过环的末端,绕回开头
        return self.ring[self.sorted_keys[idx]]

哈希环精妙的地方,就在于它彻底改变了"映射依赖什么"。在 hash 取模里,key 的归属依赖 N;在哈希环里,key 的归属只依赖"它顺时针方向最近的那台机器在哪"。这就带来一个关键性质:当你加一台新机器,它落到环上某个位置,它只会"接管"从它逆时针方向到上一台机器之间那一段的 key——环上其它所有位置的 key,归属完全不受影响减一台机器同理:只有原本属于它的那一段 key,会顺延给环上的下一台机器,别处纹丝不动。映射和"机器总数"解耦了。但这个朴素的环,还藏着一个问题:机器在环上的位置是哈希随机撒的,如果只撒几台,它们很可能挤在环的某一小段,导致有的机器管很长一段、有的只管很短一段——数据倾斜

三、虚拟节点:解决环上的数据倾斜

设想环上只有 3 台真实机器。它们的哈希位置纯靠运气,完全可能两台挨得很近、一台离得很远。结果就是:那台"身后空旷"的机器,要管大半个环的 key,被压垮;另外两台很轻松。这就是数据倾斜。解法很巧妙——虚拟节点:不让一台物理机器在环上只占一个点,而是让它化身成几百个"虚拟节点",均匀地撒满整个环。上面 add_node 里的 replicas 参数,做的就是这件事。我们写个函数,实测一下分布:

def distribution_report(ring: ConsistentHashRing,
                        keys: list) -> dict:
    """统计每个物理节点实际分到多少 key —— 检查数据是否倾斜。"""
    counts = {}
    for k in keys:
        node = ring.get_node(k)
        counts[node] = counts.get(node, 0) + 1
    avg = sum(counts.values()) / len(counts)
    # 关键:虚拟节点数 replicas 太小,环上分布不均,某些节点
    # 会分到远超均值的 key —— 这就是数据倾斜。把 replicas 调大
    # (如 150),每台机器的几百个分身均匀铺满环,分布就平了。
    skew = {n: round(c / avg, 2) for n, c in counts.items()}
    return {"counts": counts, "skew_ratio": skew}

虚拟节点的精髓,是用"大数定律"来对抗"少数点的随机性"3 个点撒在环上,运气差就严重不均;但 3 台机器 × 150 个虚拟节点 = 450 个点撒在环上,它们几乎必然会非常均匀地铺开——每台物理机器,因为有几百个分身遍布全环,它管辖的总长度趋于相等。虚拟节点还带来一个额外的好处:当一台机器宕机,它那几百个虚拟节点留下的"空缺",会被环上各个不同的其它机器分别接管——于是宕机机器的负载,被均匀分摊给了所有存活机器,而不是一股脑全压给它顺时针的那一台(否则那一台很可能被连带压垮,引发连锁宕机)。replicas 不能太小(分布不均),也不必太大(环上点太多、查询和内存开销上升),一两百是常见的取值。环和虚拟节点都就位了,现在来验证它最关键的承诺:加减节点,到底搬动了多少 key?

四、节点增删:只挪相邻一小段,不是全量

一致性哈希全部价值的兑现,就在这一节。我们用和第一节完全相同的实验——加一个节点,数一数有多少 key 的归属变了——但这次跑在哈希环上:

def count_moved_consistent(keys: list, ring: ConsistentHashRing,
                          new_node: str) -> int:
    """统计:在一致性哈希环上加一个节点,多少 key 的归属变了。"""
    before = {k: ring.get_node(k) for k in keys}   # 加节点前的归属
    ring.add_node(new_node)                        # 加入新节点
    after = {k: ring.get_node(k) for k in keys}    # 加节点后的归属
    moved = sum(1 for k in keys if after[k] != before[k])
    return moved
    # 关键对比:同样是加一个节点 ——
    #   hash 取模:约 80% 的 key 被重新映射(第一节实测)
    #   一致性哈希:只有约 1/(N+1) 的 key 被移动
    # 而且这些被移动的 key,只来自环上相邻的节点。

结果是决定性的:同样加一个节点,hash 取模搬动了 80% 的 key,一致性哈希只搬动了约 1/(N+1)——4 台扩到 5 台,大约只有 20%,而且这 20% 只来自新节点在环上相邻的那几个节点。为什么?因为新节点的每一个虚拟节点落到环上,只接管逆时针方向、到上一个虚拟节点之间那一小段的 key——环上绝大部分区段,压根没察觉有新节点加入。这正是一致性哈希名字里"一致"二字的含义:节点集合发生变化时,尽可能多的 key 保持原有的映射不变。被影响的 key 数量,从 O(K)(全量)降到了 O(K/N)。对缓存来说,这意味着扩容时只有 1/N 的缓存失效,命中率只小幅波动,数据库压力可控;对数据库分片来说,这意味着扩容只需迁移 1/N 的数据。一致性哈希的基本盘到此完成。但生产环境里,机器往往不是一样的配置

五、加权分布:让强弱不均的机器各尽其力

到目前为止,我们默认所有机器一样强,所以让它们平摊 key。但真实集群里,机器常常配置不一:有的是 64G 内存的新机器,有的是 16G 的旧机器。让它们扛一样多的 key,旧机器会先撑爆。一致性哈希应对这个问题非常自然——既然一台物理机器的"份额",取决于它在环上撒了多少虚拟节点,那给强的机器多撒一些虚拟节点就行了:

    def add_weighted_node(self, node: str, weight: int = 1):
        """加权:weight 越大,撒的虚拟节点越多,分到的 key 越多。"""
        # 关键:机器配置不均时,给高配机器更大的 weight,
        # 让它按能力多扛 key —— 而不是所有节点一律均摊,
        # 把弱机器压垮。weight=2 的机器,份额约是 weight=1 的两倍。
        count = self.replicas * weight
        for i in range(count):
            point = hash_key(f"{node}#{i}")
            self.ring[point] = node
            bisect.insort(self.sorted_keys, point)

加权的道理很朴素:虚拟节点的数量,直接正比于一台机器在环上占据的总长度,也就正比于它分到的 key 数量。给 64G 的机器 weight=4、16G 的机器 weight=1,前者就会自动分到大约后者 4 倍的 key。这件事在 hash 取模的方案里极难优雅地做到——取模天然假设"所有槽位平等"。一致性哈希因为有"虚拟节点数量"这个旋钮,加权几乎是免费送的能力。环、虚拟节点、增删、加权——一致性哈希的四块都齐了。但要把它真正用在生产上,还有几个绕不开的坑。

六、工程坑:哈希函数、热点 key 与迁移期双查

四块设计之外,还有几个工程坑,不处理就会在生产上出事。坑 1:哈希函数要选"分布均匀"的,别用业务 ID 直接当哈希值。一致性哈希的全部均匀性,都建立在"哈希函数能把输入打散得足够随机"这个前提上。md5murmurhash 这类分布优良的函数是合适的;千万不要偷懒用"用户 ID 本身"或"自增 ID"当哈希值——它们不随机,会让 key 挤在环的一小段,虚拟节点也救不回来。坑 2:一致性哈希解决不了"热点 key"。它保证的是 key 的"数量"在节点间均匀,但如果某一个 key 本身疯狂访问(比如一个爆款商品的详情缓存),那这一个 key 落在的那台机器,照样会被单独打爆——这是另一个维度的问题,要靠热点 key 单独探测、本地多级缓存或 key 拆分来解决。先看一个迁移期的关键工具:

def get_with_migration(ring_old: ConsistentHashRing,
                       ring_new: ConsistentHashRing,
                       key: str, cache_get) -> object:
    """扩容迁移期:先查新环,未命中再回查旧环 —— 平滑过渡。"""
    node = ring_new.get_node(key)
    val = cache_get(node, key)
    if val is not None:
        return val
    # 关键:数据还没从旧节点搬完时,新环上会 miss。此时回查
    # 旧环对应的节点;命中了就【顺手回填】到新节点。这样
    # 迁移期内不会出现大面积穿透,数据被逐步、平滑地搬过去。
    old_node = ring_old.get_node(key)
    val = cache_get(old_node, key)
    return val

坑 3:扩容那一刻,被搬走的 1/N 仍然会 miss。一致性哈希把扩容失效的缓存从 80% 降到了 20%,但不是 0。这 1/N 的 key 在新节点上第一次访问仍是 miss。如果你的库很脆弱,连这 1/N 的穿透都受不了,就要配合上面的迁移期双查,或在扩容前预热新节点。坑 4:节点列表要让所有客户端"看到的一致"。如果是客户端各自计算环,那么"当前有哪些节点"这份名单,必须通过配置中心等方式让所有客户端拿到完全一致的版本——否则两个客户端环不一样,同一个 key 会被路由到不同机器,缓存各写各的坑 5:别忘了数据迁移本身。一致性哈希告诉你"哪些 key 该换节点了",但把这些 key 的数据真正搬过去(对数据库分片而言)仍是一项独立的工程——环只负责路由,不负责搬运。下面用一段代码,把"一次查询如何在环上定位"完整串起来:

def demo_full_flow():
    """完整演示:建环、查路由、扩容、再看 key 的归属变化。"""
    ring = ConsistentHashRing(replicas=150)
    for n in ["cache-A", "cache-B", "cache-C", "cache-D"]:
        ring.add_node(n)
    keys = [f"user:{i}" for i in range(100000)]

    before = {k: ring.get_node(k) for k in keys}
    moved = count_moved_consistent(keys, ring, "cache-E")  # 扩到 5 台
    after = {k: ring.get_node(k) for k in keys}

    # 被搬动的 key,应只占约 1/5,且去向只是新节点 cache-E
    to_new = sum(1 for k in keys
                 if before[k] != after[k] and after[k] == "cache-E")
    return {"total": len(keys), "moved": moved, "moved_to_new": to_new}

下面这张图,把一次 key 路由如何在环上完成串起来:

关键概念速查

概念 / 手段 说明
hash 取模分片 对机器数取模决定 key 归属,机器数一变几乎全部 key 被重映射
扩容雪崩 取模分片加机器时约 N/(N+1) 的缓存瞬间失效,请求穿透打垮数据库
一致性哈希 节点和 key 映射到同一个环,key 顺时针归属最近节点,映射不依赖机器总数
哈希环 0 到 2^32-1 首尾相接的环,加减节点只影响环上相邻一小段
顺时针查找 从 key 坐标出发顺时针找第一个节点,环坐标排序后可用二分查找
数据倾斜 真实节点少时哈希位置随机不均,某节点管辖过长一段被压垮
虚拟节点 一台物理机化身几百个虚拟节点撒满环,用大数定律让分布趋于均匀
只挪 1/N 增删一个节点只移动约 K/N 的 key,且只来自环上相邻节点
加权分布 给高配机器多撒虚拟节点,让它按能力多扛 key,弱机器不被压垮
热点 key 一致性哈希只均匀 key 数量,单个 key 被狂刷仍会打爆所在机器

避坑清单

  1. hash 取模分片把映射绑死在机器数上,加减一台机器几乎全部 key 被重映射。
  2. 缓存集群用取模分片扩容,约 N/(N+1) 的缓存瞬间失效,会引发穿透雪崩。
  3. 用一致性哈希把节点和 key 放到环上,让映射不再依赖机器总数。
  4. 哈希环加减节点只影响相邻一小段,被搬动的 key 只有约 K/N。
  5. 真实节点少时环上分布会倾斜,必须用虚拟节点把每台机器撒成几百个分身。
  6. 虚拟节点数一两百为宜,太小分布不均,太大徒增查询和内存开销。
  7. 机器配置不均时用加权,给高配机器多撒虚拟节点,别让弱机器被压垮。
  8. 哈希函数要选分布均匀的如 md5 murmurhash,别拿自增 ID 直接当哈希值。
  9. 一致性哈希解决不了热点 key,单个 key 被狂刷要靠探测和多级缓存单独处理。
  10. 扩容时被搬走的 1/N 仍会 miss,脆弱的库要配迁移期双查或预热新节点。

总结

回头看那次"缓存集群加了一台机器、命中率瞬间归零"的事故,以及我后来在分布式分片上接连踩的坑,最该记住的不是某一段哈希环代码,而是我动手前那个想当然的判断——"缓存分片,就是 hash 一下取个模"。这句话错在它只看见了"怎么把 key 分匀"这一个目标,却完全没看见另一个同样重要的目标:"当机器数变化时,这个映射稳不稳"。hash(key) % N分匀这件事上做得很好,可它在稳定性糟透了——它把 key 的归属和 N 焊死,N 一动,满盘皆乱。分布式分片这件事想清楚的,正是这个:一个分片算法,要同时满足两个要求——平时要把数据分得均匀,变更时要让映射尽可能稳定。取模只满足了第一个,一致性哈希两个都满足

所以做分布式分片,真正的工程量不在"% N"那一行取模上。那一行,任何教程的第一页就教完了。真正的工程量,在于你要预见到"机器数一定会变"这件事——它会因为扩容而变,会因为宕机而变,会因为缩容而变——然后选一个能从容应对这种变化的映射方式:你要用哈希环,让映射不再依赖机器总数;你要用虚拟节点,让少数几台机器也能把环分得均匀、让宕机的负载被均摊而非转嫁;你要用加权,让强弱不一的机器各尽其力;你还要为扩容那一刻残余的 1/N 失效,想好迁移期双查的退路。这篇文章的几节,其实就是顺着这条思路展开的:先想清楚取模为什么一扩容就雪崩,再用哈希环解耦机器数,用虚拟节点抹平倾斜,然后验证"只挪 1/N"这个核心承诺,最后是加权、哈希函数、热点这几个把分片真正做扎实的工程细节。

你会发现,一致性哈希的思路,和现实里怎么安排一栋楼的住户门牌完全相通。一种糟糕的办法是:按"第几个搬进来的"对楼层数取模分房间——今天楼里 4 层,大家按 % 4 住下;明天加盖了第 5 层,变成 % 5,于是几乎每一户都得搬家,整栋楼鸡飞狗跳。而有经验的物业会怎么做?他们给每户一个固定的、和"楼里总共住了多少户"无关的门牌号——你的门牌就是你的门牌,加盖新楼层只影响新楼层那一小片,老住户纹丝不动(这是哈希环);他们还懂得,不能让一部电梯只服务一整片区域,而是每隔几层就设一个电梯厅,让人流均匀分散(这是虚拟节点);宽敞的大户型,自然对应更多的房间号段(这是加权)。好的门牌系统,衡量标准从来不是"此刻分得多齐",而是"将来加楼、拆楼时,要惊动多少老住户"。

最后想说,分片算法选得对不对,差距永远不会在集群规模稳定时暴露——机器数一直不变的日子里,hash % N一致性哈希,跑起来一模一样快,你甚至会觉得后者那个哈希环、虚拟节点没必要的复杂。它只在真实的、集群因为扩容、因为宕机、因为缩容不得不变的那一刻才显形。那时候它会用最难堪的方式给你结账:做不好,你会像我一样,只是加了一台机器,就看着缓存命中率断崖归零、看着海量请求穿透把数据库瞬间打满,一次本该波澜不惊的扩容,变成一场濒临瘫痪的事故;而做了,无论你是从容地加机器,还是突然挂掉一台,变化都只波及环上一小段,绝大多数 key 归属不变,命中率只是小幅抖动,系统稳稳地扛了过去。所以别等扩容变成一场雪崩,在你写下第一行"把 key 分到机器上"的代码之前就该想清楚:我的集群将来一定会增减机器吗?到那一刻,会有多少 key 的归属被打乱?这个代价,我承受得起吗?这几个问题都有了答案,你的分片才不只是晴天里那个"分得很齐"的样子,而是一套无论机器怎么增减都稳得住的可靠路由。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

向量数据库选型完全指南:从一次"RAG 知识库涨到百万向量、检索卡十几秒"看懂向量检索

2026-5-21 22:43:45

技术教程

AI Agent 设计完全指南:从一次"Agent 陷入死循环、烧掉几美元 token 还没完成任务"看懂智能体工程

2026-5-21 22:56:53

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索