Sharding-JDBC学习手册

5 阅读32分钟

1. 分库分表核心认知

很多新手最大误区:上来就做分片。分片属于架构重构,开发、运维、排查成本成倍提升,非必要绝不使用。

我直接记结论:单表数据超500W、QPS过高、索引失效、大表DDL卡死,就必须考虑分片。

普通优化优先级(我自己的排序):

索引优化 → 读写分离 → 冷热归档 → 分库分表

分片是最后手段,不是首选!

读写分离只能分摊读压力,解决不了单表存储上限,这是分片存在的唯一核心意义。

2. Sharding-JDBC 核心原理

2.1 是什么?(通俗类比)

Sharding-JDBC 就是一个内嵌在项目里的数据库路由代理

类比:它像一个“数据库调度员”,我们代码只写一张表的SQL,它自动帮我们:

  • 识别分片字段

  • 算出数据该进哪个库、哪张表

  • 自动改写SQL、执行、合并结果

全程无感知、不用改业务SQL

2.2 整体执行链路(极简版)

接收SQL → 解析分片键 → 路由目标库表 → 改写SQL → 多分片执行 → 结果聚合返回

2.3 为什么选它不选MyCat?(个人总结)

  • Sharding-JDBC:客户端嵌入,无中间件转发、性能高、运维简单

  • MyCat:独立服务代理,多一层网络损耗,新项目基本淘汰

3. 核心概念详解

所有术语第一次出现全部通俗注释,零基础直接看懂。

3.1 分片键(Sharding Column)✅核心中的核心

通俗解释:用来决定数据“去哪张表、哪个库”的字段,就是分片的导航键。

常用分片键:order_id、user_id

我的笔记重点

  • 分片键一旦确定不能改,改了数据全部错乱

  • 业务绝大多数查询必须带分片键,否则全表广播、性能雪崩

3.2 分片算法

3.2.1 哈希取模算法(生产首选)

原理:分片键 % 分片数 = 目标位置

优点:数据均匀、无热点

缺点:只能2/4/8倍数扩容,否则全量迁移数据

适用:订单、交易核心业务

3.2.2 范围算法

原理:按时间、ID区间分片

优点:扩容不用迁移历史数据

缺点:最新分片永远热点,流量全部打在最后一张表

适用:日志、流水、非核心数据

3.3 广播表

通俗解释:所有分片库都存一份一模一样的数据,全局统一。

场景:字典表、配置表、地区表

作用:避免查字典数据跨库查询,提升性能

3.4 绑定表

通俗解释:分片键一致的关联表,绑定后数据一定落在同一个分片

场景:订单表 & 订单详情表

作用:彻底解决跨分片不能JOIN的问题

3.5 分库 & 分表 区别(极简记忆)

  • 分表:同一个库,多张表 → 解决单表数据量大

  • 分库:多个库,分摊连接数、CPU、IO → 解决单库并发瓶颈

3.6 范围分片算法(RangeShardingAlgorithm)完整补充

通俗解释:专门用于时间、ID区间分片的算法,和精准算法成对配套使用,面试必问、时间分片必备。

适用场景:按天/按月分表、日志数据、监控数据、时序流水数据。

核心特点:支持区间查询自动路由对应分片,无需全表扫描。

import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import java.util.Collection;
import java.util.Collections;

/**
 * 自定义范围分片算法
 * 适配时间、ID区间范围查询
 */
public class CustomRangeAlgorithm implements RangeShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
        // 简单适配:范围查询直接匹配所有分片(可自定义区间精准匹配)
        return Collections.singletonList(availableTargetNames.iterator().next());
    }
}

3.7 分片全局ID解决方案(补齐自增ID失效核心问题)

新手致命坑:分库分表后,MySQL原生自增ID完全作废!多库同时自增会出现主键重复,直接报错。

通俗原理:每个数据库独立维护自增计数器,多库写入必然ID冲突。

生产唯一解决方案:全局分布式ID

我整理的三种主流方案(初学者直接背):

  • 雪花算法(Snowflake)【首选】:高性能、有序、无中心化、适配所有交易业务。

  • UidGenerator:百度优化版雪花算法,彻底解决时钟回拨,大厂标配。

  • Redis自增ID:简单易用,低并发场景可用,高并发存在单点瓶颈。

雪花算法核心隐患:时钟回拨(面试高频)

问题:服务器时间同步、校准时间会导致时间回退,生成重复ID,引发分片主键冲突。

极简解决方案:代码缓存上一次时间戳,检测到回拨则阻塞等待时间追平,禁止生成重复ID。

4. 核心实战代码

本节覆盖面试高频核心类:ShardingDataSource、ShardingRuleConfiguration、ShardingDataSourceFactory、PreciseShardingAlgorithm

所有代码我亲自精简,无冗余、可直接复制运行。

4.1 数据库建表语句(生产完整可执行)

适配本文 ds0、ds1 双库 + 每库2表 分片结构,包含订单表、订单详情表、广播字典表,可直接在本地/测试库执行。

4.1.1 新建分片数据库

-- 创建分片库0、分片库1
CREATE DATABASE IF NOT EXISTS sharding_db0 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS sharding_db1 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

4.1.2 业务分片表(订单+订单详情)

-- 【sharding_db0、sharding_db1 均需执行】
-- 订单表:t_order_0 / t_order_1
USE sharding_db0;
CREATE TABLE IF NOT EXISTS t_order_0 (
    order_id BIGINT NOT NULL COMMENT '分片键-订单ID(雪花ID)',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    order_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额',
    order_status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (order_id),
    KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单分片表0';

CREATE TABLE IF NOT EXISTS t_order_1 (
    order_id BIGINT NOT NULL COMMENT '分片键-订单ID(雪花ID)',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    order_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额',
    order_status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (order_id),
    KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单分片表1';

USE sharding_db1;
CREATE TABLE IF NOT EXISTS t_order_0 (
    order_id BIGINT NOT NULL COMMENT '分片键-订单ID(雪花ID)',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    order_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额',
    order_status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (order_id),
    KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单分片表0';

CREATE TABLE IF NOT EXISTS t_order_1 (
    order_id BIGINT NOT NULL COMMENT '分片键-订单ID(雪花ID)',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    order_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额',
    order_status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (order_id),
    KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单分片表1';

-- 订单详情绑定表(与订单表同分片规则)
USE sharding_db0;
CREATE TABLE IF NOT EXISTS t_order_item_0 (
    item_id BIGINT NOT NULL COMMENT '详情ID',
    order_id BIGINT NOT NULL COMMENT '分片键-关联订单ID',
    goods_id BIGINT NOT NULL COMMENT '商品ID',
    goods_num INT NOT NULL DEFAULT 1 COMMENT '购买数量',
    goods_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '商品单价',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (item_id),
    KEY idx_order_id (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单详情分片表0';

CREATE TABLE IF NOT EXISTS t_order_item_1 (
    item_id BIGINT NOT NULL COMMENT '详情ID',
    order_id BIGINT NOT NULL COMMENT '分片键-关联订单ID',
    goods_id BIGINT NOT NULL COMMENT '商品ID',
    goods_num INT NOT NULL DEFAULT 1 COMMENT '购买数量',
    goods_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '商品单价',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (item_id),
    KEY idx_order_id (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单详情分片表1';

USE sharding_db1;
CREATE TABLE IF NOT EXISTS t_order_item_0 (
    item_id BIGINT NOT NULL COMMENT '详情ID',
    order_id BIGINT NOT NULL COMMENT '分片键-关联订单ID',
    goods_id BIGINT NOT NULL COMMENT '商品ID',
    goods_num INT NOT NULL DEFAULT 1 COMMENT '购买数量',
    goods_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '商品单价',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (item_id),
    KEY idx_order_id (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单详情分片表0';

CREATE TABLE IF NOT EXISTS t_order_item_1 (
    item_id BIGINT NOT NULL COMMENT '详情ID',
    order_id BIGINT NOT NULL COMMENT '分片键-关联订单ID',
    goods_id BIGINT NOT NULL COMMENT '商品ID',
    goods_num INT NOT NULL DEFAULT 1 COMMENT '购买数量',
    goods_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '商品单价',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (item_id),
    KEY idx_order_id (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单详情分片表1';

4.1.3 全局广播字典表(全库同步)

-- 广播表:两个库均创建,数据保持一致
USE sharding_db0;
CREATE TABLE IF NOT EXISTS t_dict (
    dict_id INT NOT NULL AUTO_INCREMENT COMMENT '字典ID',
    dict_type VARCHAR(64) NOT NULL COMMENT '字典类型',
    dict_label VARCHAR(128) NOT NULL COMMENT '字典名称',
    dict_value VARCHAR(128) NOT NULL COMMENT '字典值',
    sort INT NOT NULL DEFAULT 0 COMMENT '排序',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (dict_id),
    UNIQUE KEY uk_type_value (dict_type,dict_value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='全局字典广播表';

USE sharding_db1;
CREATE TABLE IF NOT EXISTS t_dict (
    dict_id INT NOT NULL AUTO_INCREMENT COMMENT '字典ID',
    dict_type VARCHAR(64) NOT NULL COMMENT '字典类型',
    dict_label VARCHAR(128) NOT NULL COMMENT '字典名称',
    dict_value VARCHAR(128) NOT NULL COMMENT '字典值',
    sort INT NOT NULL DEFAULT 0 COMMENT '排序',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (dict_id),
    UNIQUE KEY uk_type_value (dict_type,dict_value)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='全局字典广播表';

笔记重点:所有表结构完全对齐,保证多分片DDL一致,杜绝生产结构不一致BUG;order_id 统一作为分片键,与前文算法、配置完全匹配。

4.2 必备Maven依赖

4.3 自定义精确分片算法

适用场景:等值查询、IN查询,精准路由单分片

import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.util.Collection;

/**
 * 自定义精确分片算法
 * 我个人笔记:处理 =、IN 查询,精准定位单库/单表
 */
public class CustomPreciseAlgorithm implements PreciseShardingAlgorithm<Long> {

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        // 获取分片键值
        Long value = shardingValue.getValue();
        // 2分片取模路由
        long index = value % 2;
        // 返回目标表/库名称
        for (String name : availableTargetNames) {
            if (name.endsWith(index + "")) {
                return name;
            }
        }
        throw new RuntimeException("分片路由失败");
    }
}

4.4 代码方式手动配置分片规则

不用yml配置,纯代码实现,看懂底层核心组装逻辑

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shardingsphere.api.config.sharding.ShardingRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.TableRuleConfiguration;
import org.apache.shardingsphere.api.config.sharding.strategy.StandardShardingStrategyConfiguration;
import org.apache.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Sharding-JDBC 核心配置类
 * 核心类全覆盖:ShardingDataSource、ShardingRuleConfiguration、ShardingDataSourceFactory
 */
@Configuration
public class ShardingConfig {

    // 1. 构建多数据源
    private Map<String, DataSource> createDataSourceMap() {
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        // 数据源0
        DruidDataSource ds0 = new DruidDataSource();
        ds0.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds0.setUrl("jdbc:mysql://localhost:3306/sharding_db0?useUnicode=true&serverTimezone=Asia/Shanghai");
        ds0.setUsername("root");
        ds0.setPassword("123456");
        dataSourceMap.put("ds0", ds0);

        // 数据源1
        DruidDataSource ds1 = new DruidDataSource();
        ds1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds1.setUrl("jdbc:mysql://localhost:3306/sharding_db1?useUnicode=true&serverTimezone=Asia/Shanghai");
        ds1.setUsername("root");
        ds1.setPassword("123456");
        dataSourceMap.put("ds1", ds1);
        return dataSourceMap;
    }

    // 2. 构建分片规则
    private ShardingRuleConfiguration createShardingRule() {
        ShardingRuleConfiguration rule = new ShardingRuleConfiguration();

        // 配置t_order表规则:2库2表
        TableRuleConfiguration orderTable = new TableRuleConfiguration("t_order", "ds${0..1}.t_order_${0..1}");
        // 标准分片策略:分片键order_id + 自定义精确算法
        orderTable.setTableShardingStrategyConfig(
                new StandardShardingStrategyConfiguration("order_id", new CustomPreciseAlgorithm())
        );
        rule.getTableRuleConfigs().add(orderTable);

        // 绑定表:订单、订单详情关联绑定,禁止跨库join
        rule.getBindingTableGroups().add("t_order,t_order_item");
        // 广播表:全局字典表
        rule.getBroadcastTables().add("t_dict");

        return rule;
    }

    // 3. 工厂创建最终Sharding数据源(核心入口)
    @Bean
    public DataSource shardingDataSource() throws Exception {
        return ShardingDataSourceFactory.createDataSource(
                createDataSourceMap(),
                createShardingRule(),
                new Properties()
        );
    }
}

4.5 生产通用YAML配置

前面是代码硬编码配置(学习底层用),工作中全部使用yml配置,零侵入、可直接上线。

spring:
  shardingsphere:
    # 多数据源配置
    datasource:
      names: ds0,ds1
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db0?useUnicode=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db1?useUnicode=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
    # 分片规则
    rules:
      sharding:
        # 分片表规则
        tables:
          t_order:
            actual-data-nodes: ds->{0..1}.t_order_->{0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-mod
        # 分片算法
        sharding-algorithms:
          order-mod:
            type: MOD
            props:
              sharding-count: 2
        # 绑定表、广播表
        binding-tables:
          - t_order,t_order_item
        broadcast-tables:
          - t_dict
    # 开启SQL日志(调试必备)
    props:
      sql-show: true

4.6 核心类总结

  • ShardingDataSourceFactory:工厂类,唯一创建分片数据源的入口

  • ShardingRuleConfiguration:承载所有分片规则、绑定表、广播表、算法配置

  • PreciseShardingAlgorithm:精准分片算法,面试必考,处理等值查询

  • StandardShardingStrategyConfiguration:标准策略,绑定「分片键+算法」

4.7 SpringBoot 生产级完整集成方案

适配 SpringBoot2.x 主流生产版本,整合 Druid连接池 + 分片规则 + SQL日志 + 事务适配,解决原生配置连接泄漏、日志缺失、事务失效等生产问题。

4.7.1 生产完整依赖(剔除冗余、规避冲突)

<!-- SpringBoot 分片核心依赖 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.1</version>
    <!-- 排除默认日志,避免冲突 -->
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 生产级Druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.16</version>
</dependency>

<!-- MyBatis 适配 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

4.7.2 生产YAML终极配置(多分片+连接池优化+调试可控)

适配生产:连接池参数优化、关闭冗余日志、支持动态SQL调试、绑定表/广播表完备、事务兼容

spring:
  # 数据源分片配置
  shardingsphere:
    # 多数据源定义
    datasource:
      names: ds0,ds1
      # 库0配置
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db0?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
        username: root
        password: 123456
        # 生产连接池核心参数
        initial-size: 5
        max-active: 20
        min-idle: 5
        max-wait: 60000
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
      # 库1配置
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
        username: root
        password: 123456
        initial-size: 5
        max-active: 20
        min-idle: 5
        max-wait: 60000
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
    # 分片规则
    rules:
      sharding:
        # 自定义分片算法注册
        sharding-algorithms:
          order-mod:
            type: MOD
            props:
              sharding-count: 2
        # 表分片规则
        tables:
          t_order:
            actual-data-nodes: ds->{0..1}.t_order_->{0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-mod
        # 绑定表(解决跨分片JOIN)
        binding-tables:
          - t_order,t_order_item
        # 全局广播表(字典/配置)
        broadcast-tables:
          - t_dict
    # 生产环境参数
    props:
      sql-show: false # 生产关闭SQL打印,测试开启
      max.connections.size.per.query: 2 # 限制单查询最大连接,防雪崩

# MyBatis生产配置
mybatis:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    default-statement-timeout: 3000

4.7.3 分布式ID生产配置(全局唯一,替代自增ID)

内置Sharding-JDBC原生雪花ID,无需额外引入组件,生产零成本使用。

spring:
  shardingsphere:
    rules:
      sharding:
        # 全局分布式ID配置
        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 0 # 单机0,集群不同节点配置不同ID(0-31)

笔记重点:集群部署必须保证 worker-id 唯一,否则出现ID重复!

4.7.4 生效测试 Controller(可直接自测)

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
public class ShardingTestController {

    @Resource
    private OrderMapper orderMapper;

    // 分片路由测试
    @GetMapping("/test/sharding/{orderId}")
    public String testSharding(@PathVariable Long orderId){
        return "路由分片成功:" + orderMapper.selectById(orderId);
    }
}

4.8 SpringCloud 微服务生产适配方案

微服务核心原则:分片只在数据服务层实现,网关、业务服务不感知分片规则

4.8.1 微服务落地规范

  • 分片配置、数据源、算法统一下沉至数据中台/基础服务

  • 业务微服务只调用Feign接口,零分片配置

  • 全局分布式ID统一由数据服务生成,避免多服务ID冲突

  • 微服务禁止跨服务JOIN,彻底规避分片关联问题

4.8.2 微服务事务适配(生产刚需)

Sharding-JDBC 本地分片事务无法跨微服务,微服务场景必须搭配 Seata AT 实现最终一致性。

<!-- SpringCloud 分布式事务适配依赖 -->
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

4.8.3 微服务避坑要点

  • ❌ 禁止多微服务重复配置分片规则

  • ❌ 禁止微服务内直接跨库查询

  • ✅ 所有分片CRUD收敛至数据服务

  • ✅ 分页、聚合、排序统一在数据层处理,上层只拿结果

4.9 生产上线校验清单

  • ✅ 关闭 sql-show,避免日志刷屏、泄露SQL

  • ✅ 雪花算法 worker-id 集群唯一

  • ✅ 连接池参数适配业务QPS,避免连接耗尽

  • ✅ 所有核心查询强制携带分片键

  • ✅ 绑定表、广播表配置完整,无遗漏关联表

  • ✅ 禁用所有分片黑名单SQL语法

4.10 生产核心补全:雪花ID 时钟回拨终极解决源码

原生Sharding-JDBC雪花算法存在时钟回拨ID重复生产致命BUG,本章节提供可直接上线的优化版雪花算法,彻底杜绝时间回拨导致的主键冲突,适配集群多节点部署。

4.10.1 优化版雪花算法(无时钟回拨、生产可用)

/**
 * 优化版雪花算法(彻底解决时钟回拨)
 * 生产落地标准:支持集群worker-id配置、时间回拨阻塞、杜绝ID重复
 */
public class SnowflakeIdGenerator {

    // 起始时间戳(固定:2025-01-01)
    private static final long EPOCH = 1735689600000L;
    // 机器ID位数 5位 0-31
    private static final long WORKER_ID_BITS = 5L;
    // 数据中心ID位数 5位 0-31
    private static final long DATA_CENTER_ID_BITS = 5L;
    // 序列号位数 12位 每毫秒0-4095
    private static final long SEQUENCE_BITS = 12L;

    private static final long MAX_WORKER_ID = (1L << WORKER_ID_BITS) - 1;
    private static final long MAX_DATA_CENTER_ID = (1L << DATA_CENTER_ID_BITS) - 1;

    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;

    private static final long SEQUENCE_MASK = (1L << SEQUENCE_BITS) - 1;

    // 机器ID
    private final long workerId;
    // 数据中心ID
    private final long dataCenterId;
    // 最后时间戳
    private long lastTimestamp = -1L;
    // 序列号
    private long sequence = 0L;

    public SnowflakeIdGenerator(long workerId, long dataCenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("workerId 超出范围(0-31)");
        }
        if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
            throw new IllegalArgumentException("dataCenterId 超出范围(0-31)");
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    // 线程安全、同步生成ID,彻底解决时钟回拨
    public synchronized long nextId() {
        long currentTimestamp = System.currentTimeMillis();

        // 核心:时钟回拨检测 & 阻塞等待
        if (currentTimestamp < lastTimestamp) {
            long offset = lastTimestamp - currentTimestamp;
            // 短时间回拨阻塞等待,时间追平再生成
            if (offset <= 1000) {
                try {
                    Thread.sleep(offset);
                    currentTimestamp = System.currentTimeMillis();
                } catch (InterruptedException e) {
                    throw new RuntimeException("时钟回拨,生成ID失败", e);
                }
            } else {
                throw new RuntimeException("严重时钟回拨,禁止生成ID");
            }
        }

        // 同一毫秒,序列号自增
        if (lastTimestamp == currentTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            // 毫秒内序列号耗尽,等待下一毫秒
            if (sequence == 0) {
                currentTimestamp = waitNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = currentTimestamp;
        return ((currentTimestamp - EPOCH) << TIMESTAMP_SHIFT)
                | (dataCenterId << DATA_CENTER_ID_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }

    private long waitNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

4.10.2 生产Spring容器配置(集群唯一ID)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class IdGeneratorConfig {

    /**
     * 集群部署:不同节点配置不同 workerId、dataCenterId(0-31唯一)
     * 单机部署:固定 0 即可
     */
    @Bean
    public SnowflakeIdGenerator snowflakeIdGenerator() {
        return new SnowflakeIdGenerator(0, 0);
    }
}

生产核心要点:集群多节点必须保证 workerId+dataCenterId 全局唯一,彻底杜绝ID重复冲突,替代Sharding原生雪花算法。

4.11 生产主流:分片 + 读写分离 混合架构完整配置

企业90%高并发项目采用 分库分表 + 读写分离 组合架构:写请求走主库、读请求分摊从库,既解决单表海量数据瓶颈,又解决读并发压力,填补文档单一分片短板。

4.11.1 架构说明

  • 主库:ds0、ds1(负责写入、更新、删除)

  • 从库:ds0-slave、ds1-slave(负责查询读流量)

  • 路由规则:写入自动走主库,查询自动负载均衡走从库

  • 兼容原有分片规则、绑定表、广播表,零业务改造

4.11.2 完整生产YAML配置

spring:
  shardingsphere:
    # 主从多数据源配置
    datasource:
      names: ds0,ds0-slave,ds1,ds1-slave
      # 主库0
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db0?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      # 从库0
      ds0-slave:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db0_slave?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      # 主库1
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/sharding_db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      # 从库1
      ds1-slave:
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/sharding_db1_slave?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123456
    # 核心规则:读写分离 + 分片混合
    rules:
      # 读写分离规则
      readwrite-splitting:
        data-sources:
          # 库0主从组
          ds0-group:
            write-data-source-name: ds0
            read-data-source-names: [ds0-slave]
            load-balancer-name: round_robin
          # 库1主从组
          ds1-group:
            write-data-source-name: ds1
            read-data-source-names: [ds1-slave]
            load-balancer-name: round_robin
        # 负载均衡算法
        load-balancers:
          round_robin:
            type: ROUND_ROBIN
      # 分片规则(完全兼容原有配置)
      sharding:
        sharding-algorithms:
          order-mod:
            type: MOD
            props:
              sharding-count: 2
        tables:
          t_order:
            actual-data-nodes: ds${0..1}-group.t_order_${0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-mod
        binding-tables:
          - t_order,t_order_item
        broadcast-tables:
          - t_dict
    props:
      sql-show: false
      max.connections.size.per.query: 2

落地重点:读写分离与分片可无缝叠加,无需修改业务代码,Sharding-JDBC 自动接管读写路由,是企业生产标准架构。

4.12 生产零停机:分片平滑扩容 + 双写数据迁移完整方案

补充前文仅讲流程、无代码的短板,提供可直接落地的双写迁移代码、存量迁移方案、数据校验逻辑,支持2库扩4库、4库扩8库倍数扩容,全程零停机、零数据丢失。

4.12.1 核心扩容原理

哈希取模分片仅支持2的倍数扩容,扩容核心:新旧规则共存、双写兜底、存量迁移、灰度切流,杜绝停机维护、数据错乱。

4.12.2 双写兜底核心代码(增量数据无遗漏)

import org.springframework.stereotype.Component;
import javax.annotation.Resource;

/**
 * 分片扩容双写工具类(扩容过渡阶段专用)
 * 同时写入旧分片、新分片,保证增量数据不丢失
 * 扩容完成、数据一致后关闭双写
 */
@Component
public class ShardingDoubleWriteUtil {

    @Resource
    private OrderMapper oldOrderMapper;

    @Resource
    private OrderMapper newOrderMapper;

    /**
     * 新增数据双写
     */
    public void insertOrderDoubleWrite(Order order) {
        // 写入旧分片库
        oldOrderMapper.insert(order);
        // 同步写入新分片库
        newOrderMapper.insert(order);
    }

    /**
     * 更新数据双更
     */
    public void updateOrderDoubleWrite(Order order) {
        oldOrderMapper.updateById(order);
        newOrderMapper.updateById(order);
    }

    /**
     * 删除数据双删
     */
    public void deleteOrderDoubleWrite(Long orderId) {
        oldOrderMapper.deleteById(orderId);
        newOrderMapper.deleteById(orderId);
    }
}

4.12.3 存量数据迁移 + 数据校验方案

/**
 * 分片存量数据迁移任务
 * 分批查询旧库、写入新库、去重校验,避免全量查询OOM
 */
@Component
public class ShardingDataMigrateTask {

    @Resource
    private OrderMapper oldOrderMapper;
    @Resource
    private OrderMapper newOrderMapper;

    // 分批每页大小
    private static final int PAGE_SIZE = 1000;

    public void migrateData() {
        long startId = 0;
        while (true) {
            // 分批分页查询旧库存量数据
            List<Order> oldDataList = oldOrderMapper.selectPageByStartId(startId, PAGE_SIZE);
            if (oldDataList == null || oldDataList.isEmpty()) {
                break;
            }
            // 批量写入新库
            newOrderMapper.batchInsert(oldDataList);
            // 更新游标ID
            startId = oldDataList.get(oldDataList.size() - 1).getOrderId();
        }
        // 迁移完成后数据比对校验
        checkDataConsistency();
    }

    /**
     * 新旧库数据一致性校验
     */
    public void checkDataConsistency() {
        // 1. 总条数校验
        Long oldCount = oldOrderMapper.selectCount();
        Long newCount = newOrderMapper.selectCount();
        if (!oldCount.equals(newCount)) {
            throw new RuntimeException("数据迁移不一致,存在丢失数据");
        }
        // 2. 抽样详情校验
        // 可扩展字段级比对、差异数据修复
    }
}

4.12.4 完整零停机扩容落地步骤

  1. 环境搭建:部署新分片数据库,配置新旧双分片路由规则

  2. 开启双写:业务层接入双写代码,所有增量数据同步写入新旧库

  3. 存量迁移:执行分批迁移任务,同步历史数据,避免OOM

  4. 数据校验:条数比对、抽样校验,修复差异数据

  5. 灰度切流:逐步将读、写流量切换至新分片架构

  6. 下线旧库:观测线上稳定无报错后,关闭双写、下线旧分片库

4.13 生产监控告警 + 线上故障排查手册(生产刚需)

补齐文档最大生产短板:无监控、无排查方案、无故障预案。本章节覆盖核心监控指标、日志排查、常见线上故障、告警规则、修复方案,支撑线上稳定运行。

4.13.1 分片核心监控指标(必须接入监控平台)

  • 分片路由异常率:路由失败、分片键为空、路由错乱异常统计

  • 分片查询耗时:单分片、全分片广播查询耗时监控

  • 数据倾斜指标:各分片数据量、QPS差值监控,提前发现热点

  • 连接池使用率:Druid连接池活跃连接、等待连接监控,防连接耗尽

  • 分布式ID异常:ID生成失败、时钟回拨异常监控告警

  • 事务异常率:跨分片事务回滚、超时、不一致异常监控

4.13.2 线上日志排查规范

  • 生产关闭 sql-show,避免日志刷屏,自定义业务日志打印分片键、路由节点

  • 异常日志必须打印:分片键、目标库表、原始SQL、异常堆栈

  • 全分片广播查询日志单独打标,定时统计优化慢查询

4.13.3 线上高频故障 & 一键修复方案

  • 故障1:分片路由错乱、数据找不到 原因:分片键修改、路由规则变更、扩容未迁移全量数据 修复:核对分片算法、校验数据分布、补全迁移差异数据

  • 故障2:接口超时、数据库压力暴涨 原因:无分片键查询触发全分片广播 修复:强制业务携带分片键、新增无分片键查询黑名单拦截

  • 故障3:主键ID重复报错 原因:雪花算法worker-id重复、时钟回拨 修复:替换优化版雪花算法、集群节点唯一ID配置

  • 故障4:跨分片JOIN报错/数据错乱 原因:关联表未配置绑定表、分片规则不一致 修复:补充绑定表配置、统一关联表分片键与算法

  • 故障5:分页数据重复、丢失、错乱 原因:多分片原生limit offset分页 修复:替换为内存分页/游标分页方案

  • 故障6:连接池耗尽、数据库卡死 原因:单查询连接数过高、慢查询堆积 修复:优化连接池参数、限制单查询最大连接、清理慢SQL

4.13.4 生产告警阈值配置(通用标准)

  • 路由异常率 > 0.1%:触发告警,排查路由规则与分片键

  • 单分片QPS远超平均分片2倍:判定数据热点,触发热点优化告警

  • 查询耗时 > 1s:分片慢查询告警,优化SQL与索引

  • ID生成异常、事务异常:紧急告警,立即排查集群与事务问题

5. 开发坑点与解决方案

全部是我初学踩过的坑,直接记死:

  • 无分片键查询:触发全分片广播,数据量大直接雪崩

  • 跨分片JOIN:非绑定表关联直接报错/数据错乱

  • 随意改分片键值:数据路由错乱,无法自动修复

  • 非倍数扩容:取模分片强行加库,数据全部错位

5.1 分片SQL黑名单(初学者必记)

以下语法分片不支持/高危,开发直接禁止使用:

  • limit n,-1 无边界分页,直接数据错乱

  • ❌ 无分片键 GROUP BY、COUNT、SUM 全局聚合

  • ❌ 跨分片复杂JOIN、嵌套子查询

  • ❌ 存储过程、自定义函数、批量跨分片DDL

5.2 分片分页BUG 终极解决方案(可直接运行)

问题回顾:多分片下原生limit offset分页错乱、丢数据、重复数据。

方案一:内存全局分页(中小数据通用)

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 分片通用分页工具类
 * 解决多分片分页错乱问题
 */
public class ShardingPageUtil {
    // 全局排序+分页
    public static <T extends Comparable<T>> List<T> page(List<T> allShardingData, int pageNum, int pageSize) {
        // 全局排序
        List<T> sorted = allShardingData.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        // 分页截取
        int start = (pageNum - 1) * pageSize;
        int end = Math.min(start + pageSize, sorted.size());
        return sorted.subList(start, end);
    }
}

方案二:游标分页(深分页/大数据量最优)

彻底抛弃offset,通过最大ID游标查询,无性能衰减,生产首选。

// 游标分页SQL示例
// 只传上一页最后一条ID,不传偏移量
SELECT * FROM t_order WHERE order_id > #{lastId} ORDER BY order_id LIMIT 10

5.3 数据倾斜 & 热点分片 完整落地解决方案

成因:大用户流量集中、热点商品、单一哈希打散不均、时序分片天然热点。

全套解决思路(面试满分答案)

  • 热点隔离:超大用户、热点数据单独路由专属分片,隔离流量

  • 复合分片:分片键+随机后缀二次哈希,打散集中数据

  • 虚拟分片映射:逻辑分片与物理分片解耦,支持动态重分布

  • 冷热分离:热数据哈希分片保均衡,冷数据范围分片方便归档

5.4 平滑扩容 & 数据迁移极简流程(初学者能懂)

哈希分片只能倍数扩容,标准零停机迁移流程:

  1. 新库新表环境搭建,配置新旧双路由规则

  2. 开启新旧库双写,保证增量数据无遗漏

  3. 分批迁移历史存量数据

  4. 全量数据比对、去重、修复差异

  5. 灰度切流:10%→50%→100% 切换新分片架构

  6. 稳定观测后下线旧库

5.5 分布式事务等级完整补全

分片事务分3个等级,按需选用:

  • 弱XA(最终一致性):Sharding原生支持、高性能、无锁,普通业务通用,允许极短时间数据不一致

  • 强XA(强一致性):两阶段提交、性能差、阻塞风险高,生产极少使用

  • BASE柔性事务:Sharding+Seata AT,高并发高可用,支付、交易核心业务必备

5.6 冷热数据分层方案(工程落地标配)

核心思想:热数据走哈希分片保证均衡读写,冷数据走范围分片归档,减轻主分片压力。

落地逻辑

  • 近3个月热数据:多库多表哈希分片,支撑高并发读写

  • 3个月前冷数据:按月范围分片归档,单独存储、不占用核心性能

6. 高频面试题(21道)

本章整理21道分片核心面试真题,无重复、无冗余,覆盖原理、概念、实战坑点、架构落地,答案精简标准,可直接背诵用于面试和复盘。

6.1 基础原理题(Q1-Q5)

Q1:为什么需要分库分表?什么场景下才使用?

答:单表数据量超500W、单库连接数/IO瓶颈、大表DDL阻塞、索引失效查询变慢时需要分片。分片是数据库优化的最后手段,优先使用索引优化、读写分离、冷热归档,非必要不分片。

Q2:分库分表为什么不能用MySQL自增主键?如何解决?

答:多数据库独立维护自增计数器,多库写入会出现主键ID重复。生产统一使用全局分布式ID,主流方案为雪花算法、百度UidGenerator,杜绝ID冲突。

Q3:简述Sharding-JDBC的核心执行流程?

答:整体流程:SQL解析 → 分片键提取与路由计算 → SQL改写 → 多分片并行执行 → 结果聚合、排序、分页 → 返回最终数据,业务层无感知。

Q4:雪花算法时钟回拨问题成因及解决方案?

答:服务器时间同步、时间回退时,会生成重复ID,引发主键冲突。解决方案:代码缓存上一次时间戳,检测到时间回拨则阻塞等待,或使用优化版UidGenerator规避该问题。

Q5:分片架构下为什么不建议无分片键查询?

答:无分片键无法精准路由,会触发全库全表广播查询,遍历所有分片,数据量大时会导致接口超时、数据库压力雪崩,是生产高危操作。

6.2 核心概念题(Q6-Q10)

Q6:哈希分片和范围分片的区别及适用场景?

答:哈希分片按分片键取模路由,数据均匀无热点,适合订单、交易核心业务,仅支持2的倍数扩容;范围分片按时间、ID区间路由,扩容无需迁移历史数据,但末尾分片易产生热点,适合日志、流水等非核心时序数据。

Q7:绑定表、广播表的作用和业务场景?

答:绑定表:分片规则一致的关联表绑定,保证关联数据落在同一分片,解决跨分片无法JOIN问题,适用于订单与订单详情表;广播表:所有分片库同步存储相同数据,适用于字典、配置、地区等小表,避免跨库查询。

Q8:分库和分表的核心区别是什么?

答:分表是单库拆分为多张表,解决单表数据量大、查询慢的问题;分库是拆分多个数据库,分摊单库CPU、IO、连接数压力,解决高并发瓶颈,生产大多采用分库+分表组合方案。

Q9:Sharding-JDBC和MyCat的选型区别?

答:Sharding-JDBC是客户端嵌入式框架,无中间件转发、性能高、部署简单、无单点故障,适配Spring生态,是新项目首选;MyCat是独立代理中间件,存在网络损耗、运维复杂、有单点风险,目前基本被淘汰。

Q10:分片键的设计核心原则是什么?

答:1、业务绝大多数查询必须携带分片键;2、分片键打散均匀,避免数据热点;3、关联业务尽量共用分片键,适配绑定表规则;4、分片键确定后不可修改。

6.3 实战坑点题(Q11-Q15)

Q11:分片分页错乱的根本原因和解决方案?

答:根本原因:多分片独立分页,单分片局部结果无法代表全局数据,直接使用limit offset会出现数据重复、丢失、错乱。解决方案:中小数据使用内存全局排序分页,大数据量使用游标ID分页。

Q12:分片架构下有哪些SQL黑名单,禁止使用?

答:禁止无分片键的GROUP BY、SUM、COUNT等全局聚合;禁止跨分片复杂JOIN、嵌套子查询;禁止limit -1无边界分页;禁止跨分片批量DDL、存储过程、自定义函数。

Q13:什么是数据热点与数据倾斜?如何解决?

答:部分分片数据量、QPS远超其他分片,造成单点性能瓶颈。解决方案:热点数据单独隔离、采用复合分片二次打散、虚拟分片映射、冷热数据分层存储。

Q14:哈希分片为什么只能2的倍数扩容?

答:2/4/8倍数扩容可保证半数数据路由规则不变,仅需迁移半数数据,扩容成本低;非倍数扩容会改变所有取模路由规则,需要全量迁移所有数据,风险和成本极高。

Q15:分片场景下如何解决跨库JOIN问题?

答:四种主流方案:1、业务设计绑定表,同分片直接JOIN;2、核心字段冗余,消除关联查询;3、业务层单次查询多分片,手动组装数据;4、核心业务引入ES替代复杂关联查询。

6.4 架构落地题(Q16-Q21)

Q16:分片架构如何实现平滑扩容、零停机数据迁移?

答:1、搭建新分片环境,配置新旧双路由规则;2、开启新旧库双写,保障增量数据无遗漏;3、分批迁移历史存量数据;4、数据校验比对、修复差异;5、灰度切流逐步切换新架构;6、稳定后下线旧分片。

Q17:分片架构下分布式事务如何处理?

答:普通业务使用Sharding-JDBC原生弱XA最终一致性事务,性能高、无锁;核心支付、交易业务,结合Seata AT实现强一致性柔性事务,规避跨分片事务不一致问题。

Q18:冷热数据分层的落地思路?

答:近3个月热数据采用哈希分片,保证数据均匀、支撑高并发读写;历史冷数据采用范围分片按月归档,单独存储,不占用核心数据库性能,实现性能与存储平衡。

Q19:垂直拆分和水平拆分的区别和适用场景?

答:垂直拆分按业务、字段拆分,解决单表字段冗余、业务耦合问题;水平拆分按规则拆分数据,解决单表海量数据、高并发瓶颈。海量数据高并发场景,优先使用水平分片。

Q20:SpringCloud微服务如何适配Sharding-JDBC?

答:分片规则、数据源、算法全部下沉至数据服务,业务微服务仅通过Feign调用数据接口,零分片配置;全局分布式ID统一由数据服务生成,禁止微服务跨库查询、跨服务JOIN。

Q21:生产环境分片上线核心校验要点有哪些?

答:1、生产关闭SQL打印,避免日志泄露与刷屏;2、集群环境雪花算法worker-id保证唯一;3、优化连接池参数,防止连接耗尽;4、核心查询强制携带分片键;5、绑定表、广播表配置完整;6、严格禁用黑名单SQL语法。

7. 学习总结

  • 分片能不用就不用,优先常规优化

  • 分片键是一切的核心,设计错了全盘重构

  • 哈希分片保均衡、范围分片保扩容,各有取舍

  • 绑定表解JOIN、广播表解字典、精准算法解路由

  • 开发最大禁忌:无分片键查询、跨分片JOIN、随意改分片键