1 分钟

数据库分片如何工作——以及为什么难以推理

分片通过将数据拆分到多个节点来扩展数据库,但它带来了路由、重平衡和新的故障模式,使系统更难以推理。

数据库分片如何工作——以及为什么难以推理

什么是分片(以及什么不是分片)

分片(也称为水平分区)是把对应用看起来像“一个”数据库的数据拆分到多台机器(称为分片)上。每个分片只保存行的一个子集,但合起来代表完整的数据集。

一个逻辑表,多处物理存放

一个有用的思考模型是“逻辑结构”与“物理放置”的区别。

  • 逻辑上: 你仍然有一个“Users”表(列和含义相同)。
  • 物理上: 该表的行存放在不同位置——比如 ID 1–1,000,000 的用户在分片 A,接下来的百万在分片 B。

从应用角度,你希望像对待一个表一样运行查询;底层系统必须决定与哪个(或哪些)分片通信。

不是复制,也不是“买更大的机器”

分片不同于复制。复制是在多节点上创建相同数据的副本,主要用于高可用和读扩展。分片则把数据拆分,使每个节点保存不同的记录。

它也不同于纵向扩展(把数据库放到更大的机器上)。纵向扩展可能更简单,但有实际上限且成本很快攀升。

分片并不会魔法般解决一切

分片扩大容量,但不会自动让你的数据库“变简单”或让每个查询都更快。

  • 关联查询(Joins) 会在相关行分布在不同分片时变得昂贵。
  • 跨分片事务 更难保证;“要么全部成功要么回滚”的更新需要协调。
  • 运维复杂性 增加:路由、重平衡、调试和故障处理都成为系统的一部分。

因此,应把分片理解为扩展存储与吞吐的手段——而非对数据库行为的免费升级。

团队为什么会选择分片:它要解决的问题

分片很少是首选。团队通常在系统成功并触及物理极限后,或运维痛点频繁出现后,才考虑分片。动机不是“我们想要分片”,而是“我们需要在不让单一数据库成为故障点和高成本来源的前提下继续增长”。

促使团队走向分片的痛点

单个数据库节点可能在几种场景下耗尽资源:

  • 存储限制: 表和索引增长,磁盘紧张,备份变慢,维护风险增加。
  • 写入吞吐限制: CPU、WAL/重做日志或锁竞争限制写入每秒。
  • 读吞吐限制: 即便有缓存和副本,某些工作负载仍会压垮主库(或扩展副本成本高)。
  • 嘈杂邻居: 某个租户/客户或工作负载模式霸占资源,影响其他人。

当这些问题经常出现时,往往不是某个坏查询的问题,而是一台机器承载了过多责任。

目标:横向扩展、隔离并控制成本

数据库分片 将数据和流量分散到多台节点,使容量通过增加机器而非单机升级来增长。做到位的话,它还能隔离工作负载(某租户激增不会拖垮其他租户)并通过避免超大高价实例来控制成本

提前的警告信号

常见模式包括在高峰期 p95/p99 延迟上升、复制延迟变长、备份/恢复超出可接受窗口,以及“少量”模式的架构改动变成大事。

为什么分片通常是最后一步

在下定决心之前,团队通常会耗尽更简单的选项:索引和查询修复、缓存、读副本、单库分区、归档旧数据和硬件升级。分片能解决规模问题,但也会增加协调、运维复杂性和新的故障模式——因此门槛应当很高。

核心组成:分片、路由器与元数据

分片数据库不是一件单独的东西——它是若干协作部件的系统。分片之所以“难以推理”,是因为正确性与性能依赖于这些部件如何交互,而不仅仅是数据库引擎本身。

分片:独立的分区(带独立索引)

分片 是数据的子集,通常存放在自己的服务器或集群上。每个分片通常拥有自己的:

  • 存储(数据文件)
  • 索引(使该分片内查询快速)
  • 本地限制(CPU、内存、磁盘、连接数)

从应用角度,分片设置通常尽量看起来像一个逻辑数据库。但在底层,一个在单节点数据库中“单次索引查找”的查询可能变成“先找到正确分片,然后在该分片查找”。

路由器/协调器:请求如何到达正确分片

路由器(有时称为协调器、查询路由器或代理)是交通指挥官。它回答实际问题:给定这个请求,哪个分片应该处理它?

常见两种模式:

  1. 客户端路由: 应用库知道分片映射并直接连接到正确分片。
  2. 代理路由: 应用连接到路由器服务,由路由器转发请求。

路由器能减轻应用复杂性,但若设计不当,也可能成为瓶颈或新的单点故障。

元数据/配置服务:分片映射、所有权与健康

分片依赖元数据——一份描述真相来源的信息:

  • 分片映射(哪个分片拥有哪个范围/哈希桶/ID)
  • 所有权(尤其是在迁移期间,所有权可能临时重叠)
  • 健康与成员信息(哪些节点在线、主/副角色、是否在排空)

这些信息通常存放在配置服务(或小型“控制面”数据库)中。如果元数据过期或不一致,路由器会把流量发到错误位置——即使每个分片本身都健康也会出问题。

后台作业:均衡、迁移与备份

最后,分片依赖一些后台进程让系统长期可用:

  • 重平衡:某个分片增长快于其他分片时需要重新分配数据
  • 迁移:在变更所有权时移动数据
  • 备份/恢复:在多个分片间工作并满足你的恢复目标

这些作业容易在早期被忽视,但它们是许多线上意外的根源——因为它们在系统仍在服务流量时改变系统形态。

选择分片键:第一个重大权衡

分片键 是系统用来决定一行/文档该存放到哪个分片的字段(或字段组合)。这个单一选择会悄悄决定性能、成本,甚至未来哪些功能显得“容易”,因为它控制着请求能否被路由到单一分片还是必须扇出到多个分片。

好的分片键应具备

  • 高基数:很多不同的取值(例如用 user_id 而不是 country)。
  • 均匀分布:值将读写分散到各分片,而不是汇聚到某一处。
  • 稳定的访问模式:它对应你当前最常查询的数据路径,并且大致符合未来数月的查询预期。

一个常见例子是在多租户应用中按 tenant_id 分片:大多数读写请求会局限在该租户所在分片,且租户数量足以分散负载。

坏的分片键及其后果

一些键几乎注定会带来麻烦:

  • 基于时间的单调键(时间戳、自增 ID):新数据集中到“最新”分片,形成写热点。
  • 低基数字段(状态、套餐等级、国家):选项太少导致少数分片承担大部分负载。
  • 可变标识(电子邮件、可变用户名):键变化会迫使在分片间移动数据,代价高且风险大。

即使低基数键在过滤上看似方便,也会把常规查询变成扇出查询,因为匹配的行分布在各处。

真正的权衡:查询便利性 vs 分布质量

用于负载均衡的最佳分片键并不总是对产品查询最友好的分片键。

  • 优化为常见低延迟操作的键(例如 user_id),一些“全局”查询(管理报表)会变慢或需要独立流水线。
  • 优化为报表键(例如 region),你可能会面临热点与容量不均衡。

多数团队会在这个权衡中做出选择:把分片键对准最频繁且延迟敏感的操作,其它需求通过索引、反范式、复制或专门的分析表来解决。

常见分片策略(范围、哈希、目录)

测试跨分片写入
在可弃置的小服务中试验幂等键与重试安全写入。

没有一种“放之四海而皆准”的分片方式。你选的策略会影响路由难易、数据分布的均衡程度,以及哪类访问模式会变得糟糕。

范围分片(Range)

范围分片让每个分片拥有键空间的连续切片,例如:

  • 分片 A:customer_id 1–1,000,000
  • 分片 B:customer_id 1,000,001–2,000,000

路由简单:查看键即可选择分片。

问题是热点:如果新用户总是得到递增 ID,"最近"的分片会成为写瓶颈。范围分片也对增长不均敏感(某个范围热门、另一个范围冷清)。优点是范围查询(例如“10 月 1 日到 10 月 31 日的所有订单”)因数据被物理分组而高效。

哈希分片(Hash)

哈希分片把分片键通过哈希函数映射到分片,这通常能让数据更均匀分布,减少“所有新数据都去最近分片”的问题。

权衡是范围查询代价高。像“ID 在 X 到 Y 之间的客户”这样的查询不再只命中少数分片,而可能触及许多。

实际工程中常用一致性哈希:不是直接按照分片数映射(添加分片会重新分配全部键),而是用哈希环与“虚拟节点”,添加容量只会移动一部分键。

目录/查找分片(Directory/Lookup)

目录分片保存一个显式映射(键 → 分片位置)。这是最灵活的:可以把特定租户放到专用分片、在不移动所有数据的情况下移动某个客户,或支持大小不均的分片。

缺点是额外依赖:目录慢、过期或不可用时,路由会受影响——即便分片本身健康也会出问题。

复合键与子分片

真实系统常常混合方法。复合分片键(例如 tenant_id + user_id)能在隔离租户的同时在租户内部分散负载。子分片类似:先按租户路由,再在该租户的分片组内哈希,以避免单租户撑爆一个分片。

查询如何工作:路由与扇出

分片数据库有两条非常不同的“查询路径”。理解自己处在哪条路径上能解释大多数性能惊讶以及为什么分片有时感觉无法预测。

单分片查询:快速路径

理想结果是把查询路由到恰好一个分片。如果请求包含分片键(或路由器能映射到分片),系统可以直接发送到正确位置。

这就是为什么团队极力让常见读取“感知分片键”。单个分片意味着更少网络跳转、更简单的执行、更少锁争用和更少协调。延迟主要来自数据库真正做的工作,而不是集群在争论谁来做。

扇出读取:并发放大与尾延迟

当查询无法精确路由(例如按非分片键过滤)时,系统可能会把查询广播到许多或所有分片。每个分片本地执行查询,然后路由器(或协调器)合并结果——排序、去重、应用 LIMIT 并合并部分聚合。

这种扇出放大了尾延迟:即便 9 个分片响应迅速,一个慢分片也能拖累整个请求。它也放大了负载:一次用户请求会变成 N 次分片请求。

跨分片连接与聚合

跨分片的 Join 很昂贵,因为原本在数据库内部就能匹配的数据现在必须在分片间传输(或汇集到协调器)。即使简单聚合(COUNT、SUM、GROUP BY)也可能需要两阶段计划:在每个分片上计算部分结果,然后合并。

索引限制:本地索引 vs 全局索引

多数系统默认使用本地索引:每个分片仅索引自己的数据。它们维护成本低,但不利于路由——查询仍可能扇出。

全局索引可以使非分片键也能被精确路由,但会增加写开销、额外协调以及自己的扩展与一致性难题。

写入与跨分片事务

写入是分片把事情从“只是扩展”变为需要重新设计算法的地方。触及一个分片的写入可以快速而简单;跨分片写入则可能缓慢、易失败,并且难以保证正确性。

单分片写入:理想情况

如果每个请求都能路由到恰好一个分片(通常通过分片键),数据库可以使用该分片的常规事务机制。在分片内你能获得原子性和隔离性,大多数运维问题看起来像熟悉的单节点问题——只是重复 N 次。

多分片写入:复杂性激增的地方

当你需要在一次“逻辑操作”中更新多个分片的数据(例如转账、在客户间移动订单或更新存储在别处的聚合值),你就进入了分布式事务领域。

分布式事务困难在于需要跨机器协调,而这些机器可能很慢、分区或重启。两阶段提交类协议增加往返、可能因超时阻塞,并使故障变得不明确:分片 B 在协调器死掉前是否已应用更改?如果客户端重试,会不会导致重复应用?不重试又会丢失写入?

避免跨分片写入的模式

常见做法包括:

  • 数据局部化: 把相关记录放到同一分片(例如把客户相关的一切放在一起)。
  • 请求路由: 确保某个操作由一个分片拥有,把其它分片当作只读输入。
  • 反范式: 复制小的必要数据,使更新无需扇出。

幂等性与重试安全

在分片系统中,重试不可避免。通过使用稳定的操作 ID(例如幂等键)并让数据库记录“已应用”标记,可以把写入设计为幂等。这样,在超时后客户端重试时,第二次尝试可以成为无操作而不是重复计费、重复订单或不一致计数器的来源。

一致性与复制:保持数据正确性

建模多租户分片
构建小型多租户应用,查看 tenant_id 分片如何改变查询。

分片把数据拆到多台机器,但并不消除冗余的需求。复制让一个分片在节点故障时仍可用,但也让“现在的真实状态是什么?”变得更难回答。

每个分片内部的复制

大多数系统在每个分片内做复制:一个主(leader)节点接受写入,若干副本复制这些更改。主故障时会提升副本。副本也可用于减轻读取负载。

代价在于时序:读副本可能滞后几毫秒到几秒。这在用户期望“刚更新就能看到”时很重要。

简明一致性模型

  • 强一致性: 写入成功后,读取会反映该写(通常意味着读主或等待副本确认)。
  • 最终一致性: 系统会收敛,但读取可能暂时返回旧数据。

在分片设置中,通常是“分片内强一致,跨分片弱保证”。

数据分散时的“单一真相”观念

在分片场景下,“单一真相”通常意味着:对任一数据块,存在一个权威写入位置(通常是该分片的 leader)。但从全局看,没有一台机器能瞬时确认所有事物的最新状态。你有许多本地真相,需要通过复制来保持一致。

全局约束:唯一性、外键、计数器

当需要检查的数据位于不同分片时,约束会很棘手:

  • 唯一性(例如用户名): 要在全局范围内保证唯一,可能需要中心索引、专用“约束分片”或应用层的预留工作流。
  • 外键: 若父行和子行在不同分片,数据库无法在不做跨分片协调的情况下轻易强制参照完整性。
  • 计数器(全局总数、顺序 ID): 天然会成为瓶颈。常见修复是每分片预分配范围、批处理或接受近似计数。

这些选择不仅是实现细节——它们定义了你的产品对“正确性”的含义。

无停机重平衡与重分片

重平衡是当现实发生变化时让分片数据库可用的关键操作。数据增长不均、分片键倾斜、添加新节点或淘汰硬件,任何一项都可能让某个分片成为瓶颈——即便原始设计看起来完美。

为什么困难

与单库不同,分片把数据位置编织进路由逻辑。移动数据不仅是复制字节——你在改变查询必须去向的位置。这意味着重平衡不仅是存储问题,也是元数据与客户端行为的问题。

在线迁移模式(复制 → 重叠 → 切换)

大多数团队采用在线工作流以避免停服:

  1. 复制: 在系统在线时把源分片的数据回填到目标分片。
  2. 双写(有时双读): 迁移期间把新改动写到旧/新位置。读可按规则合并,或使用“新者生效”策略。
  3. 切换: 更新分片映射,让路由器/客户端发送到新位置。
  4. 清理: 关闭双写,删除旧副本并回收空间。

分片映射与客户端行为

分片映射变更会在客户端缓存路由决策时成为破坏性事件。良好系统把路由元数据当作配置:对其进行版本化、频繁刷新,并明确客户端在遇到移动键时的行为(重定向、重试或代理)。

需要规划的运维风险

重平衡常常带来临时性能下降(额外写入、缓存失效、后台复制负载)。部分迁移很常见——有些范围先迁移,有些后迁移——因此你需要清晰的可观测性和回滚计划(例如把映射切回并排空双写)在切换前就准备好。

故障模式与在分片系统中的调试

选择分片键
在编码前使用规划模式映射分片键、查询路径和迁移步骤。

分片不仅增加了更多服务器——还增加了更多故障方式和更多排查位置。许多事件并不是“数据库宕机”,而是“某个分片不可用”或“系统无法就数据位置达成一致”。

常见故障模式

常见模版包括:

  • 分片不可用(崩溃、磁盘满、长时间 GC),导致部分用户受影响。
  • 路由器错误路由,常在配置变更或错误部署后发生。若被错误路由,读取可能会静默返回空结果。
  • 元数据陈旧或不一致(例如分片映射在迁移/拆分时不同组件上不一致)。
  • 局部网络问题:路由器与部分分片间超时会表现为“随机”错误并触发重试,进而放大负载。

排查方式的变化

在单节点数据库中,你只需查看一份日志和一组指标。在分片系统中,你需要能跟踪请求跨分片的全链路可观测性。

使用关联 ID并在每个请求中传播,从 API 层经路由器到每个分片。配合分布式追踪,让扇出查询显示出哪个分片慢或失败。度量应按分片细分(延迟、队列深度、错误率),否则热分片会被整体均值掩盖。

数据正确性事故

分片相关的故障常以正确性问题出现:

  • 在重试或非幂等写入后出现重复
  • 迁移将数据移动但路由仍指向旧位置导致丢失行
  • 两个元数据视图都接受对同一键范围的写入导致脑裂写入

备份、恢复与灾备

“恢复数据库”变成“按正确顺序恢复许多部分”。你可能需要先恢复元数据,再逐个分片恢复,并验证分片边界与路由规则与恢复时点一致。灾备计划应包括演练以证明你能把一致性集群重组起来,而不仅仅是恢复单机。

何时不分片:实际替代方案与决策清单

分片常被当作“扩展开关”,但它也会永久增加系统复杂性。如果你能在不跨节点拆分数据的前提下满足性能与可靠性目标,通常会得到更简单的架构、更容易的调试和更少的运维边缘情况。

常见的替代办法(通常能争取大量余量)

在决定分片前,尝试能保留单逻辑数据库的选项:

  • 更好的索引 + 查询优化: 先修复慢查询——缺失索引、无界查询、昂贵 Join 与 N+1 问题。
  • 缓存: 把读密集且稳定的响应放到缓存(应用层缓存、公开内容用 CDN、或热键的内存缓存)。
  • 读副本: 在不改变写路径的情况下卸载读取流量(并接受副本滞后在可接受的情形)。
  • 单节点表分区: 许多数据库支持表分区,能改善维护与查询性能而无需跨节点路由。
  • 归档旧数据

在不做大投入前做原型

一种降低风险的实用办法是先原型化分片相关的“管道”(路由边界、幂等、迁移工作流、可观测性),再决定是否将生产库迁移。举例来说,使用 Koder.ai 可以快速从对话生成一个小型真实服务(如 React 管理界面加 Go 后端与 PostgreSQL),在沙箱中试验分片键感知的 API、幂等键和切换行为,并将代码与运行手册导入主栈。

何时分片合适(何时不合适)

当数据集或写入吞吐明显超过单节点限制 查询模式能可靠地使用分片键(跨分片 Join 少、扇出查询少)时,分片更合适。

当产品需要大量临时查询、频繁跨实体事务、全局唯一约束,或团队无法承担运维工作量(重平衡、重分片、事故响应)时,分片并不适合。

速查决策清单

问自己:

  • 工作负载: 瓶颈是 CPU、I/O、内存还是锁竞争?能否在不分片的情况下解决?
  • 查询模式: 90%+ 的关键查询能否通过分片键路由?
  • 团队能力: 谁负责分片映射、值守 runbook 和跨分片事务?
  • SLO: 能否接受部分降级(某分片不可用)和更长的尾延迟?

为增长而规划,而不仅是画图

即便不立即分片,也要设计迁移路径:选择不会阻碍未来分片键的标识符、避免硬编码单节点假设,并演练如何以最小停机完成数据迁移。计划重分片的最佳时机是在你真正需要之前。

常见问题

什么是数据库分片,它与复制有什么不同?

分片(水平分区)将单一逻辑数据集拆分到多台机器(“分片”)上,每个分片存储不同的行。

相比之下,复制会在多台节点上保留相同数据的副本——主要用于可用性和读扩展。

为什么不直接把数据库升级为更大的机器而非分片?

纵向扩展是升级单台数据库服务器(更多 CPU/RAM/更快磁盘)。这在运维上更简单,但终会遇到物理瓶颈或高昂成本。

分片通过增加机器向外扩展容量,但会带来路由、重平衡和跨分片一致性等挑战。

分片实际解决了哪些问题?

团队在单节点反复成为瓶颈时会采用分片,例如:

  • 磁盘/索引增长导致备份和维护变慢
  • 写入吞吐受 CPU/WAL/锁竞争限制
  • 读流量压垮主库/副本
  • “嘈杂邻居”租户占用资源影响其他人

分片将数据和流量分散到多台节点,使容量可以通过添加节点扩展。

分片数据库系统的核心组件有哪些?

典型的分片系统包含:

  • 分片(Shards): 独立分区,拥有自己的存储和索引
  • 路由器/协调器: 决定应查询哪个分片
  • 元数据/配置服务: 分片映射、所有权、健康状态、成员信息
  • 后台作业: 重平衡、迁移、备份/恢复流程

性能和正确性依赖这些组件保持一致性。

什么是分片键,为什么它如此重要?

分片键是用来决定一行存放到哪个分片的字段(或字段组合)。它决定了请求是命中单个分片(快速)还是必须扇出到多个分片(慢)。

好的分片键通常具有高基数均匀分布,并匹配你的常见访问模式(例如 tenant_iduser_id)。

哪些情况下分片键是“糟糕”的,会造成什么问题?

常见的“坏”分片键包括:

  • 单调/基于时间的键(最新数据聚集到同一分片,形成写热点)
  • 低基数字段(可选值太少 → 负载不均)
  • 可变标识(例如会变化的 email/用户名:变更键会迫使昂贵且有风险的数据迁移)

这些通常会导致热点或把日常查询变成扇出型查询。

范围分片、哈希分片和目录分片有什么区别,什么时候用哪种?

三种常见策略:

  • 范围分片(Range): 路由简单、对范围查询友好;但易形成热点和倾斜
  • 哈希分片(Hash): 数据分布更均匀;范围查询变得昂贵;添加节点需用到一致性哈希等技巧以减少重排
  • 目录/查找分片(Directory): 灵活,可以将特定租户放到专属分片,便于迁移;缺点是引入了对目录服务的依赖以保证路由正确

真实系统通常会混合策略,例如复合键或子分片以同时隔离租户并在租户内分散负载。

为什么有些查询在分片后变慢(扇出/聚合)?

如果请求包含分片键,路由器能把查询发到单个分片(快速路径)。

如果不能精确路由,系统会把查询广播到多个或所有分片(扇出/聚合)。各分片本地执行后,路由器汇总结果——排序、去重、应用 LIMIT 并合并部分聚合。

这种扇出放大了尾延迟:9 个分片都很快,但只要 1 个慢就能阻塞整体请求。它也把一次用户请求变成 N 次分片请求,成倍放大负载。

跨分片的写入和事务是如何处理的?

单分片写入可以使用该分片的本地事务,行为和单节点类似。

跨分片写入需要分布式协调(比如两阶段提交类协议),这会增加延迟并使故障变得模糊不清。

常见缓解:

  • 将相关数据聚合到同一分片(数据局部化)
  • 让操作被某个分片“拥有”,其他分片作为只读输入
  • 通过反范式复制小块数据以避免跨分片更新
  • 使用幂等操作 ID(幂等键)保证重试安全
分片后如何保持数据一致性与复制?

在每个分片内通常有复制:一个主节点接受写入,若干副本复制这些更改。主节点故障时会提升副本。

副本复制带来的时序差异会导致读到落后数据(几毫秒到几秒)。

常见一致性模型(简述):

  • 强一致性: 写成功后随后的读能看到该写(通常需要读主或等待副本确认)
  • 最终一致性: 系统会收敛,但临时读可能返回旧数据

在分片场景下,通常是在“分片内强一致,跨分片弱保证”。

全局约束(唯一性、外键、全局计数器)在跨分片时很难直接强制,需要中心索引、约束分片或应用层预留等方案。

如何在不停机的情况下进行重平衡和重分片?

重平衡/重分片是在现实变化时维持可用性的关键操作:数据增长不均、分片键出现倾斜、添加或下线节点都可能触发它们。

在线迁移常见流程(复制 → 覆盖写/读 → 切换):

  1. 复制: 在存活系统中把数据从源分片回填到目标分片
  2. 双写(有时双读): 迁移期间将新写同时写入旧/新位置;读可按规则合并
  3. 切换: 更新分片映射,让路由器/客户端指向新位置
  4. 清理: 停止双写、移除旧数据、回收空间

要注意客户端缓存路由信息会破坏切换,元数据应有版本并频繁刷新。重平衡会带来临时性能下降,因此需要可观测性和回滚策略。

什么是热点与倾斜,如何发现并缓解?

热点发生在少量键承载大部分流量时(名人账号、热门商品、某租户的批量作业或基于时间的键)。若这些键集中在一个分片,该分片就成瓶颈。

“倾斜”既包括数据倾斜(某分片数据量大)也包括流量倾斜(某分片请求 QPS 高),两者不一定一致。

快速检测方法:按分片监控 p95 延迟、QPS、存储使用量。如果某分片的延迟随 QPS 升高而上升,很可能出现热点。

缓解手段:

  • 选择能均匀分散流量的分片键
  • 对热点键做分桶/加盐(bucketing/salting)
  • 对读热点做缓存
  • 对租户施加配额或限流
  • 将热点分片拆分或迁移热范围
分片系统有哪些常见故障模式,排查时有什么不同?

分片增加了更多失败路径和更多排查点。一些常见故障模式:

  • 某个分片不可用(崩溃、磁盘满、长 GC 暂停),导致部分用户无法使用
  • 路由器错误路由(配置变更或部署出问题),导致读到空结果
  • 元数据陈旧/不一致(分片映射在迁移期间不同组件上不一致)
  • 局部网络问题导致路由器与部分分片通信超时,重试又放大负载

排查方式改变:需要跨分片追踪请求,使用关联 ID 并在路由器到每个分片间传播。度量要按分片细分(延迟、队列长度、错误率),否则热分片会在平均值中被掩盖。

备份/恢复也更复杂:常常需要先恢复元数据,再按正确顺序恢复每个分片,并验证分片边界与路由规则一致。演练 DR 策略很重要。

何时应避免分片?有哪些实际替代方案和决策要点?

在保留单逻辑数据库之前,应先尝试能大量缓解问题的替代方案:

  • 更好的索引与查询优化(先修复慢路径)
  • 缓存(应用层缓存、CDN、内存缓存)
  • 读副本(将读流量卸到副本,接受副本延迟)
  • 单节点的表分区(许多数据库支持)
  • 归档旧数据

去风险的做法是先在安全沙箱里原型化分片相关的管道(路由、幂等、迁移、可观测性),例如用 Koder.ai 快速搭建一个管理界面和后端,演练 cutover 行为并导出代码与 runbook。

分片适合在数据或写入吞吐确实超过单节点且绝大多数关键查询可用分片键路由(最小化跨分片 Join/事务)时采用;若产品需要大量随意查询、频繁跨实体事务或团队无法承担运维负担,则不宜分片。

简单决策清单:

  • 瓶颈是 CPU、I/O、内存还是锁竞争?有没有无需分片即可解决的办法?
  • 90%+ 的关键查询是否能靠分片键路由?
  • 团队是否有能力维护分片映射、值守 runbook 和跨分片事务流程?
  • SLO 是否能接受部分降级(某分片不可用)和更长的尾延迟?

即便暂不分片,也要为未来设计迁移路径:选不妨碍未来分片键的标识符、避免硬编码单节点假设、并预演如何以最小停机做迁移。

Related posts