DDIA CH 7 — 分片 Sharding (上)
2026年10月21日
我们在 DDIA 导读 CH 6 — 复制 (上) 中谈到,在分布式数据库中,可以通过在多个节点上复制数据,来把数据分散到多台机器上。复制能够提高系统的可用性,避免单台机器故障导致整个系统不可用;同时也能将不同的读请求分散到不同的副本上,借此减轻单台机器的压力。
除了复制之外,分片 (Sharding) 也是把数据分散到不同机器上的方法。不过,分片和复制在实现方式与解决的问题上都有所不同。在这一章的导读中,我们会讨论分片,包括分片是什么、使用时有哪些利弊,以及实现上有哪些不同的选择。
分片是什么?
之前在 DDIA 导读 CH 6 — 复制 (上) 中提过,复制是在多台机器中存储「相同的数据」;相比之下,分片是在多台机器中存储「不同的数据」。换句话说,假如一个数据库中有多条数据,分片并不是在每台机器上都存放完整的数据,而是把数据切成许多小块(所以叫分片),再将不同的小块放到不同的机器上。
举例来说,假如某个会员系统中有上亿名会员,我们可以对会员数据进行分片,例如将会员编号 1 到 10,000,000 放在分片 1,将会员编号 10,000,001 到 20,000,000 放到分片 2,以此类推。按照这样的规则,会员 12,345,678 就属于分片 2。系统如果要查询这个会员,就不用去每台机器上找数据,直接访问分片 2 即可。
在实际应用中,复制和分片并不互斥,更常见的做法是将两者结合使用。一条数据会被分配到某个分片,同时通过复制,将这个分片复制到多台机器上。这样,一条数据仍属于一个分片,但可以通过多个副本来提高可用性。因此,虽然多台机器的磁盘上都有这条数据,但从系统管理数据范围的角度来看,它只属于一个分片,而不是多个分片。
以上面的例子来说,如果分片 2 只放在一台机器上,当这台机器故障时,分片 2 的数据就会暂时不可用。但如果结合复制,那么在某台机器故障时,其他持有相同分片副本的机器仍能继续处理客户端的请求。
分片有哪些好处与成本?
同样是把数据分散到多台机器上,复制可以提高系统的可用性,也能够降低延迟,而分片发挥着不同的作用。具体来说,分片给系统带来的最大好处是可扩展性 (scalability)。
当一个系统持续发展、数据量越来越大时,通常有两种扩展方式:垂直扩展和水平扩展。垂直扩展是持续升级单台机器,例如增加内存、使用容量更大的磁盘。然而,单台机器的磁盘容量终究有上限,所以在数据量极大的情况下,即使是配置最高的机器,也可能无法满足需求。
相比垂直扩展,水平扩展是增加机器数量,而分片就属于水平扩展的方式。当数据量增加时,可以将数据拆分成多个分片,让不同机器各自负责一部分数据,借此实现扩展。当数据量或负载持续增加时,可以通过增加节点、拆分或迁移分片来扩充容量与处理能力。
每个系统都应该使用分片吗?
虽然从理论上来说,分片能够带来扩展方面的好处,但在实际应用中,并不推荐所有数据系统都进行分片,尤其是仍处于早期、规模较小的系统。
之前讨论的复制,无论数据量大小,都能带来可用性上的好处。因为即使数据量很小,机器仍然可能故障;配合适当的故障检测与切换机制,副本可以提高系统在节点故障时持续提供服务的能力。但分片不同。对于数据量和读写负载都还在单节点能力范围内、也没有租户隔离等特殊需求的系统,分片带来的收益通常有限,未必值得承担额外的开发与运维成本。
尤其是随着硬件不断发展,单台机器能够承受的负载上限已经比过去高了很多。因此,除非数据量或写入量已经明显超出单节点能力,而且未来还会持续增长,否则不一定需要采用分片。
分片有哪些实际困难与成本?
具体来说,实际实施分片存在一些难点,第一个就是选择什么分区键 (partition key)。
分区键是系统用来判断「这条数据应该放在哪个分片」的字段或字段组合,例如可以使用会员编号作为分区键。分区键的选择会显著影响后续查询的速度。如果查询提供了分区键,系统就更容易快速定位数据。以上面的例子来说,知道会员 12,345,678 属于分片 2,就可以直接到分片 2 获取数据。
但问题在于,很多查询条件与分片方式并不匹配,这就可能导致查询时必须扫描大量分片,让查询效率和速度大幅下降。尤其是在关系型数据模型下,如果同时存在用户、订单、商品、付款等不同数据及其关联,当这些数据分散在不同分片中时,查询成本就可能增加。
如果订单数据也按照会员编号分片,查询某个会员的订单时,就可以把请求定位到对应的分片,避免跨分片查询。但如果要查询某个商品在整个平台的购买记录,在没有可用的全局索引或其他查询结构时,就可能需要向多个分片发出查询,再汇总结果,从而增加查询成本。反过来,如果按照商品编号分片,查询商品相关数据就会很快,但查询某个用户的完整购买记录,成本就可能很高。
因此,从实际应用的角度来看,分片的第一个难点往往不在代码本身,而在于对系统的理解。需要先了解系统中数据之间的关联、通常执行哪些查询、哪些查询最重要,以及哪些查询频率较低。有了这些全面的了解,才有可能选出合适的分区键。
分片的第二个难点在于处理数据库事务。在单个数据库中,事务相对容易处理;但分片之后,假如 A 账户和 B 账户位于不同分片,从 A 向 B 转账就会成为跨分片操作,比在单个数据库中更慢、更复杂。
例如,要保证 A 账户的扣款与 B 账户的入账具有原子性,就需要跨分片的事务协调。以两阶段提交为例,协调者需要先确认各参与分片已准备就绪,再决定提交或中止;遇到节点故障或通信中断时,恢复处理也会更加复杂。
因此,在实际选择分区键时,也会考虑哪些数据经常需要一起更新,并以此为依据来选择分区键,减少跨分片事务的频率。
加入 E+ 会员方案
如果你觉得这篇分享有帮助,想阅读更深入的内容,欢迎加入 E+ 会员。除了有会员专属的深度文与影片,探讨前后端开发、AI 工程、职业发展,同时有 Discord 社群一同交流与成长。
许多 E+ 会员通过所任职公司的教育训练补助 (E+ 会开立有统编的发票),不用花自己的钱,也能每周通过深度主题文与社群交流,拓展技术视野、加速职业成长。