【MapSheep】
[好记性不如烂笔头]

MySQL 分库分表(简短版)

一、核心总结

当 MySQL 单表数据量超 1000 万条/10G,查询性能急剧下降时,通过分库(拆大库为多个小库)、分表(拆大表为多个小表)分散数据压力,核心分两种方式:

  1. 垂直拆分:按业务/字段拆分(如电商拆用户库、订单库;用户表拆核心表/详情表),实现简单,适合业务隔离。
  2. 水平拆分:按规则(哈希/时间)拆分同一张表的行数据(如订单表按时间拆分为 order_202401、order_202402),从根本解决海量数据问题,需中间件支持。

企业主流方案:水平拆分(哈希规则)+ Sharding-JDBC 中间件

二、实战示例(2 个核心场景,通俗易懂)

示例 1:垂直拆分(无中间件,直接落地)

场景:电商用户表字段过多(60 个字段),高频查询仅 5 个核心字段
实现:纵向分表,拆分为 2 张表
  1. 核心表(高频查询,优先优化索引)
-- 用户核心表(user_core):存储高频查询字段
CREATE TABLE `user_core` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户唯一ID',
  `username` VARCHAR(50) NOT NULL COMMENT '用户名',
  `phone` VARCHAR(20) NOT NULL COMMENT '手机号',
  `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1正常 0禁用',
  `create_time` DATETIME NOT NULL COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户核心表(高频查询)';
  1. 详情表(低频查询,不建冗余索引)
-- 用户详情表(user_detail):存储低频查询字段,与核心表一对一关联
CREATE TABLE `user_detail` (
  `user_id` BIGINT PRIMARY KEY COMMENT '用户唯一ID(关联user_core.id)',
  `address` VARCHAR(200) COMMENT '收货地址',
  `avatar` VARCHAR(200) COMMENT '头像地址',
  `remark` TEXT COMMENT '备注信息',
  `update_time` DATETIME COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户详情表(低频查询)';
  1. 业务操作示例(查询用户完整信息)
-- 关联查询(仅需用户详情时才查询user_detail,减少高频查询压力)
SELECT c.*, d.address, d.avatar
FROM user_core c
LEFT JOIN user_detail d ON c.id = d.user_id
WHERE c.id = 123456;

示例 2:水平拆分(Sharding-JDBC 实现,按用户 ID 哈希分表)

场景:订单表(order)已有 1.5 亿条数据,查询缓慢,按 user_id 哈希拆分到 4 张表
实现:基于 Spring Boot + Sharding-JDBC,核心配置+表结构
  1. 依赖引入(pom.xml 核心依赖)
<!-- Sharding-JDBC 核心依赖 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.3.2</version>
</dependency>
  1. 配置文件(application.yml,分表规则配置)
spring:
  shardingsphere:
    datasource:
      # 配置单个数据源(分库可配置多个数据源)
      names: ds0
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/ecommerce?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          # 逻辑表名(业务中操作的表名,无需实际创建)
          t_order:
            # 实际数据节点:ds0数据源下,t_order_0 到 t_order_3 四张表
            actual-data-nodes: ds0.t_order_$->{0..3}
            # 分表规则:按 user_id 哈希取模,分配到不同表
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: t_order_inline
        # 分表算法配置(哈希取模)
        sharding-algorithms:
          t_order_inline:
            type: INLINE
            props:
              # 分片表达式:表索引 = user_id % 4
              algorithm-expression: t_order_$->{user_id % 4}
    # 开启 SQL 打印,便于调试
    props:
      sql-show: true
  1. 实际表结构(创建 4 张物理表,结构完全一致)
-- 创建物理表 t_order_0(t_order_1、t_order_2、t_order_3 结构完全一致)
CREATE TABLE `t_order_0` (
  `order_id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单唯一ID',
  `user_id` BIGINT NOT NULL COMMENT '用户ID(分表键)',
  `order_amount` DECIMAL(10,2) NOT NULL COMMENT '订单金额',
  `order_status` TINYINT NOT NULL COMMENT '订单状态',
  `create_time` DATETIME NOT NULL COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '订单表(物理表0)';
  1. 业务操作示例(Java 代码,操作逻辑表即可,中间件自动路由)
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;

    // 新增订单(中间件自动按 user_id % 4 路由到对应物理表)
    public void createOrder(Order order) {
        orderMapper.insert(order);
    }

    // 查询用户订单(中间件自动路由到对应物理表,无需关心分表细节)
    public List<Order> getOrderByUserId(Long userId) {
        return orderMapper.selectByUserId(userId);
    }
}
  1. 效果说明
  • 当 user_id=1001 时,1001 % 4 = 1,数据自动写入 t_order_1
  • 当 user_id=1004 时,1004 % 4 = 0,数据自动写入 t_order_0
  • 业务代码中仅操作 t_order(逻辑表),无需关心底层物理表,实现「透明化」分表。

总结

  1. 垂直拆分适合快速缓解压力,无需中间件,核心是「按业务/字段隔离」;
  2. 水平拆分适合海量数据,核心是「按规则分散行数据」,Sharding-JDBC 是企业主流落地工具;
  3. 示例覆盖「无中间件」和「有中间件」两种场景,可直接对应小型系统和中大型系统的落地需求。
posted on 2026-02-03 18:02  (Play)  阅读(0)  评论(0)    收藏  举报