ShardingSphere-JDBC的基础使用

27 阅读22分钟

ShardingSphere-JDBC基础使用

一、ShardingSphere-JDBC 简介

1.1 什么是 ShardingSphere-JDBC?

ShardingSphere-JDBC 是 Apache ShardingSphere 的第二款核心产品,定位为 轻量级 Java 框架

  • 形态:它以 Jar 包形式存在,作为第三方依赖引入项目。
  • 原理:它基于 JDBC 协议进行扩展,对 JDBC 驱动进行增强。在 Java 的 JDBC 层提供额外服务,以插件化的形式存在。
  • 核心能力:它将数据库视为一个逻辑整体,应用层只需面向逻辑库和逻辑表进行开发。ShardingSphere-JDBC 负责在底层完成 SQL 解析、路由、改写、执行和结果归并,从而透明地实现 分库分表读写分离数据加密等功能。
  • 特点无中心化(无需部署额外的中间件服务器),性能损耗极低(仅增加一次 SQL 解析和路由开销),易于集成。

对比 ShardingSphere-Proxy

  • JDBC:嵌入应用内部,适合 Java 语言栈,性能更高,运维成本低(无独立进程)。
  • Proxy:独立的数据库代理服务(类似 MySQL 服务器),支持多语言(Python, Go, PHP 等),适合异构语言环境或 DBA 统一管理。

1.2 产品对比

产品定位适用场景特点
ShardingSphere-JDBC轻量级 Java 框架Java 应用jar 包形式,直连数据库,性能高
ShardingSphere-Proxy透明数据库代理异构语言、DBA 友好独立部署,支持 MySQL 协议
ShardingSphere-Sidecar云原生数据库代理Kubernetes 环境DaemonSet 形式,Database Mesh

二、ShardingSphere-JDBC 核心功能

2.1 数据分片

数据分片是 ShardingSphere-JDBC 最核心的功能,包括:

垂直分片

  • 垂直分库:按照业务模块将表分布在不同的数据库中
  • 垂直分表:将一张表的字段拆分到多张表(如将大字段拆分)

水平分片

  • 水平分库:将同一张表的数据分散到多个数据库
  • 水平分表:将同一张表的数据分散到多张表

2.2 读写分离

  • 自动路由:根据 SQL 语义自动将写操作路由到主库,读操作路由到从库
  • 负载均衡:支持轮询、随机、权重等多种从库负载均衡策略
  • 强制主库路由:支持通过 Hint 强制将读操作路由到主库,保证数据一致性

2.3 分布式事务

  • XA 事务:基于两阶段提交的强一致性事务
  • BASE 事务:基于 Seata 的柔性事务,最终一致性
  • 本地事务:默认使用,适用于单一分片

2.4 数据脱敏

  • 动态脱敏:对查询结果中的敏感数据进行加密/解密
  • 静态脱敏:对存储的数据进行加密

2.5 数据库治理

  • 配置动态化:支持配置的动态更新,无需重启应用
  • 链路追踪:集成 OpenTracing,监控 SQL 执行链路
  • 弹性伸缩:支持数据节点的动态扩缩容

三、核心概念

3.1 分片相关概念

概念说明
逻辑表水平拆分的数据库表的相同逻辑和数据结构的总称,例如订单表拆分为 t_order_0、t_order_1,逻辑表名为 t_order
真实表实际存在于数据库中的物理表,如 t_order_0、t_order_1
数据节点数据分片的最小单元,由数据源名称和数据表组成,如 ds0.t_order_0
分片键用于分片的数据库字段,如 order_id、user_id
分片算法根据分片键的值计算数据应该存储在哪个分片上的算法

3.2 分库分表概念详解

什么是分库分表?

分库分表是将大型数据库中的数据按照一定的规则拆分到多个数据库和多个表中的过程。

  • 分库:将数据分散到不同的数据库实例
  • 分表:将数据分散到同一个数据库中的不同表

为什么要分库分表?

  1. 性能瓶颈:单表数据量过大时,索引变大,查询性能下降
  2. 数据库连接数限制:单库连接数有限,无法支撑高并发
  3. IO 瓶颈:单库磁盘 IO 有限,无法支撑大量读写
  4. 备份恢复时间:大表备份恢复时间长

什么时候需要分库分表?

场景指标建议
单表数据量> 500万行 或 > 2GB考虑分表
单库连接数> 80% 上限考虑分库
TPS/QPS> 1000考虑分库分表
磁盘 IO> 70%考虑分库
备份时间> 4小时考虑分表

3.3 表关系类型

类型说明示例注意事项
绑定表分片规则一致的主表和子表t_order 和 t_order_item 都按照 order_id 分片避免关联查询产生笛卡尔积
广播表所有数据源中都存在的表,表结构和数据完全一致字典表、配置表不正确配置可能导致项目无法启动
单表所有数据源中只存在唯一一张的表数据量小、无需分片的表-

四、ShardingSphere-JDBC 优缺点

4.1 优点

优点说明
性能高直连数据库,无网络开销
兼容性好兼容所有 JDBC 规范和 ORM 框架
功能丰富分库分表、读写分离、分布式事务一站式解决
配置灵活支持 Java API、YAML、Spring Boot Starter 多种配置方式
社区活跃Apache 顶级项目,文档完善
扩展性强支持自定义分片算法

4.2 缺点

缺点说明
代码侵入性需要在应用层引入依赖
不支持跨库关联查询多分片关联查询需应用层处理
分布式事务性能XA 事务性能开销大
SQL 支持限制部分复杂 SQL 不支持
运维复杂度需要管理分片规则

环境准备与基础配置

五、数据库初始化脚本

5.1 创建数据库

-- 创建分库
CREATE DATABASE IF NOT EXISTS ds0 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS ds1 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS ds2 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

5.2 创建订单表(分片表示例)

ds0ds1 数据库中分别执行:

-- 订单表 - 分3张表
CREATE TABLE `t_order_0` (
  `order_id` bigint(20) NOT NULL COMMENT '订单ID(分布式ID)',
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `order_name` varchar(100) NOT NULL COMMENT '订单名称',
  `amount` decimal(10,2) DEFAULT '0.00' COMMENT '订单金额',
  `order_status` tinyint(4) DEFAULT '0' COMMENT '订单状态 0-待支付 1-已支付 2-已取消',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`order_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表0';

CREATE TABLE `t_order_1` (
  `order_id` bigint(20) NOT NULL,
  `user_id` int(11) NOT NULL,
  `order_name` varchar(100) NOT NULL,
  `amount` decimal(10,2) DEFAULT '0.00',
  `order_status` tinyint(4) DEFAULT '0',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`order_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表1';

CREATE TABLE `t_order_2` (
  `order_id` bigint(20) NOT NULL,
  `user_id` int(11) NOT NULL,
  `order_name` varchar(100) NOT NULL,
  `amount` decimal(10,2) DEFAULT '0.00',
  `order_status` tinyint(4) DEFAULT '0',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`order_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表2';

5.3 创建订单项表(绑定表示例)

ds0ds1 数据库中分别执行:

-- 订单项表 - 分3张表
CREATE TABLE `t_order_item_0` (
  `item_id` bigint(20) NOT NULL COMMENT '订单项ID',
  `order_id` bigint(20) NOT NULL COMMENT '订单ID',
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `product_name` varchar(100) NOT NULL COMMENT '商品名称',
  `price` decimal(10,2) NOT NULL COMMENT '商品价格',
  `quantity` int(11) DEFAULT '1' COMMENT '商品数量',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`item_id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表0';

CREATE TABLE `t_order_item_1` (
  `item_id` bigint(20) NOT NULL,
  `order_id` bigint(20) NOT NULL,
  `user_id` int(11) NOT NULL,
  `product_name` varchar(100) NOT NULL,
  `price` decimal(10,2) NOT NULL,
  `quantity` int(11) DEFAULT '1',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`item_id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表1';

CREATE TABLE `t_order_item_2` (
  `item_id` bigint(20) NOT NULL,
  `order_id` bigint(20) NOT NULL,
  `user_id` int(11) NOT NULL,
  `product_name` varchar(100) NOT NULL,
  `price` decimal(10,2) NOT NULL,
  `quantity` int(11) DEFAULT '1',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`item_id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单项表2';

5.4 创建广播表

ds0ds1 数据库中分别执行:

-- 字典表(广播表)
CREATE TABLE `t_dict` (
  `dict_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '字典ID',
  `dict_code` varchar(50) NOT NULL COMMENT '字典编码',
  `dict_name` varchar(100) NOT NULL COMMENT '字典名称',
  `dict_value` varchar(200) NOT NULL COMMENT '字典值',
  `sort_order` int(11) DEFAULT '0' COMMENT '排序',
  `status` tinyint(4) DEFAULT '1' COMMENT '状态 1-启用 0-禁用',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`dict_id`),
  UNIQUE KEY `uk_dict_code` (`dict_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='字典表';

-- 配置表(广播表)
CREATE TABLE `t_config` (
  `config_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '配置ID',
  `config_key` varchar(100) NOT NULL COMMENT '配置键',
  `config_value` varchar(500) NOT NULL COMMENT '配置值',
  `remark` varchar(200) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`config_id`),
  UNIQUE KEY `uk_config_key` (`config_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='配置表';

5.5 插入测试数据

-- 插入字典数据(在所有库执行)
INSERT INTO t_dict (dict_code, dict_name, dict_value, sort_order) VALUES 
('order_status', '待支付', '0', 1),
('order_status', '已支付', '1', 2),
('order_status', '已取消', '2', 3),
('product_type', '电子产品', 'electronic', 1),
('product_type', '服装', 'clothing', 2),
('product_type', '食品', 'food', 3);

-- 插入配置数据(在所有库执行)
INSERT INTO t_config (config_key, config_value, remark) VALUES 
('order_timeout', '30', '订单超时时间(分钟)'),
('max_order_amount', '10000', '单笔订单最大金额'),
('support_payment', 'alipay,wechat', '支持的支付方式');

六、Maven 依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>sharding-jdbc-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <shardingsphere.version>5.3.2</shardingsphere.version>
        <mybatis-plus.version>3.5.2</mybatis-plus.version>
        <druid.version>1.2.15</druid.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- ShardingSphere-JDBC -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc</artifactId>
            <version>${shardingsphere.version}</version>
        </dependency>

        <!-- MyBatis Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <!-- Druid连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

七、基础实体类与Mapper

package com.example.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@TableName("t_order")
public class Order {
    @TableId
    private Long orderId;
    private Integer userId;
    private String orderName;
    private BigDecimal amount;
    private Integer orderStatus;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

@Data
@TableName("t_order_item")
public class OrderItem {
    @TableId
    private Long itemId;
    private Long orderId;
    private Integer userId;
    private String productName;
    private BigDecimal price;
    private Integer quantity;
    private LocalDateTime createTime;
}

@Data
@TableName("t_dict")
public class Dict {
    @TableId
    private Integer dictId;
    private String dictCode;
    private String dictName;
    private String dictValue;
    private Integer sortOrder;
    private Integer status;
    private LocalDateTime createTime;
}
package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.Order;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {
}

@Mapper
public interface DictMapper extends BaseMapper<Dict> {
}

分片策略配置与示例

八、标准分片策略(Precise + Range)

8.1 自定义精确分片算法

package com.example.algorithm.standard;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 自定义精确分片算法 - 数据库分片
 * 根据 user_id 取模选择数据库
 */
@Component
public class DatabasePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> {
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, 
                            PreciseShardingValue<Integer> shardingValue) {
        // availableTargetNames: [ds0, ds1]
        // shardingValue.getValue(): user_id的值
        
        Integer userId = shardingValue.getValue();
        String logicTableName = shardingValue.getLogicTableName();
        
        // 根据user_id取模选择数据库
        int index = userId % availableTargetNames.size();
        
        for (String targetName : availableTargetNames) {
            if (targetName.endsWith(String.valueOf(index))) {
                System.out.println("数据库分片 - 逻辑表: " + logicTableName + 
                                 ", user_id: " + userId + 
                                 ", 选择: " + targetName);
                return targetName;
            }
        }
        
        throw new UnsupportedOperationException("无法找到合适的数据源");
    }
}
package com.example.algorithm.standard;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 自定义精确分片算法 - 表分片
 * 根据 order_id 取模选择表
 */
@Component
public class TablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, 
                            PreciseShardingValue<Long> shardingValue) {
        // availableTargetNames: [t_order_0, t_order_1, t_order_2]
        // shardingValue.getValue(): order_id的值
        
        Long orderId = shardingValue.getValue();
        String logicTableName = shardingValue.getLogicTableName();
        
        // 根据order_id取模选择表
        int index = (int) (orderId % availableTargetNames.size());
        
        for (String targetName : availableTargetNames) {
            if (targetName.endsWith(String.valueOf(index))) {
                System.out.println("表分片 - 逻辑表: " + logicTableName + 
                                 ", order_id: " + orderId + 
                                 ", 选择: " + targetName);
                return targetName;
            }
        }
        
        throw new UnsupportedOperationException("无法找到合适的表");
    }
}
package com.example.algorithm.standard;

import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * 自定义范围分片算法 - 表范围查询
 * 处理 BETWEEN AND、>、< 等范围查询
 */
@Component
public class TableRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                        RangeShardingValue<Long> shardingValue) {
        // availableTargetNames: [t_order_0, t_order_1, t_order_2]
        // shardingValue.getValueRange(): 范围值
        
        Set<String> result = new LinkedHashSet<>();
        
        // 获取范围查询的上下界
        Long lower = shardingValue.getValueRange().hasLowerBound() 
            ? shardingValue.getValueRange().lowerEndpoint() : null;
        Long upper = shardingValue.getValueRange().hasUpperBound() 
            ? shardingValue.getValueRange().upperEndpoint() : null;
        
        System.out.println("范围查询 - 表: " + shardingValue.getLogicTableName() + 
                         ", 范围: [" + lower + " - " + upper + "]");
        
        // 简化处理:返回所有可能的分片
        // 实际业务中可以根据范围计算需要查询哪些分片
        if (lower != null && upper != null) {
            // 计算范围覆盖的分片索引
            int minIndex = (int) (lower / 1000) % availableTargetNames.size();
            int maxIndex = (int) (upper / 1000) % availableTargetNames.size();
            
            for (String targetName : availableTargetNames) {
                for (int i = minIndex; i <= maxIndex; i++) {
                    if (targetName.endsWith(String.valueOf(i))) {
                        result.add(targetName);
                        break;
                    }
                }
            }
        } else {
            // 没有明确范围,返回所有分片
            result.addAll(availableTargetNames);
        }
        
        return result;
    }
}

8.2 application.yml 配置(标准分片策略)

spring:
  # ShardingSphere配置
  shardingsphere:
    # 是否启用ShardingSphere
    enabled: true
    
    # 数据源配置
    datasource:
      # 数据源名称列表,多个用逗号分隔
      names: ds0, ds1
      
      # ds0数据源配置
      ds0:
        # 使用Druid连接池
        type: com.alibaba.druid.pool.DruidDataSource
        # MySQL驱动
        driver-class-name: com.mysql.cj.jdbc.Driver
        # 数据库连接URL
        jdbc-url: jdbc:mysql://localhost:3306/ds0?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
        # 用户名
        username: root
        # 密码
        password: 123456
        # Druid连接池配置
        druid:
          # 初始连接数
          initial-size: 5
          # 最小空闲连接数
          min-idle: 5
          # 最大活跃连接数
          max-active: 20
          # 获取连接等待超时时间
          max-wait: 60000
          # 检测连接是否有效的SQL
          validation-query: SELECT 1
          # 空闲连接检测间隔
          time-between-eviction-runs-millis: 60000
          # 连接最小生存时间
          min-evictable-idle-time-millis: 300000
          # 是否缓存PreparedStatement
          pool-prepared-statements: true
          # 缓存PreparedStatement的最大数量
          max-pool-prepared-statement-per-connection-size: 20
      
      # ds1数据源配置(与ds0相同结构)
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds1?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
        username: root
        password: 123456
        druid:
          initial-size: 5
          min-idle: 5
          max-active: 20
          max-wait: 60000
          validation-query: SELECT 1
          time-between-eviction-runs-millis: 60000
          min-evictable-idle-time-millis: 300000
          pool-prepared-statements: true
          max-pool-prepared-statement-per-connection-size: 20
    
    # 分片规则配置
    rules:
      # 分片规则
      sharding:
        # 表配置
        tables:
          # 订单表(逻辑表名)
          t_order:
            # 实际数据节点:ds0和ds1中的t_order_0、t_order_1、t_order_2
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..2}
            # 分库策略
            database-strategy:
              # 标准分片策略
              standard:
                # 分片键
                sharding-column: user_id
                # 分片算法名称(引用下方定义的算法)
                sharding-algorithm-name: database-precise
            # 分表策略
            table-strategy:
              standard:
                # 分片键
                sharding-column: order_id
                # 精确分片算法名称
                sharding-algorithm-name: table-precise
                # 范围分片算法名称(可选)
                range-sharding-algorithm-name: table-range
            # 主键生成策略
            key-generate-strategy:
              # 主键列名
              column: order_id
              # 主键生成器名称
              key-generator-name: snowflake
          
          # 订单项表(绑定表)
          t_order_item:
            # 实际数据节点
            actual-data-nodes: ds$->{0..1}.t_order_item_$->{0..2}
            # 分库策略(与订单表一致)
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: database-precise
            # 分表策略(与订单表一致)
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: table-precise
                range-sharding-algorithm-name: table-range
            # 主键生成策略
            key-generate-strategy:
              column: item_id
              key-generator-name: snowflake
        
        # 绑定表配置(关联查询优化)
        binding-tables:
          - t_order, t_order_item
        
        # 广播表配置(所有数据源都存在)
        broadcast-tables:
          - t_dict
          - t_config
        
        # 分片算法配置
        sharding-algorithms:
          # 数据库分片算法
          database-precise:
            # 使用自定义类
            type: CLASS_BASED
            props:
              # 策略类型:standard(标准)、complex(复合)、hint(Hint)
              strategy: standard
              # 自定义算法实现类
              algorithmClassName: com.example.algorithm.standard.DatabasePreciseShardingAlgorithm
          
          # 表精确分片算法
          table-precise:
            type: CLASS_BASED
            props:
              strategy: standard
              algorithmClassName: com.example.algorithm.standard.TablePreciseShardingAlgorithm
          
          # 表范围分片算法
          table-range:
            type: CLASS_BASED
            props:
              strategy: standard
              algorithmClassName: com.example.algorithm.standard.TableRangeShardingAlgorithm
        
        # 主键生成器配置
        key-generators:
          # 雪花算法主键生成器
          snowflake:
            type: SNOWFLAKE
            props:
              # 工作节点ID(分布式环境需唯一)
              worker-id: 1
    
    # 属性配置
    props:
      # 是否打印SQL
      sql-show: true
      # SQL是否简化打印
      sql-simple: false
      # 工作线程数
      executor-size: 20

九、复合分片策略(Complex Keys)

9.1 自定义复合分片算法

package com.example.algorithm.complex;

import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 复合分片算法
 * 根据 user_id 和 order_date 共同决定分片
 */
@Component
public class CustomComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm<Comparable<?>> {
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                        ComplexKeysShardingValue<Comparable<?>> shardingValue) {
        
        String logicTableName = shardingValue.getLogicTableName();
        
        // 获取分片键的值
        Map<String, Collection<Comparable<?>>> columnValues = 
            shardingValue.getColumnNameAndShardingValuesMap();
        
        Collection<Comparable<?>> userIdValues = columnValues.getOrDefault("user_id", 
            Collections.emptyList());
        Collection<Comparable<?>> orderDateValues = columnValues.getOrDefault("order_date", 
            Collections.emptyList());
        
        System.out.println("复合分片 - 逻辑表: " + logicTableName);
        System.out.println("user_id值: " + userIdValues);
        System.out.println("order_date值: " + orderDateValues);
        
        Set<String> result = new LinkedHashSet<>();
        
        // 如果同时提供了user_id和order_date
        if (!userIdValues.isEmpty() && !orderDateValues.isEmpty()) {
            for (Comparable<?> userId : userIdValues) {
                for (Comparable<?> orderDate : orderDateValues) {
                    // 组合算法:将user_id和月份组合
                    int month = 1;
                    if (orderDate instanceof LocalDateTime) {
                        month = ((LocalDateTime) orderDate).getMonthValue();
                    }
                    
                    int combinedHash = Objects.hash(userId, month);
                    int index = Math.abs(combinedHash) % availableTargetNames.size();
                    
                    addTargetByIndex(result, availableTargetNames, index);
                }
            }
        }
        // 如果只提供了user_id
        else if (!userIdValues.isEmpty()) {
            for (Comparable<?> userId : userIdValues) {
                int index = Math.abs(userId.hashCode()) % availableTargetNames.size();
                addTargetByIndex(result, availableTargetNames, index);
            }
        }
        // 如果只提供了order_date
        else if (!orderDateValues.isEmpty()) {
            for (Comparable<?> orderDate : orderDateValues) {
                int month = 1;
                if (orderDate instanceof LocalDateTime) {
                    month = ((LocalDateTime) orderDate).getMonthValue();
                }
                int index = month % availableTargetNames.size();
                addTargetByIndex(result, availableTargetNames, index);
            }
        }
        
        return result.isEmpty() ? availableTargetNames : result;
    }
    
    private void addTargetByIndex(Set<String> result, Collection<String> targets, int index) {
        for (String target : targets) {
            if (target.endsWith(String.valueOf(index))) {
                result.add(target);
                break;
            }
        }
    }
    
    @Override
    public Properties getProps() {
        return new Properties();
    }
    
    @Override
    public void init(Properties props) {
    }
}

9.2 application.yml 配置(复合分片策略)

spring:
  shardingsphere:
    enabled: true
    
    # 数据源配置(使用Druid)
    datasource:
      names: ds0, ds1, ds2
      
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds0?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          min-idle: 5
          validation-query: SELECT 1
      
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds1?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          min-idle: 5
          validation-query: SELECT 1
      
      ds2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds2?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          min-idle: 5
          validation-query: SELECT 1
    
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..2}.t_order_$->{0..2}
            # 复合分片策略
            database-strategy:
              complex:
                # 多个分片键,用逗号分隔
                sharding-columns: user_id, order_date
                # 复合分片算法名称
                sharding-algorithm-name: complex-algorithm
            
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake
        
        # 分片算法配置
        sharding-algorithms:
          # 复合分片算法
          complex-algorithm:
            type: CLASS_BASED
            props:
              # 策略类型:complex
              strategy: complex
              # 自定义算法实现类
              algorithmClassName: com.example.algorithm.complex.CustomComplexShardingAlgorithm
        
        # 主键生成器
        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 1
    
    props:
      sql-show: true

十、Hint 分片策略

10.1 自定义 Hint 分片算法

package com.example.algorithm.hint;

import org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingValue;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * Hint分片算法
 * 通过HintManager传入分片值,不依赖SQL
 */
@Component
public class CustomHintShardingAlgorithm implements HintShardingAlgorithm<Integer> {
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                        HintShardingValue<Integer> shardingValue) {
        
        String logicTableName = shardingValue.getLogicTableName();
        Collection<Integer> values = shardingValue.getValues();
        
        System.out.println("Hint分片 - 逻辑表: " + logicTableName);
        System.out.println("Hint值: " + values);
        
        Set<String> result = new LinkedHashSet<>();
        
        if (values.isEmpty()) {
            // 没有Hint值,根据默认策略返回
            return Collections.emptySet();
        }
        
        // 根据Hint值选择分片
        for (Integer value : values) {
            int index = value % availableTargetNames.size();
            
            for (String targetName : availableTargetNames) {
                if (targetName.endsWith(String.valueOf(index))) {
                    result.add(targetName);
                    break;
                }
            }
        }
        
        return result;
    }
    
    @Override
    public Properties getProps() {
        return new Properties();
    }
    
    @Override
    public void init(Properties props) {
    }
}

10.2 application.yml 配置(Hint分片策略)

spring:
  shardingsphere:
    enabled: true
    
    datasource:
      names: ds0, ds1
      
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds0?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          validation-query: SELECT 1
      
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds1?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          validation-query: SELECT 1
    
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..2}
            # Hint分片策略
            database-strategy:
              hint:
                # Hint分片算法名称
                sharding-algorithm-name: hint-algorithm
        
        # 分片算法配置
        sharding-algorithms:
          hint-algorithm:
            type: CLASS_BASED
            props:
              # 策略类型:hint
              strategy: hint
              # 自定义算法实现类
              algorithmClassName: com.example.algorithm.hint.CustomHintShardingAlgorithm
    
    props:
      sql-show: true

十一、Inline 表达式分片

11.1 application.yml 配置(Inline表达式)

spring:
  shardingsphere:
    enabled: true
    
    datasource:
      names: ds0, ds1
      
      ds0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds0?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          validation-query: SELECT 1
      
      ds1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/ds1?useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
        druid:
          initial-size: 5
          max-active: 20
          validation-query: SELECT 1
    
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..2}
            # 分库策略
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: database-inline
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: table-inline
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake
        
        # 分片算法配置
        sharding-algorithms:
          # Inline分库算法
          database-inline:
            type: INLINE
            props:
              # Groovy表达式:根据user_id取模
              algorithm-expression: ds${user_id % 2}
          
          # Inline分表算法
          table-inline:
            type: INLINE
            props:
              # Groovy表达式:根据order_id取模
              algorithm-expression: t_order_${order_id % 3}
              # 是否允许范围查询(可能会全路由)
              allow-range-query-with-inline-sharding: true
        
        # 主键生成器
        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 1
    
    props:
      sql-show: true

Service 层与 SQL 示例

十二、Service 实现类

package com.example.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.entity.Order;
import com.example.entity.OrderItem;
import com.example.mapper.OrderItemMapper;
import com.example.mapper.OrderMapper;
import org.apache.shardingsphere.infra.hint.HintManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;
    
    @Resource
    private OrderItemMapper orderItemMapper;

    /**
     * 1. 插入订单 - 自动分片
     * SQL: INSERT INTO t_order (order_id, user_id, order_name, amount, order_status, create_time) 
     *      VALUES (?, ?, ?, ?, ?, ?)
     */
    @Transactional
    public Order createOrder(Integer userId, String orderName, BigDecimal amount) {
        Order order = new Order();
        order.setUserId(userId);
        order.setOrderName(orderName);
        order.setAmount(amount);
        order.setOrderStatus(0);
        order.setCreateTime(LocalDateTime.now());
        
        // order_id由雪花算法自动生成
        orderMapper.insert(order);
        System.out.println("插入订单成功,order_id: " + order.getOrderId() + 
                          ", user_id: " + userId + 
                          ", 路由到: ds" + (userId % 2) + ".t_order_" + (order.getOrderId() % 3));
        
        return order;
    }

    /**
     * 2. 创建订单和订单项(绑定表)
     * SQL: INSERT INTO t_order_item (item_id, order_id, user_id, product_name, price, quantity, create_time)
     *      VALUES (?, ?, ?, ?, ?, ?, ?)
     */
    @Transactional
    public Order createOrderWithItems(Integer userId, String orderName, BigDecimal amount, 
                                      List<String> products) {
        // 创建订单
        Order order = createOrder(userId, orderName, amount);
        
        // 创建订单项
        for (String product : products) {
            OrderItem item = new OrderItem();
            item.setOrderId(order.getOrderId());
            item.setUserId(userId);
            item.setProductName(product);
            item.setPrice(amount.divide(new BigDecimal(products.size()), 2, BigDecimal.ROUND_HALF_UP));
            item.setQuantity(1);
            item.setCreateTime(LocalDateTime.now());
            
            orderItemMapper.insert(item);
            System.out.println("插入订单项成功,item_id: " + item.getItemId() + 
                              ", 与订单在同一分片: ds" + (userId % 2) + ".t_order_item_" + (order.getOrderId() % 3));
        }
        
        return order;
    }

    /**
     * 3. 根据订单ID精确查询(携带分片键)
     * SQL: SELECT * FROM t_order WHERE order_id = ? AND user_id = ?
     */
    public Order getOrderById(Long orderId, Integer userId) {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("order_id", orderId);
        wrapper.eq("user_id", userId);  // 提供分片键,精确定位
        
        Order order = orderMapper.selectOne(wrapper);
        System.out.println("精确查询 - order_id: " + orderId + 
                          ", user_id: " + userId + 
                          ", 路由到: ds" + (userId % 2) + ".t_order_" + (orderId % 3));
        
        return order;
    }

    /**
     * 4. 根据用户ID查询订单列表
     * SQL: SELECT * FROM t_order WHERE user_id = ? ORDER BY create_time DESC
     */
    public List<Order> getOrdersByUserId(Integer userId) {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId);
        wrapper.orderByDesc("create_time");
        
        List<Order> orders = orderMapper.selectList(wrapper);
        System.out.println("用户查询 - user_id: " + userId + 
                          ", 路由到: ds" + (userId % 2) + " 的所有分表");
        
        return orders;
    }

    /**
     * 5. 范围查询(需要范围分片算法支持)
     * SQL: SELECT * FROM t_order WHERE order_id BETWEEN ? AND ? AND user_id = ?
     */
    public List<Order> getOrdersByRange(Long startOrderId, Long endOrderId, Integer userId) {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.between("order_id", startOrderId, endOrderId);
        wrapper.eq("user_id", userId);  // 提供user_id可以精确定位到数据库
        
        List<Order> orders = orderMapper.selectList(wrapper);
        System.out.println("范围查询 - order_id: [" + startOrderId + ", " + endOrderId + "]" + 
                          ", user_id: " + userId);
        
        return orders;
    }

    /**
     * 6. 复合查询(多条件,无分片键)
     * SQL: SELECT * FROM t_order WHERE order_name LIKE ? AND create_time > ?
     * 注意:无分片键会全路由,性能较差
     */
    public List<Order> searchOrders(String orderName, LocalDateTime startTime) {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.like("order_name", orderName);
        wrapper.gt("create_time", startTime);
        
        List<Order> orders = orderMapper.selectList(wrapper);
        System.out.println("复合查询 - 无分片键,全路由到所有分片");
        
        return orders;
    }

    /**
     * 7. Hint分片示例 - 强制指定分片
     */
    public List<Order> queryWithHint(Integer targetDatabase, Integer targetTable) {
        try (HintManager hintManager = HintManager.getInstance()) {
            // 强制指定数据库分片
            if (targetDatabase != null) {
                hintManager.addDatabaseShardingValue("t_order", targetDatabase);
            }
            // 强制指定表分片
            if (targetTable != null) {
                hintManager.addTableShardingValue("t_order", targetTable);
            }
            
            QueryWrapper<Order> wrapper = new QueryWrapper<>();
            wrapper.eq("order_status", 1);  // 已支付订单
            
            List<Order> orders = orderMapper.selectList(wrapper);
            System.out.println("Hint查询 - 强制路由到: ds" + targetDatabase + 
                              ".t_order_" + targetTable);
            
            return orders;
        }
    }

    /**
     * 8. 强制读主库(保证数据一致性)
     * SQL: SELECT * FROM t_order WHERE order_id = ? FOR UPDATE
     */
    public Order queryForUpdate(Long orderId, Integer userId) {
        try (HintManager hintManager = HintManager.getInstance()) {
            // 强制读主库
            hintManager.setMasterRouteOnly();
            
            QueryWrapper<Order> wrapper = new QueryWrapper<>();
            wrapper.eq("order_id", orderId);
            wrapper.eq("user_id", userId);
            
            return orderMapper.selectOne(wrapper);
        }
    }

    /**
     * 9. 关联查询(绑定表优化)
     * SQL: SELECT o.*, i.product_name FROM t_order o 
     *      JOIN t_order_item i ON o.order_id = i.order_id 
     *      WHERE o.user_id = ?
     */
    public List<Order> getOrdersWithItems(Integer userId) {
        // MyBatis Plus 关联查询示例
        return orderMapper.selectList(new QueryWrapper<Order>()
            .eq("user_id", userId));
        // 实际关联查询需要自定义Mapper XML
    }

    /**
     * 10. 统计查询
     * SQL: SELECT COUNT(*) FROM t_order WHERE user_id = ?
     */
    public long countOrdersByUser(Integer userId) {
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId);
        
        long count = orderMapper.selectCount(wrapper);
        System.out.println("统计查询 - user_id: " + userId + 
                          ", count: " + count);
        
        return count;
    }

    /**
     * 11. 更新订单状态
     * SQL: UPDATE t_order SET order_status = ? WHERE order_id = ? AND user_id = ?
     */
    @Transactional
    public int updateOrderStatus(Long orderId, Integer userId, Integer status) {
        Order order = new Order();
        order.setOrderStatus(status);
        
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("order_id", orderId);
        wrapper.eq("user_id", userId);
        
        int result = orderMapper.update(order, wrapper);
        System.out.println("更新订单 - order_id: " + orderId + 
                          ", user_id: " + userId + 
                          ", 结果: " + result);
        
        return result;
    }

    /**
     * 12. 广播表示例 - 查询字典
     */
    public List<Dict> getDictByCode(String dictCode) {
        // 广播表在所有库都存在,随机选择一个查询
        return dictMapper.selectList(new QueryWrapper<Dict>()
            .eq("dict_code", dictCode));
    }
}

十三、测试类

package com.example;

import com.example.entity.Order;
import com.example.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@SpringBootTest
class ShardingJdbcDemoApplicationTests {

    @Resource
    private OrderService orderService;

    @Test
    void testInsert() {
        // 插入订单 - user_id=1 路由到 ds1
        Order order1 = orderService.createOrder(1, "测试订单1", new BigDecimal("100.00"));
        
        // 插入订单 - user_id=2 路由到 ds0
        Order order2 = orderService.createOrder(2, "测试订单2", new BigDecimal("200.00"));
        
        // 插入订单 - user_id=3 路由到 ds1
        Order order3 = orderService.createOrder(3, "测试订单3", new BigDecimal("300.00"));
    }

    @Test
    void testInsertWithItems() {
        List<String> products = Arrays.asList("商品A", "商品B", "商品C");
        Order order = orderService.createOrderWithItems(
            1, "批量订单", new BigDecimal("300.00"), products);
    }

    @Test
    void testQuery() {
        // 精确查询
        Order order = orderService.getOrderById(1678901234567L, 1);
        System.out.println("查询结果: " + order);
        
        // 用户查询
        List<Order> userOrders = orderService.getOrdersByUserId(1);
        System.out.println("用户订单数: " + userOrders.size());
        
        // 范围查询
        List<Order> rangeOrders = orderService.getOrdersByRange(
            1678900000000L, 1678999999999L, 1);
        
        // 统计查询
        long count = orderService.countOrdersByUser(1);
    }

    @Test
    void testHint() {
        // 强制路由到 ds1.t_order_1
        List<Order> orders = orderService.queryWithHint(1, 1);
    }

    @Test
    void testUpdate() {
        int result = orderService.updateOrderStatus(1678901234567L, 1, 1);
    }
}

十四、分片算法对比总结

算法类型配置方式适用场景优点缺点
INLINE 表达式type: INLINE简单取模分片配置简单,性能高不支持复杂逻辑
标准分片算法type: CLASS_BASED + strategy: standard单分片键,需要范围查询灵活,支持范围查询需要自定义实现
复合分片算法type: CLASS_BASED + strategy: complex多分片键共同决定支持复杂业务规则实现复杂,性能稍低
Hint 分片算法type: CLASS_BASED + strategy: hint分片键不在SQL中动态指定分片代码侵入性强

十五、分片键选择原则

  1. 查询频率高:选择最常用的查询条件字段
  2. 分布均匀:避免数据倾斜
  3. 不会频繁变更:分片键变更需要迁移数据
  4. 具有业务意义:如用户ID、订单ID

十六、性能优化建议

优化项说明
携带分片键查询时尽量携带分片键,避免全路由
配置绑定表关联查询的表配置为绑定表,避免笛卡尔积
范围查询谨慎范围查询可能导致全表扫描,尽量带上分片键
分片数量规划选择2的幂次方便于扩容
监控数据分布定期检查分片数据是否均匀
索引优化在每个真实表上创建相同索引

十七、注意事项

  1. 分布式事务性能:XA事务性能开销大,建议使用最终一致性
  2. SQL支持限制:部分复杂SQL不支持(如存储过程、多表更新)
  3. 主键策略:推荐使用雪花算法,避免分布式ID重复
  4. 扩容考虑:提前规划分片数量,后续扩容成本高

总结

ShardingSphere-JDBC 作为一个轻量级的 Java 框架,为应用提供了强大的分库分表和读写分离能力。通过合理的分片策略配置,可以解决单库单表性能瓶颈,支撑高并发、大数据量的业务场景。

核心价值

  • 性能提升:分散数据库压力,提升查询性能
  • 容量扩展:突破单库容量限制,支持海量数据
  • 高可用:结合读写分离,提高系统可用性
  • 透明化:对应用层透明,简化开发复杂度

适用场景

  • 数据量快速增长,单表超过500万行
  • 并发量高,单库无法支撑
  • 需要读写分离,降低主库压力
  • 业务需要水平扩展,降低运维成本