订单表上亿行,我按用户ID拆成128片之后怎么样了

简介: 从单表几千万行慢查询的痛点出发,讲清垂直拆分与水平拆分的区别、分片键怎么选、分片算法(hash取模/range/一致性hash)怎么权衡,以及分库分表带来的分布式ID、跨片查询、分布式事务等问题,给出避免过度拆分的避坑清单。

大家好,我是数据库小学妹👋 我踩过的坑,你别再踩。

订单表五千万行了,慢查询一个接一个。运营一查历史订单,SQL跑几十秒,页面转圈。同事给出的方案是上分库分表。我一听头都大了,分库分表不是上来就拆,拆错方向,后面全得返工。其实后来发现,拆不是唯一的出路,国产数据库里面金仓KES的集中分布一体化就是另一条路,这个先放着,后面细说。今天把分库分表这件事讲清楚:什么时候该拆,垂直拆还是水平拆,分片键怎么选,拆完要面对哪些新问题。看完你就知道自己的表该不该拆、怎么拆。

一、先想清楚:真的该拆了吗

很多人一遇到慢查询,第一反应就是分库分表。其实先别急,慢查询不一定是数据量的问题。可能是指标没建对,可能是SQL写烂了,可能是硬件瓶颈。这些先排查,成本低。分库分表是重武器,一上就收不回来,运维复杂度直接翻倍。

什么时候才真该拆?两个信号一起出现:单表数据量超过5000万行(超过500万行且查询响应持续变慢是预警信号),同时常规优化(索引、SQL、归档)已经压不住查询和写入延迟。这时候才认真考虑拆。如果单库TPS持续超过5000,说明写入压力也已经到了单机瓶颈。

我的原则:能用索引解决的,别拆。能归档历史的,别拆。分库分表是最后的手段,不是第一选择。

二、垂直拆还是水平拆

真到要拆了,先分清楚两种拆法。

垂直拆分,是按业务拆。一张宽表,字段太多,拆成几张窄表。比如把订单表拆成订单主表、订单明细表、订单扩展表。或者更狠一点,按业务模块拆库,订单库、用户库、商品库分开。垂直拆解决的是“表太宽、字段冗余、单库压力”。

水平拆分,是按数据量拆。一张表数据太多,按某个键把行拆到多张表。比如订单表按用户ID拆成128张表,每张表数据量就只有原来的128分之一。水平拆解决的是“单表数据量太大、单库写不过来”。

// 水平拆分:同一张表,按分片键拆成多片
orders_0000  ← 用户ID对128取模=0
orders_0001  ← 用户ID对128取模=1
...
orders_0127  ← 用户ID对128取模=127

大多数说"分库分表"的,指的是水平拆分。它解决的是数据量和写入吞吐的瓶颈。

三、分片键怎么选,是拆分的命门

水平拆分的核心,是选对分片键。分片键选错,整个方案废掉一半。分片键要满足两点:分布均匀,查询友好。

分布均匀,是说数据要散得开。选用户ID这种天然均匀的键,每一片的数据量差不多。选地区这种,可能华东一片挤爆,西北一片没几个,就失衡了。

查询友好,是说你的高频查询要能用上分片键。分库分表后,查询必须带分片键,才能定位到具体哪一片。如果你的查询是“查某个用户的所有订单”,分片键选user_id,完美。如果你的查询是“按订单号查”,分片键却选了user_id,那这条查询就得扫全部分片,慢到怀疑人生。

-- 分片键是user_id,带user_id的查询能精确定位到片
SELECT * FROM orders WHERE user_id = 12345;

-- 不带user_id,只按订单号查 → 广播到所有分片,全表扫
SELECT * FROM orders WHERE order_no = 'ORD20260827001';

所以选分片键,先列你的核心查询。哪些查询是最高频的,就围绕它选分片键。别的查询只能靠中间件聚合,或者加冗余表兜底。

四、分片算法:hash、range、一致性hash

分片键定了,再用什么算法把键映射到片,也有讲究。

hash取模,最简单。user_id % 128,得到片号。均匀,但有个硬伤:一旦要扩容,从128片加到256片,取模结果全变,数据要大规模重分布。迁移成本巨大。

range范围,按区间分。比如按用户ID的区间,0到1000一片,1000到2000一片。扩容方便,加一片就行。但可能不均匀,热点用户集中在某一片。

一致性hash,为了缓解hash取模的扩容问题。它把节点放到一个哈希环上,扩容只影响部分数据。但实现复杂,均匀性也要靠虚拟节点调。

怎么选?我的建议是:业务增长曲线平稳、短期内不打算扩容的,选hash取模,最简单。业务增长快、明确知道要扩容的,提前上range或一致性hash。如果数据有明显的时间特征(比如订单按年增长),range分片天然适合冷热分离。

五、拆完,新问题比想象多

分库分表不是拆完就完事。拆完之后,一堆新问题等着你。

分布式ID。分库分表后,如果还沿用单表自增ID,每个分片都会从1开始独立计数,不同分片之间ID必然冲突。得换成全局ID方案,雪花算法、号段模式,自己选一个。

跨分片查询。要查的数据分布在不同片,怎么办?GROUP BY、ORDER BY、JOIN,都得在中间件层聚合。数据量一大,聚合就是灾难。所以分库分表后,查询设计要尽量避开跨片操作。

分布式事务。一笔业务要写多张分片表,怎么保证要么都成功要么都回滚?这得引入分布式事务方案,或者用最终一致性补偿。复杂度直接上一个台阶。

扩容。今天拆了128片,业务再翻一倍,128片也不够用了,要扩到256片。hash取模的分片,扩容意味着取模结果全变,数据要大规模重分布。扩容这件事,拆之前就得想好。

中间件。ShardingSphere、MyCat这些,帮你屏蔽分片的复杂性。但中间件本身也是要运维的,也有性能开销。

这些不是吓你,是拆之前就要想清楚的。很多人只看到拆完变快了,没看到拆完的运维地狱。

六、有没有不拆的选择?

分库分表拆完之后,最头疼的就是分布式ID、跨片查询和分布式事务这三件事。有没有办法既能解决数据量瓶颈,又不用自己扛这些复杂度?

金仓KES提供了一条不同的路。它的“集中分布一体化”架构,让集中式和分布式不是二选一。业务小的时候用集中式,数据量涨上来了,同一套架构原地扩成分布式,不用推倒重来。KES Sharding分片集群以KES为底座,通过分库分表机制将数据压力分散到多个节点,应用侧不用感知分片逻辑,路由和事务由数据库内核统一处理。

也就是说,前面提到的分布式ID、跨片查询、分布式事务这些“拆完之后的麻烦”,如果交给KES Sharding来做,就是在数据库内核层解决的,不需要中间件层再兜一层。某省海洋局的项目里,3节点KES Sharding支撑了每日3000万条峰值写入,就是这类场景的验证。

当然,这不代表分库分表不重要。理解它的原理、分片键怎么选、扩容怎么规划,这些基本功不能丢。因为不管用不用中间件,分片的思想是通的。只是说,如果你的团队没有足够的中间件运维经验,又确实面临数据量瓶颈,可以评估一下这类“内核级分布式”的方案。

七、分库分表的避坑清单

分片键别拍脑袋选。我见过一个团队按订单号分片,结果所有查询都带订单号,单点查询倒是快了,但按用户聚合的报表全要扫全片,慢得没法用。选分片键前,先把核心查询列清楚,让分片键覆盖最高频的查询。

别过早分库分表。数据还没到量,就把系统拆了,白白增加复杂度。有个判断办法:单表数据量没到千万级,索引优化还能扛,就别拆。拆了以后,所有SQL、事务、报表都要跟着改,成本极高。我见过拆完又拆回去的。

拆完一定要处理分布式事务和ID。别拆完表,自增ID还是各片独立,事务还是本地事务。真出问题,数据对不上,查都查不清。上线前先把分布式ID、跨片查询、事务方案都设计好,再动工。

写在最后

分库分表是分布式改造里最重的一步棋,动了就难回头。它解决的是数据量和吞吐的瓶颈,代价是复杂度成倍上升。我的建议始终是:能优化就不拆,真要拆就一次想清楚。想清楚什么?分片键、分片算法、分布式ID、跨片查询、分布式事务,这些在上线前都要有答案。方案设计得越细,后面踩的坑越少。

数据库的拆分,不是拆完就完事,而是另一种复杂度的开始。看懂这个,才算真的入门。


你的表拆过分库分表吗?分片键选的啥?踩过什么坑?评论区聊聊。

我是数据库小学妹,帮你少走弯路少踩坑,咱们下篇见👋

相关文章
|
23天前
|
SQL 监控 关系型数据库
MySQL索引合并优化器陷阱:为什么复合索引比索引合并快一个数量级?
MySQL优化器有一个“自作聪明”的行为——当单列索引无法完全覆盖查询时,它可能选择索引合并(Index Merge) ,同时使用多个单列索引,把结果集合并起来。听起来很合理对吧?但索引合并有严格的适用条件,用错了比全表扫描还慢——尤其是UNION类型的索引合并,需要对多个结果集去重和排序,代价极高。本文拆解索引合并的3种类型、3个踩坑场景,以及什么时候该用复合索引替代。
|
23天前
|
SQL 人工智能 数据库
AI写的SQL语法对、性能炸?上线前五道关卡能救命
从AI生成SQL的三大翻车模式(字段幻觉、性能灾难、语义错误)出发,给出上线前五道审核关卡:结构预检、执行计划校验、高危操作拦截、灰度上线、审计追踪,附SQL示例与避坑清单。
|
24天前
|
安全 关系型数据库 MySQL
高并发下1档只慢一点?innodb_flush_log_at_trx_commit的0/1/2实测
生成图片:不要沿用上面的图片风格,重新生成 4 张文章封面图供我选择,16:9 主标题:MySQL持久性最佳实践 副标题:redo刷盘参数三档取舍与故障分析 文章概述:实测innodb_flush_log_at_trx_commit的0、1、2三档性能,讲清进程崩溃与断电下的丢数据边界,以及redo、doublewrite、双1的关系,给出选型建议。
|
28天前
|
SQL 人工智能 运维
3个月AI Agent运维实测:慢SQL它管,根因还得我上
以三个月实测的视角,划清AI Agent自治运维的真实能力边界:巡检、慢SQL发现等重复活已可替代,复杂根因、变更审批、数据兜底仍需人把关,探讨DBA角色从救火队员向定规则、把关人的转型。
|
1月前
|
SQL 关系型数据库 MySQL
死锁报错看了三遍没看懂?我拆给你看(附定位SQL)
从一次真实死锁现场切入,讲清行锁、间隙锁、插入意向锁的加锁机制与死锁形成原理,手把手教你怎么用show engine innodb status和information_schema定位死锁,并给出加锁顺序设计等避坑清单。
|
21天前
|
SQL Java 数据库连接
1万行插入13秒到0.9秒:ORM批量插入只差一个参数
从一次列表接口慢的排障讲起,发现2000多条一模一样的N+1查询。文章拆开ORM生成慢SQL的三类典型病:N+1懒加载、逐条批量插入(只差一个rewriteBatchedStatements参数)、隐式转换让索引白建。给出JOIN/批量IN/@BatchSize的取舍、MyBatis与JPA各自的修法,以及用performance_schema按SQL指纹抓N+1、测试环境打印真实SQL的协作办法。
|
24天前
|
SQL 关系型数据库 MySQL
别再盯着EXPLAIN的rows列了,8.0.18之后有更好的选择
EXPLAIN是DBA最常用的工具之一,但大多数人还在看type、rows、Extra这些传统字段——然后靠经验猜。MySQL 8.0.18开始引入的EXPLAIN ANALYZE,直接把实际执行时间和行数输出给你看,不用猜了。本文对比传统EXPLAIN和EXPLAIN ANALYZE的差异,展示如何用新工具把执行计划分析这件事从“猜”变成“看”。
|
20天前
|
人工智能 Cloud Native 数据库
向量数据库选型实战:从 Embedding、ANN 索引到三条落地路线
本文直击向量数据库本质:不堆概念,不列产品,专讲它“是什么”、三条选型路径(专用库/关系库扩展/云托管)如何取舍,以及开发者落地必须关注的召回率、延迟、更新一致性等真实问题。聚焦RAG实战,强调“先用pgvector跑通再升级”,拒绝盲目上马。
116 0
|
29天前
|
存储 关系型数据库 MySQL
面试总问的B+树,我把磁盘IO到底怎么算的讲清楚了
从磁盘IO的底层约束讲起,逐层对比哈希、二叉、红黑树、B树与B+树,讲清MySQL为什么选B+树(矮胖树、顺序IO、范围查询、查询稳定),并用这套底层理解反过来指导覆盖索引、前缀索引、最左前缀等日常索引设计。
|
16天前
|
存储 关系型数据库 MySQL
对账差了三毛钱,查完我把全部金额字段从DOUBLE改成了DECIMAL
一次财务对账差三毛钱的排查,牵出金额字段用浮点数的老坑。从IEEE 754为什么存不准0.1讲起,用同一批金额把FLOAT、DOUBLE、DECIMAL三种类型实测对比,再给出金额字段的选型、聚合与改表做法,附避坑清单。