
上个月的一天下午我正开着会运维群里突然炸了锅——订单库的CPU使用率直接冲到98%慢查询日志每分钟刷出几十条连接数眼看就要被打满。第一反应是又有人写了烂SQL可查了一圈发现一堆走了索引的查询也慢得离谱。那一刻我心里清楚单表数据量已经涨到1.5亿行MySQL有点扛不住了。数据库分片Sharding这件事终于绕不过去了。这篇文章想把分表、分库、分片、分区这几个概念一次讲透同时把我自己做分片时的选型思路、踩坑记录和迁移过程完整分享出来。不搞教科书式的那套全是实际干过之后的心得。不管你是正在评估要不要分片的架构师还是已经被老板逼着赶紧把库拆了的后端开发这篇文章应该对你有用。1. 先说清楚分表、分库、分片、分区到底是不是一回事很多人一上来就分不清这四个词。面试的时候我也经常问候选人结果十有八九是混着说的。这里先花点篇幅把事情说透因为概念不清后面的方案一定是一团浆糊。1.1 分表是单库内部的拆分分表指的是在同一个数据库实例里把一张结构相同的大表拆成多张小表。比如订单表 orders 拆成 orders_0、orders_1、orders_2……一直到 orders_15每张表的结构完全一样只是数据范围不同。这样做解决的核心问题是单表数据量过大导致索引体积膨胀、B树层级变深、查询效率下降。拆完之后每张表的数据量只有原来的十六分之一索引体积小了同样一条SQL的扫描范围小了一个数量级性能自然就回来了。但要注意分表没有解决数据库实例本身的IO压力。所有表还在同一个实例上磁盘、内存、CPU是共享的。如果瓶颈在资源层面只分表是不够的。1.2 分库是实例层面的拆分分库是把数据拆分到多个独立的数据库实例上。可以是一台机器上多个实例也可以是分布在多台机器上。比如把用户库、订单库、商品库拆开部署也可以把订单数据按规则分到订单库1、订单库2、订单库3。分库解决的核心问题是分散单实例的资源瓶颈。读写并发高的时候一个实例的连接数、IO能力、内存都有限拆到多个实例后每个实例的压力就降下来了。这也是为什么很多互联网公司的核心库都是分库 分表一起做因为单表数据量和实例并发压力往往是同时爆发的。1.3 分片是全局视角的统称分片Sharding是一个更大的概念。它描述的是一种数据水平拆分机制把一份完整的数据集按照某种规则分片键 分片策略拆散到多个节点上。节点可以理解为库或者表核心是数据被分散存储了。所以你可以把分片理解为分布式的数据水平切分。分库、分表都是分片的表现形式。分库是跨实例分片分表是实例内分片。实际讨论中大家说的分库分表其实就是Sharding在MySQL场景下的落地形态。1.4 分区是存储引擎内部的切分分区Partition跟前面三个容易混淆因为它在数据库内部实现应用层无感知。MySQL的 InnoDB 支持表分区比如 RANGE 分区、LIST 分区、HASH 分区。一张表在逻辑上还是一张表但底层存储被拆成了多个分区文件查询时优化器能走分区裁剪Partition Pruning只扫描命中的分区。举个例子一单日志表按月份做RANGE分区查上个月的日志时只会去对应分区的物理文件里找不会扫整张表。很多人问有了分区是不是就不用分表了答案是不一定。分区确实能解决单表数据量过大的部分问题但它受限于单个实例的资源。当并发写入量持续走高实例本身成为瓶颈时分区也救不了你。另外MySQL分区功能在实际使用中有不少限制比如分区键必须包含在主键和唯一键里用起来并没有想象中那么顺手。概念拆分粒度应用是否感知主要解决什么问题核心技术点分表同库内多张表感知SQL路由单表数据量过大、索引膨胀表名替换、路由分库多个实例感知数据源路由单实例并发与资源瓶颈多数据源管理、事务边界分片跨节点水平拆分感知或不感知数据容量与吞吐的整体扩展分片键、一致性哈希、扩容分区单表内物理文件不感知单表查询性能优化分区裁剪、分区键限制这里我有一个很直观的类比分库分表相当于把一个大仓库拆成好几个独立的仓库每个仓库有自己的货架和出入口分区则是在一个仓库里隔出不同的货物区仓库还是那个仓库门还是那个门。这个区别想清楚了后面做技术选型时思路就顺了。2. 什么信号出现时你才真的需要分片不是所有表都需要分片。网上流传的单表超过2000万行就要分表说实话太绝对了。我在实际项目里遇到过单表8000万行、查询依然很快的情况也遇到过单表300万行就卡得不行的场景。数据量只是表象真正的信号是性能和运维指标同时亮红灯。2.1 我在项目里看到的性能拐点就拿我维护的订单库来说数据量从2000万涨到1.5亿的过程中有几个非常明显的阶段第一阶段单表不到3000万行主键查询、带订单号的精确查询基本都在毫秒级普通索引查询也还能接受。这个阶段完全不需要分片做好索引、优化SQL就够了。第二阶段数据量到6000万行左右出现了一个明显的性能拐点。部分范围查询、统计类SQL开始变慢尤其是用了非索引字段做筛选的查询全表扫描的代价已经非常大了。另外每天晚上做全量备份的时间从半小时拉长到接近两个小时。第三阶段数据量超过1亿行情况急转直下。即使走了索引因为索引B树的层级加深随机IO次数增加查询耗时也在成倍增长。更麻烦的是高频写入带来的行锁竞争、undo log膨胀、binlog量暴涨开始影响整个实例上其他业务表的稳定性。这个经历告诉我判断要不要分片不能只看行数要结合实例负载、写入并发、查询模式、备份窗口这些因素一起看。2.2 我做分片决策时看的三类指标第一类是容量指标。单库磁盘使用率持续超过70%并且保持增长趋势单表的数据量已经让日常DDL变得难以忍受——比如给一张一亿行的表加索引可能要跑几个小时而且过程中还有锁表风险这个很要命。第二类是性能指标。慢查询数量持续增加尤其是那种明明走了索引但还是慢的SQL出现频率升高数据库连接数频繁打满主从同步延迟经常超过秒级。这些信号说明单实例的吞吐能力已经逼近天花板。第三类是业务指标。核心实体的数据增长速度是线性的还是指数的未来一年预计容量会翻几倍。如果一个订单系统未来一年要承载3倍的数据量那现在就该设计分片而不是等数据涨上来再做。2.3 一个容易被忽略的分片时机还有一种情况当前数据量不大但提前做了分片——那就是业务模型决定了某个实体的访问是天然隔离的。比如SaaS系统里不同租户的数据本来就互不往来按租户ID分片就是顺水推舟的事。这种决策更多是架构层面的预判不是为了解决眼前的性能问题。但这里要提醒一句过早分片的代价也不小。分片带来的复杂度是实打实的跨节点查询、分布式事务、数据迁移、运维监控每一项都会消耗团队大量精力。如果业务还没到那个阶段不妨用下面第7章讲的替代方案先顶着但要在代码层面预留好分片改造空间别把路走死。3. 分片键选不好后面全是坑核心策略拆解如果说分片是个坑那第一个坑就是分片键的选择。分片键选错了后面所有操作都别扭查询绕路、数据倾斜、扩容困难每一样都是麻烦。这一章我把分片策略和分片键选择放在一起讲因为两者是绑定关系。3.1 分片算法取模、一致性哈希、范围分片取模是常用方式也是最容易懂的方式。分片键的值对分片总数取模得到一个固定的分片编号。比如 orders_id 对16取模就能均匀地分到16个分片中。优点是简单、数据分布均匀、实现成本低缺点是扩容时几乎要迁移全部数据。原来16个分片扩到32个分片取模基数变了绝大多数数据都要搬家。一致性哈希是解决扩容问题的思路。把分片节点映射到一个哈希环上数据按哈希值顺时针找最近的节点。扩容时只需迁移少量数据但带来的新问题是如果虚拟节点设计得不好数据分布可能不够均匀而且排查数据归属时需要多一步哈希计算运维和理解成本更高。范围分片是按分片键的值区间来切分。比如按订单创建时间的月份分片1月的数据放 shard_12月的数据放 shard_2。优点是天然支持范围查询局部性很好缺点是容易产生热点。比如大促月的数据量可能是平时的好几倍某些分片会特别热出现数据倾斜。3.2 分片键选择的业务约束分片键绝不是拍脑袋定的它要满足几个条件。第一分片键必须覆盖95%以上的核心查询条件。如果订单系统的高频查询是查某个用户的订单列表那 user_id 就是合适的分片键如果高频查询是查某个订单的详情那 order_id 更合适。两头都想要怎么办要么用映射关系要么冗余数据后面我会细说。第二分片键的值必须稳定。一个订单所属的用户不会变所以 user_id、order_id 这类字段是好的分片键。但商家名称这种可能改名的字段就不能当分片键否则改名等于数据要重新分布。第三分片键不能是随机值。UUID 这类随机分布的值虽然能让数据均匀分散但会彻底摧毁范围查询能力在实际业务中很少直接当分片键用。3.3 我常用的分片策略组合不同业务场景有不同的组合方式我整理了一张选型表场景推荐分片键分片策略原因订单中心user_id取模或一致性哈希用户订单列表是核心查询订单明细/按订单查order_id取模精确查询居多数据均匀日志流水时间字段RANGE分片天然适合按时间归档和清理SaaS多租户tenant_id一致性哈希租户间数据隔离消息/会话记录会话ID取模单会话内部连续访问均匀分散这里补一个很重要的实操细节如果订单系统的核心查询既有按用户查列表又有按订单号查详情一个分片键覆盖不了怎么办我用的方案是订单号生成时就把用户信息编码进去。具体做法订单号的前几位包含用户ID的哈希分片信息这样拿到订单号就能直接定位分片不用再走映射表。另一种做法是维护一张订单号到用户ID的映射表但这样多了一次查询开销而且映射表本身也可能成为瓶颈。我更推荐第一种把路由信息直接编码进业务主键里。3.4 一致性哈希的虚拟节点问题如果你选了一致性哈希一定要注意虚拟节点的配置。没有虚拟节点的话节点数量少时数据分布可能严重不均匀。我曾在一个项目里看到5个物理节点的一致性哈希数据最多的节点比最少的多了接近3倍就是因为哈希环上的位置过于集中。解决办法是每个物理节点配置上百个虚拟节点让它们在哈希环上均匀分布。像 128 个虚拟节点就是一个常用的配置起点。不过虚拟节点太密也会增加管理复杂度建议先做数据分布模拟实验别拍脑袋定数量。4. 分片中间件选型ShardingSphere还是MyCat分片方案确定后面临的第二个大问题是用什么工具落地。完全自己写路由逻辑也不是不行我早期就干过但维护成本太高。后来我用过 ShardingSphere也调研过 MyCat、Vitess这里把我的选型思路和实际体验分享出来。4.1 客户端模式与代理模式的本质区别分片中间件主要有两种形态客户端模式Client-Side和代理模式Proxy-Side。ShardingSphere-JDBC 是典型的客户端模式。它以 jar 包的形式嵌入到应用里应用直接连接各个分片数据库SQL由中间件解析、路由、改写、归并。优点是性能损耗小、部署简单不需要额外维护一个中间件集群缺点是只支持特定语言Java生态最好而且每个应用都要集成一遍。ShardingSphere-Proxy 是代理模式。它在应用和数据库之间加了一层代理服务应用连接的是代理层由代理去连接背后的分片库。优点是语言无关任何客户端只要有MySQL协议都能连缺点是多一跳网络开销代理自身的高可用和性能也会成为新的关注点。MyCat 也是代理模式早期在Java圈子里很流行基于拦截SQL并解析的方式实现路由。但是ShardingSphere生态起来之后,MyCat在社区活跃度和内核能力上的差距逐渐显现我现在已经不太推荐新项目用了。4.2 我给出的选型建议我自己的习惯是Java应用首选 ShardingSphere-JDBC性能好、功能全分布式事务、读写分离、数据加密这些都能支持如果是异构系统比如多个语言都要访问分片数据或者不想改应用代码那就用 ShardingSphere-Proxy。有人问为什么不用VitessVitess 本身很优秀目前主要在Kubernetes化的大规模数据库场景下有明显的优势比如云原生数据库平台、跨机房部署等。但对于很多中小团队来说Vitess 的部署和运维门槛偏高除非你已经深度拥抱Kubernetes否则不必一开始就上这个级别。为了让你更直观地对比我给一张我做过多次评估的表格对比项ShardingSphere-JDBCShardingSphere-ProxyMyCat部署模式客户端内嵌独立代理独立代理支持语言Java优先任意MySQL客户端任意MySQL客户端性能损耗低中中分布式事务XA、Seata集成支持有限支持运维成本低高代理集群中社区活跃度非常活跃非常活跃一般适用场景Java服务直接分片多语言、不改代码老项目遗留4.3 ShardingSphere-JDBC接入的最小配置我以订单表按 user_id 取模分片为例给你写一个最简配置方便你快速理解整个接入过程。当然这只是一个示例配置实际使用中还要根据自己的场景调整。# shardingsphere-jdbc 分片配置示例 dataSources: ds0: url: jdbc:mysql://192.168.1.10:3306/order_db username: root password: 123456 ds1: url: jdbc:mysql://192.168.1.11:3306/order_db username: root password: 123456 rules: - !SHARDING tables: t_order: actualDataNodes: ds${0..1}.t_order_${0..15} tableStrategy: standard: shardingColumn: user_id shardingAlgorithmName: order_table_inline databaseStrategy: standard: shardingColumn: user_id shardingAlgorithmName: order_db_inline shardingAlgorithms: order_db_inline: type: INLINE props: algorithm-expression: ds${user_id % 2} order_table_inline: type: INLINE props: algorithm-expression: t_order_${user_id % 16}这段配置的意思是先根据 user_id 对2取模决定数据进哪个库再对16取模决定进哪张表总共32个物理分片。实际使用中要格外注意 INLINE 策略不能做跨分片JOIN和子查询ShardingSphere 的官网文档里写得很清楚配置前值得花时间好好看看。4.4 选型时最容易忽略的三个点第一个是驱动版本兼容性。ShardingSphere-JDBC 对 MySQL 驱动的版本有要求版本不对会出现一些诡异问题比如时间类型解析异常所以接入前先看支持的版本范围最好在测试环境把驱动版本也一起固定下来。第二个是SQL兼容性。分片后不是所有SQL都能透明执行。带 LIMIT 深度分页的 SQL 会消耗性能复杂的多表关联跨分片JOIN可能直接不支持子查询有时候会走全分片路由实际效果可能比不分片更差。接手项目前先把核心服务的多条查询SQL在测试环境跑一遍心里有数。第三个是连接池配置。客户端模式下应用要同时保持到多个分片库的连接。如果分片库有32个每个应用实例的连接池又配了50个连接那数据库端看到的连接数就是32乘以50再乘以实例数很容易把数据库的连接限制打爆。我后来把每个分片库的最小空闲连接调低最大连接数也做了限制才把这个隐患压住。5. 分片落地后那几件让人头疼的事分片真正上线后麻烦才刚刚开始。分布式ID、跨节点查询、分布式事务、唯一性约束每一件都跟单库时代完全不同。这里挑几个让我印象最深的展开讲。5.1 分布式ID全局唯一主键怎么设计单库时代可以用自增主键分片后每个分片各自自增一定会有重复所以必须引入全局唯一ID方案。我用的主流方案有两种第一种是雪花算法Snowflake。64位整数包含时间戳、机器ID、序列号性能极高趋势递增。缺点是依赖机器时钟如果时钟回拨可能会生成重复ID所以要在代码里做时钟回拨保护。网上有很多改良版本比如百度UidGenerator、美团的Leaf都是基于雪花思路做的建议直接参考这些项目的实现别自己从零造轮子。第二种是号段模式。数据库维护一个号段表应用批量获取一批ID段比如每次拿1000个号在本地生成全局唯一ID。优点是简单、可控、没有时钟回拨问题缺点是需要额外维护号段表而且在号段用完之前需要提前申请下一批否则会出现ID生成阻塞。我给一个比较稳的方案核心业务表使用雪花算法生成ID但把分片路由信息编码进ID里比如用ID的中间几位表示分片编号这样拿到ID就能直接计算出数据在哪个分片非常好用。5.2 跨节点查询与分页没那么简单分片后最大的痛点是查询。如果你的 SQL 没有带分片键条件中间件就只能把SQL广播到所有分片执行然后把结果合并。比如你想查最近一个月下单量排名前100的用户这条SQL本身没有指定 user_id那就得在32个分片上都执行一遍再合并排序。这个场景我的处理思路是核心的跨分片查询尽量降级为单分片查询实在避免不了就把结果预计算好存起来。像排行榜、统计报表这类数据用定时任务提前算好写进结果表查询时只查结果表彻底绕开跨分片问题。分页也是重灾区。分片后LIMIT 10000, 20 这种深度分页会把每个分片的前10020条数据都捞出来然后在中间件内存里合并排序再取第10000条起的20条。数据量一大内存直接爆掉。解决办法有三个方向禁止深度分页改为下一页模式用上一页的最后一条记录ID作为查询条件如果必须跳页可以限定只能查询前N页使用 Elasticsearch 这类外部搜索引擎把分页查询交给搜索引擎做。5.3 分布式事务不是所有场景都需要强一致分片之后原来在一个库里的本地事务变成跨库分布式事务。经典的例子下单要扣库存、加订单、减余额如果订单在分片1库存和余额在分片2那一个操作就涉及到两个分片的数据变更无法用本地事务解决。分布式事务方案我按场景分三类第一类是要求强一致的场景用 XA 协议或 Seata AT 模式。但XA的性能损耗偏高高并发场景下要慎重使用。Seata 的AT模式做了不少优化Spring Cloud Alibaba 生态里接入也方便适合对一致性要求高的核心链路但我依然强烈建议尽量把数据放在同一个分片用本地事务解决。第二类是追求最终一致性的场景用本地消息表 消息队列。做法是在本地事务里写业务数据和消息表提交事务后再异步发送消息给下游模块下游消费消息后做自己的业务靠MQ的重试机制保证最终成功。这个方案性能好是很多场景下性价比最高的选择。第三类是纯异步补偿TCC、Saga模式。适合长流程业务比如聚合支付、跨组织协作。但TCC侵入性强需要业务方写 Confirm 和 Cancel 逻辑编码量很大只推荐在确实必要的核心交易链路用。我的经验是能通过合理的分片键设计把需要强一致的数据放在同一个分片就别引入分布式事务。比如按用户维度分片时把用户信息、订单、积分都放在用户ID对应的分片里那么一个用户的所有操作始终落在同一个分片事务还是本地事务省去一堆麻烦。5.4 唯一性约束的破局单表能建唯一索引分片后跨分片唯一约束就比较难做。比如手机号、身份证号、用户昵称这类需要全局唯一的字段。思路有三条第一条唯一字段本身包含分片信息。比如手机号不是分片键的话可以为手机号建一张路由表手机号的哈希对应到特定分片通过单分片唯一索引保证全局唯一。第二条用分布式组件实现唯一性校验。比如用Redis的SETNX做唯一性检查用ZooKeeper做分布式锁。但前提是这些组件自身的高可用要做好否则会引入新的单点。第三条如果并发量不大定期在在数据库里做全局唯一性扫描。这个方案最朴素适合对实时性要求不高场景比如清理重复昵称。实际项目中我最常用的是第一条把需要全局唯一的业务字段作为分片键或者在写入时通过映射关系定位到固定的分片用数据库自身的唯一索引来保证一致性。不管用哪种方案测试阶段都要专门准备一套用例去压这个唯一性约束否则线上最容易出数据问题。6. 数据迁移与灰度切换的实操记录做好分片设计、中间件选型不等于就万事大吉。老系统上分片最难的其实是数据迁移和切换一不小心就是线上事故。这里我以订单表从单库迁移到32个分片为例说说我走的完整流程。6.1 双写新旧两套并行写入迁移过程中最怕的是丢数据或数据错乱。所以第一步不是直接切流量而是先做双写。应用层在业务代码里同时写入旧库单表和新分片库新分片库按照提前设计好的分片规则路由到对应的分片表。但双写也有陷阱写入新分片库失败怎么办如果双写失败直接返回错误影响主链路如果异步补偿又可能造成新旧库数据不一致。我采用的是同步双写 失败重试 日志告警的组合。同步双写失败不阻塞主流程但会记录日志并告警随后由补偿任务定时扫描失败记录重新同步。这里要给一个血泪经验双写期间一定不要改分片规则。一旦分片规则变了之前写入的新库数据跟旧库对不上后续校验怎么做都是乱的。分片规则一旦定下来上线之前要当作不可变配置对待。6.2 全量 增量同步双写只覆盖切换时刻之后的新增数据历史存量数据需要先同步到新分片库。我用的是全量 增量结合的方式。全量同步比较容易写脚本按主键范围分批读取旧库数据再按分片规则写入新库。但要控制每次读取范围不要一把梭把内存撑爆。我一般按5000行一个批次边读边写同时记录批次进度方便断点续跑。增量同步可以用 binlog 订阅。旧库的数据库开启 binlog用 Canal 订阅变更日志将每一条 insert、update、delete 事件应用到新分片库。这样就保证全量同步期间产生的新增数据不会丢。Canal 应用的延迟要监控正常情况下应保持在秒级以内一旦延迟太久说明堆积严重需要扩容消费能力。6.3 数据校验与切流前的检查同步跑完之后不能急着切流量先做数据校验。我的校验办法是抽样 对账按分片键范围抽样对比新旧库同一业务ID的核心字段是否一致对关键指标做汇总对账比如总订单数、总金额、状态分布用SUM和COUNT来粗粒度校验随机挑几个高频用户查看他们在新旧库的订单明细是否完全一致。校验通过后先让少量测试流量走新分片库观察日志和监控确认无异常。然后再按比例逐步放量5% - 20% - 50% - 100%。每放大一档观察至少半小时如果成交量、错误率、接口响应时间都稳定再进入下一档。6.4 回滚预案即使做得很谨慎也要做好回滚预案。我在切换期间保留了两条后路一是旧库持续保留并且保持7天的只读快照归挡。如果切换后发现严重问题可以把流量切回旧库虽然切换期间双写产生的增量数据可能丢失但至少主业务能快速恢复。二是新分片库的中间件配置提前准备好一键回切脚本包括路由配置、数据源配置、连接池参数。有时候切到新库出问题不是业务问题而是某个配置参数不合法导致启动异常有脚本可以快速回退。实际上我们最后没有触发回滚但“永远准备好一条可以跑的通的路”这个习惯让我在切流量的时候心里踏实很多。做技术方案信心不是来自“肯定不会出错”而是来自“出错了我也有办法收拾”。7. 有些场景其实不用急着分片写了这么多分片的内容最后我要说点反直觉的话很多系统根本不需要分片。市面上对分片有过度推崇的倾向好像大库不分片就是技术债。但在我的经验里很多场景存在更轻量的替代方案先用它们扛一阵往往比一开始就上分片更划算。7.1 分区表能解决一部分问题如果你的瓶颈只是单表数据量过大、查询太慢而且数据有明显的生命周期比如日志、流水、订单归档可以先试试分区表。用时间字段做 RANGE 分区查询时走分区裁剪挻多数场景下性能提升明显。分区表的实现成本很低——它只是一个DDL操作应用代码不用改。但它确实解决不了跨实例的资源瓶颈问题所以它适合作为分片的前置手段能缓一阵是一阵。7.2 归档表与历史库是个低成本方案很多大表里面有大量冷数据。比如订单表日常访问的基本是最近3个月到半年的数据三年前的历史订单几乎没人查。把这些历史数据迁移到归档表或者独立的历史库主表数据量立刻大幅下降性能自然恢复。实际操作上没有想象中复杂。写一个定时任务每天把超过180天的订单搬到历史表/历史库主库只留热数据。查询历史订单时路由到历史库。成本低、见效快而且风险远比分片小。7.3 读写分离 缓存能扛很大一部分流量如果瓶颈是读多写少缓存加读写分离的价值可能比分片还大。用 Redis 缓存热点数据把读流量从数据库转移到缓存数据库层面做一主多从写入走主库读取走从库相当于把单实例的读能力成倍放大。我见过一个业务峰值QPS冲到6000多数据库单库扛不住很多人建议分片。结果我们梳理后发现写QPS不到100绝大部分是读。我们用 Redis 缓存热点商品和用户信息配合一主两从的读写分离就把问题解决了压根没动分片。7.4 不要为了分片而分片分片有一个很致命的特性一旦上了分片很多数据库能力都会受限。JOIN不好用了、子查询被限制、分布式事务变复杂、运维监控难度上升、团队能力要求变高。这些代价都是长期的不是上线那一刻的切换成本。我的建议是在决定分片之前先回答这几个问题有没有认真做过 SQL 优化和索引优化有没有统计过数据的冷热分布历史数据能不能归档有没有评估过缓存、读写分离、分区表的收益分片后业务团队能不能接受查询和事务限制如果这些答案里有一个还没有就先别急着分片。分片是数据架构的大手术能不做就不做等确实必要了再动手。8. 几点实践经验当聊天了最后不做什么总结了就聊几个我反复踩过的细节吧。第一分片元数据一定要统一管理。不同的库表对应哪个分片分片键是什么分片算法参数是多少这些要集中维护不能散落在业务代码里。我曾见过一个项目同一个订单表在不同服务里分别维护了不同的分片规则结果跨服务查询时数据路由对不上排查了一个星期。第二分片数尽量定得大一点但别太大。比如预估一年后数据量是4000万单表合理数据量是500万的话8个分片就够了。但如果预判两年后还可能再翻倍我当时会直接设计成16个分片。宁可一开始分得多一点也不要一年后就面临扩容。但也不能盲目的分上千个分片分片太多会带来连接数、文件描述符、运维复杂度等一堆开销而且很多分片的数据量太少根本没有必要。第三监控系统要提前适应分片形态。分片后不能再只看数据库实例层面还得看分片级别每个分片的数据量是否均匀、慢查询主要集中在哪个分片、连接数在分片间是否均衡。我用的是Prometheus Grafana 搭建分片监控大屏把每个分片的关键指标通通拉出来第一次扩容和排查问题的时候帮了大忙。第四所有方案都要有人能接得住。分片不是一个人拍脑袋能做成的团队里至少要有一个核心开发熟悉分片中间件的原理、至少一个DBA能处理分片库的备份恢复和扩容操作。否则一旦老员工离职这套系统就成了烫手山芋。分片这件事技术上没有银弹。每个人的业务不一样、访问模型不一样适合的才是最好的。希望我这篇关于分表、分库、分片、分区的记录能帮你少走一点弯路。