Juan Pan | Trista 2022-03-14
分库分表是最早用于提升数据库性能的分布式方法之一。而近期的技术创新,使其成为当今最高效的解决方案之一。
如今,数据库正受到前所未有的关注,因为它们管理着企业最重要的资产:数据。仅仅30年前,大多数数据还存储在纸质文件、磁带或某种磁盘上。当时,人均产生的数据量和消费的数据量都远低于今天,因此我们仍能高效地存储、管理和访问这些数据。
然而,如今的数据格局已完全不同。智能手机变得越来越必要且无处不在。随之而来的各类应用程序,使我们在过去15年中所产生的数据量达到了难以想象的程度。这给数据库集群带来了巨大压力——它们必须处理越来越多的流量,一些顶级网站和服务每周甚至要应对数十亿次访问。
那么,当如此庞大的流量涌入数据库集群时,我们该如何应对?
答案可能是分库分表(Sharding)。也许你从未听说过它,或者曾草率地将其视为过时的方案,认为它无法应对现代挑战。分库分表架构听起来或许不如其他方案花哨,也没有那么多“酷炫”的功能,但它确实高效且实用。
最近,这一方法获得了大量创新性的改进,使其能力远超以往(例如分布式SQL技术,就大大简化了分库分表的实现与管理)。或许正因如此,它在寻求可扩展性的区块链公司中越来越受欢迎。
数据库碎片化
数据库已有50多年历史。你可能会觉得这个领域已经没有太多创新空间,但数据库碎片化却是当前科技行业中发展最快的垂直领域之一。现有数据基础设施的复杂性似乎还在不断加剧。
许多现代应用最终会构建在多个、通常是专用的数据库之上。一个单一应用可能同时使用:
- 用于存储和访问内容的关系型数据库(如 PostgreSQL);
- 用于缓存的内存数据库(如 Redis);
- 定制化的时序数据库;
- 用于分析的数据仓库。
试想一下,如果一家企业拥有多个应用、多个部门各自维护自己的系统,甚至涉及不同供应商,这种复杂性将呈指数级增长。
如前所述,数据已成为企业最重要的资产之一。近年来,数据库技术的发展速度明显加快,这无疑与人工智能、机器学习、区块链和云计算等领域的快速发展密切相关。
根据 DB-Engines 的统计,目前有超过 350 种数据库管理系统(DBMS),而未被收录的还有更多。卡内基梅隆大学的《数据库大全》(Database of Databases)则列出了 792 种值得关注的数据库管理系统。
如此庞大的 DBMS 数量,反映出企业在选择数据库时面临的多样化需求:
- 银行或金融机构可能选择 SQL Server 或 PostgreSQL 等关系型数据库,以确保结构化数据的 ACID(原子性、一致性、隔离性、持久性)事务;
- 大型在线多人游戏或需要会话管理的 Web 应用通常偏好 Redis 等键值型 NoSQL 数据库;
- 社交媒体分析公司可能选用图数据库;
- 物联网(IoT)企业则倾向于使用时序数据库来处理传感器或网络数据。
如果你相信“选择越多越好”,那么未来几年你会看到更多新解决方案涌现——既有创新型初创公司推出的新产品,也有老牌数据库厂商对现有产品的升级或全新发布。
可以预见,数据库市场将在短期内进一步碎片化。而这种碎片化也带来了显著挑战,例如不同厂商技术的兼容性、旧系统的适应性以及替换成本等。
为什么你需要分库分表?
传统数据库在面对日益增长的数据量和查询流量时可能力不从心。尽管 NoSQL 和 NewSQL 概念如今非常流行,市场上也涌现出大量受其启发的新数据库产品,但仅靠这些概念本身并不能解决数据爆炸的问题。
分库分表(Sharding) 是一种将数据拆分为多个行或列,并分别存储在不同数据库服务器实例上的技术,从而分散流量负载。每个小型数据表称为一个 分片(shard)。一些 NoSQL 产品(如 Apache HBase 或 MongoDB)本身就支持分片,而 NewSQL 系统则将分片架构内建其中。
让我们聚焦于一种特定的 NewSQL 架构:分库分表如何应对当今的 OLTP(在线事务处理)挑战。
虽然有多种方法可以减轻数据库负载,但分库分表具有以下优势:
- 将数据存储分布到多台机器上;
- 轻松在不同分片间均衡流量负载;
- 显著提升查询性能;
- 无需额外工作即可横向扩展数据库;
- 高效复用并升级传统 DBMS;
- 通过代理支持多租户,允许多个数据库共享单台服务器或云资源。
如何对数据库进行分库分表?
以下是一个基本的工作流程,可帮助你在 DBMS 中实现分库分表。在介绍设置和基础概念后,我们将深入探讨几个关键方面。
实现分片的最佳方法之一是将数据拆分为多个小表,这些小表也称为分区(partitions)。
原始表可以按两种方式拆分:
- 垂直分片(Vertical Sharding):将一个或多个列存储在不同的表中;
- 水平分片(Horizontal Sharding):将一个或多个行存储在不同的表中。
这些表可标记为 VS1(垂直分片1)、HS1(水平分片1)等,数字代表第一个表或第一个模式,依此类推。所有这些子集共同构成原始表的完整模式。
分库分表的两个核心概念是:
- 分片键(Sharding Key):用于确定某一行数据应存储在哪个分片中的特定列值;
- 分片算法(Sharding Algorithm):用于将数据分配到一个或多个分片的算法。
步骤1:分析业务查询与数据分布,确定分片键和分片算法
要确定某一行应存入哪个分片,需将分片算法应用于分片键。不同的分片策略适用于不同场景。常见策略包括:
- MOD(取模):将每第 n 行或列发送到特定分片。例如,MOD 3 算法会将第1、4、7行分配到第一个分片,第2、5、8行到第二个分片,依此类推。
- HASH(哈希):通过计算分片键的哈希值,将数据均匀且随机地分布到各分片。
- RANGE(范围):将特定范围内的行或列分配到单个分片。
- TAG(标签):将匹配特定值的所有行或列分配到指定分片。
例如,若分片键为 “ID”,分片算法为 “ID MOD 2”(即奇偶分离),则数据将按如下方式分布:

因此,你需要设计一个合适的算法来使用分片键。你的分片策略将显著影响查询效率和未来的扩展能力。糟糕的分片算法会导致跨分片冗余计算,最终拖累整体性能。
决定如何分片时,关键要考虑业务查询特征和数据分布情况。虽然每个数据库都有其独特因素,但我们可以通过一些典型场景说明优秀分片算法如何高效分配数据。
RANGE(范围分片)
例如,在对包含时间戳的日志详情表进行分片时,推荐使用 RANGE 算法,并以创建日期作为分片键。原因在于,人们通常只查询特定时间范围内的记录。
但使用日期时间作为 RANGE 键也可能带来问题:历史记录很少被更新,而近期记录频繁被查询和修改,导致大部分请求都集中在存储最新数据的分片上,造成热点竞争。
MOD(取模分片)
MOD 算法则能有效避免这种竞争。它通过 shardingKey MOD shardCount 将最新行分散到不同分片,从而使新查询也分散到不同分片,避免热点。如果分片键是字符串(且可能敏感),可先用 HASH 算法生成数值,再用于 MOD 分配。
TAG(标签分片)
有时你可能希望根据单元格的值进行分片,这时应使用 TAG 算法。例如,为遵守 GDPR 法规,你希望将所有欧盟用户数据存储在欧盟境内的服务器上。若使用 TAG 分片算法,可将带有“EU”标签的行定向到位于欧盟的特定分片。此时,查询 SELECT COUNT(*) FROM registrant_table WHERE region = "EU" 只需从 EU 分片返回 COUNT(*),而无需在整个分布式系统中执行聚合计算。
没有放之四海而皆准的方案。为获得最佳性能,请花时间深入分析你的具体业务场景。若想快速上手,大多数分布式分片数据库系统会默认采用适用于多数场景的通用策略。

步骤2:迁移现有数据
如果你决定实施分片,其实不必一次性迁移所有历史数据。这样做会面临以下挑战:
- 如何在业务 24/7 运行的同时进行分片;
- 如何将增量数据同步到新的分片集群;
- 如何比对原数据库与新分片集群的数据一致性;
- 如何选择最佳时机切换流量。
但如果你确实需要迁移历史数据,传统做法如下:
- 首先,通过分片算法将历史数据分区并导入新的分片集群。建议使用自动化程序执行所有必要的 SQL 操作。
- 其次,运行一个平台或程序,解析数据库日志,捕获分片过程中的变更,并将这些增量数据应用到新集群。
- 第三,选择合适的数据校验策略。你可以选择高精度(逐行比对)、快速(仅比对行数)或折中方案(如 CRC32 校验)。逐行比对最准确但耗时,仅比对总量最快但精度低。
步骤3:将流量切换到新集群
假设上述步骤顺利完成,下一步就是将线上流量切换到新的分片集群。此操作应在数据库不可写入的时段进行(例如业务低峰期),以确保两个数据集的一致性,并允许只读查询继续运行。
在此期间,应禁止所有更新请求以保障分布式数据一致性,但允许查询操作,因为它们不会改变系统状态。

整个流程看似简单,但每一步都可能充满挑战。自动化切换可最大限度减少停机时间,且操作时务必谨慎,因为你处理的是宝贵的数据。
好消息是,你并非第一个面对这些挑战的人。开源项目让我们能站在巨人的肩膀上。
例如,Apache ShardingSphere(笔者是其贡献者之一)就将整个分片流程作为核心能力之一。它提供多种分片策略、数据迁移、重新分片及分片管理功能,并具备高级特性以解决下文提到的问题。此外,它拥有活跃的社区,意味着你遇到的大多数问题都已有解决方案。
什么是“好的”分库分表?
你现在已了解分片的工作流程和必要步骤,但“好的”分片应该是什么样子?
抛开边缘理论和特定场景的特殊需求不谈,好的分片通常具备六大特质:
- 易于设置和理解(便于 DBA 交接);
- 高可用性;
- 弹性扩缩容能力;
- 高性能的分布式系统表现;
- 可观测性;
- 低迁移开销。
这六项特质代表了理想分片的标准,但也取决于你所选择的分片客户端。
分库分表与复制(Replication)结合使用
除了上述核心流程,你还应了解以下内容,因为数据库场景多样,且随着应用规模扩大,需求也会变化。
另一种提升数据库性能和可扩展性的方法是复制(Replication)。复制会创建独立运行的数据库节点副本,写入一个节点的数据会被同步到其他副本。
通常,无论是专业团队还是个人开发者,都希望从数据库中榨取最高可用性和性能。然而,分片与复制结合的架构可能导致复杂的数据库管理和路由策略。
想象每个分片都有多个副本节点,结果可能如下图所示。如果每个主节点有多个副本,应用访问将变得更加复杂。

那么,分片与复制有何区别?
- 分片是将大表拆分为多个小表(分片),数据分布在不同服务器上,以实现可扩展性;
- 复制则是创建原始表的完整副本,以提升系统可用性。
两者优势互补,因此混合使用分片与复制十分常见。
如下图所示,用户可能将海量数据的数据库分片到 P1、P2、P3 三台服务器上,每个查询也被路由到对应分片,从而提升整个系统的 TPS/QPS。但如果其中一个分片宕机,系统可用性将降至 2/3,且恢复耗时,可能造成严重损失。
为提高可用性,可在每个分片(P1、P2、P3)上配置副本(R1、R2、R3)。当 P1 不可用时,其副本 R1 可自动提升为主节点继续服务,从而最小化业务中断和损失。
这种设计虽好,但其拓扑结构会使应用访问变得复杂。假设每个主节点有两个副本,那么由 P1/P2/P3 及其六个副本组成的网络将让开发者困惑:该访问哪个主节点?如何访问副本?如何在副本间做负载均衡?主节点故障时谁来重路由查询?
开发者的职责本应聚焦于业务逻辑,而非陷入复杂的数据库拓扑管理。
如何向应用隐藏这种复杂性?
通常有两种客户端或访问模式可供选择,外加一种新兴的“附加”模式:
- 专用数据库驱动:在应用端集成智能驱动,自动处理分片和路由;
- 代理模式:应用连接到一个代理,由代理负责数据路由;
- Sidecar 模式(新兴):源自服务网格(Service Mesh),即为每个服务部署一个伴随的代理实例,处理通信、监控等任务,如同摩托车旁的边车。
无论采用专用驱动还是代理,它们都会对外表现为单一数据库服务器,帮助用户管理整个数据库集群。这样,应用无需感知底层复杂的拓扑结构,也无需为适配新架构而重构代码。

结论与趋势
分库分表是应对网络化应用演进所带来新挑战的有效手段之一。其他方案还包括 DBaaS(数据库即服务)、新型数据库架构,或简单粗暴地增加数据库数量。
本文希望能为你介绍分库分表架构,或如果你曾因其“过时”而忽视它,现在或许可以重新审视。
事实上,我不喜欢用“潮流”(fashion)一词来形容技术,因为它暗示着短暂易逝——今日流行,明日即弃。虽然技术领域确实如此,但我更倾向于根据实用性、效率和特定场景下的成本优势来评判一项技术。
总之,拥抱新趋势固然重要,但也不要忘记:有时,那些成熟、稳定、久经考验的技术,恰恰是当前问题的最佳解。