数据库分库分表怎么做

wen IT资讯 31

从原理到实战的完整指南

目录导读

  1. 为什么需要分库分表?
  2. 分库分表的核心策略:垂直与水平拆分
  3. 分片键选择与常见算法
  4. 数据迁移与一致性保障
  5. 分库分表后的查询与事务难题
  6. 主流中间件对比(ShardingSphere、MyCat、Vitess)
  7. 分库分表的“避坑”问答集锦

为什么需要分库分表?

当单表数据量超过500万行,或单库写入TPS超过3000,数据库通常会出现以下症状:

数据库分库分表怎么做

  • 查询延迟飙升:B+树索引层数增加,随机IO频繁
  • 锁竞争剧烈:行锁、间隙锁升级为表锁,死锁概率上升
  • 写入瓶颈:单机磁盘IOPS达到上限,binlog同步延迟

典型案例:某电商订单表日增50万数据,3个月后单表超4500万行,删除过期数据时导致主从延迟超过2小时,影响线上查询,分库分表后,将数据按年份拆分为12张表,并分散到4个库,单表量降至370万行,查询耗时从2.3s降至47ms。


核心策略:垂直与水平拆分

垂直拆分(纵向)

  • 垂直分库:按业务模块拆分,例如将用户库、订单库、支付库独立部署,注意:跨库关联查询需通过服务层聚合。
  • 垂直分表:将宽表拆分为“主表+扩展表”,例如将商品表的图片URL字段(频繁读取但体积大)分离,减少单行I/O成本。

水平拆分(横向)

  • 范围分片:按时间或ID范围(如2024_012024_02),优点:扩展时无需迁移旧数据;缺点:容易产生热点(尾部表压力集中)。
  • 哈希分片:通过ID % 数据库数量或一致性哈希算法分配,优点:数据分布均匀;缺点:扩容时需大规模迁移。
  • 映射表:维护独立的路由表,灵活性高但增加中间层查询开销。

推荐组合:高频场景采用哈希分片平衡负载,历史归档场景采用范围分片


分片键选择与常见算法

分片键定义规则

  • 高频查询字段:如订单ID、用户ID,需保证95%以上的查询携带该字段。
  • 均匀分布性:避免自增主键作为哈希分片键(会导致连续ID落入同一节点)。
  • 不可变:禁止修改分片键值,否则需重算路由。

三种主流算法实现示例

// 1. 取模分片(适用于固定节点数)
public String routeByMod(long orderId) {
    int dbIndex = (int) (orderId % 4); // 4个库
    int tableIndex = (int) ((orderId / 4) % 12); // 每库12张表
    return "db_" + dbIndex + ".t_order_" + tableIndex;
}
// 2. 一致性哈希(减少扩容影响)
// 通过MD5哈希后取环上节点,配合虚拟节点缓解数据倾斜
// 3. 时间范围+ID后缀(适合时序数据)
public String routeByTime(long orderId, Date createTime) {
    String month = new SimpleDateFormat("yyyyMM").format(createTime);
    int suffix = orderId % 32; // 32张表
    return "db_order_" + month + ".t_order_" + suffix;
}

数据迁移与一致性保障

不停机迁移三步法

  1. 增量同步:开启binlog监听,将源库数据实时同步至新分片
  2. 双写校验:应用层同时写入老库和新库,比对数据差异并修复
  3. 流量切换:灰度放量5% → 50% → 100%,观察监控指标(慢查询率、主从延迟)

关键工具:开源方案选用Canal(Alibaba)+ DataX;商业方案选用NineDataDTS(阿里云)。。


分库分表后的查询与事务难题

全局主键生成

  • 雪花算法(Snowflake):64位long(1位符号位+41位毫秒时间戳+10位机器ID+12位序号),单机QPS可达409万
  • Redis incr:适用于中低并发,注意持久化丢失风险
  • 号段模式:Leaf-segment(美团开源),预取1000个ID到内存,减少数据库压力

跨节点查询优化

  • 禁止全库扫描:通过sharding-hint强制指定路由键
  • 分页难题:采用“二次查询法”:先查询各分片的最小ID,再汇总排序取全局第N页
  • 分布式事务:建议改用“柔性事务”,用可靠消息最终一致性替代强ACID,例如RocketMQ事务消息

关联查询替代方案

  • 数据冗余:将用户昵称、商品标题直接也冗余到订单表中
  • 搜索引擎:用Elasticsearch做聚合查询,MySQL仅做Key-Value写入

主流中间件对比

特性 ShardingSphere(推荐) MyCat Vitess
接入方式 JDBC驱动(零运维) 独立代理(需机器) 基于K8s的云原生方案
SQL支持 95%标准语法 70%语法 80%语法(限制复杂子查询)
分布式事务 支持XA/SEATA 仅单库事务 基于Vtgate的两阶段提交
性能损耗 <5% 15%~25% 10%

选型建议:初创公司首选ShardingSphere-JDBC,轻量无依赖;大团队云原生场景选择Vitess;追求快速入门可用MyCat,但注意其SQL兼容性问题。


问答集锦

Q1:表数据量200万行,需要分库分表吗? A:通常不需要,可先排查索引优化(复合索引、覆盖索引)和连接池配置(HikariCP),当查询响应超过200ms且无法通过索引优化时,再考虑分表(垂直分表优先)。

Q2:分片后如何做全表扫描的统计报表? A:使用Spark或Flink定时并行扫描所有分片,中间结果落入OLAP数据库(如ClickHouse),切勿直接在MySQL执行跨分片聚合。

Q3:扩容时如何不停机增加节点? A:采用虚拟节点一致性哈希,如将每张物理表映射到1024个虚拟槽位,扩容时只需调整槽位分配比例,数据迁移范围控制在5%以内,搭配MongoDB模式:先读新节点,写入双节点,最终删除旧数据。

Q4:分库后主键重复怎么处理? A:绝对禁止使用AUTO_INCREMENT,推荐雪花算法,注意时钟回拨问题(预留回拨补偿值或使用Redis生成ID)。

Q5:分表后如何支持多条件查询? A:将查询场景划分为两类:

  • 高频条件(如用户ID+订单状态):按用户ID分片,同时建立联合索引
  • 低频条件(如按时间筛选):建立中间表记录索引信息,或用Elasticsearch聚合

分库分表的核心价值在于线性扩展读/写能力,而非解决所有数据库问题,实施前务必评估业务增长曲线,优先通过连接池优化、读写分离、冷热数据分层等低成本手段应对,如果确定需要分片,请记住三个关键点:选对分片键(均匀+高频)、预留扩容方案拥抱最终一致性,多利用搜索引擎验证方案——在github搜索“spingboot sharding-jdbc demo”,80%的踩坑场景已有社区解决方案。

抱歉,评论功能暂时关闭!