Sharding Sphere

ShardingSphere 分库分表

什么是 ShardingSphere

"ShardingSphere 主要由三个核心组件构成:ShardingSphere-JDBC(嵌入式JDBC驱动)、ShardingSphere-JDBC(独立数据库代理)和ShardingSphere-UI(管理界面)。其中JDBC模式适合Java应用,Proxy模式适合多语言应用,UI提供可视化管理,三者共同构成完整的分布式数据库解决方案。"

什么是分库分表

分库分表 是两个方面,即分库分表

  • 分库:将一个数据库分成多个数据库,并部署到不同机器中
  • 分表:将一个数据库表分成多个表

分库分表是为了解决单库单表数据量过大、并发压力高、存储瓶颈等问题,本质是"空间换时间",通过增加存储架构的复杂度,换取性能和扩展性的提升。

什么是垂直拆分

垂直拆分是按""或"业务模块"进行拆分,分为垂直分库垂直分表

1. 垂直分库

  • 定义 :把单一数据库按照业务进行划分,专库专表

  • 应用场景:功能模块分离、性能优化、安全性

  • 举例:电商系统将用户信息存储在user_db,订单信息存储在order_db

    sql 复制代码
    -- 原始综合数据库
    CREATE DATABASE ecommerce;
    
    -- 垂直分库后
    CREATE DATABASE user_db;
    CREATE DATABASE order_db;
    
    -- user_db中
    USE user_db;
    CREATE TABLE users (...);
    
    -- order_db中
    USE order_db;
    CREATE TABLE orders (...);

2. 垂直分表

  • 定义:操作数据库中某张表,把这张表中一部分字段数据存到一张新表里面,再把这张表另一 部分字段数据存到另外一张表里面

  • 应用场景:减少表宽度、提高查询性能、提高索引效率

  • 举例:用户表拆分为常用字段表和不常用字段表

    sql 复制代码
    -- 原始用户表
    CREATE TABLE users (
      user_id INT PRIMARY KEY,
      username VARCHAR(50),
      password VARCHAR(255),
      email VARCHAR(100),
      phone VARCHAR(20),
      address TEXT,          -- 不常用字段
      profile_picture BLOB   -- 大字段
    );
    
    -- 垂直分表后
    -- 热点表(常用字段)
    CREATE TABLE users_core (
      user_id INT PRIMARY KEY,
      username VARCHAR(50),
      password VARCHAR(255),
      email VARCHAR(100),
      phone VARCHAR(20)
    );
    
    -- 冷数据表(不常用字段)
    CREATE TABLE users_profile (
      user_id INT PRIMARY KEY,
      address TEXT,
      profile_picture BLOB
    );

什么是水平拆分

水平拆分是按""进行拆分,分为水平分库水平分表

水平拆分的常见规则

  1. 范围分片:按时间范围(如订单表按"下单时间"拆分)

  2. 哈希分片:对分片字段(如用户ID)进行哈希计算,均匀分布数据

    sql 复制代码
    shard_id = hash(user_id) % shard_count
  3. 地理位置分片:按地理位置(如"华北地区用户"、"华东地区用户")

  4. 业务标识分片:按业务标识(如按"商家ID"拆分订单表)

1. 水平分库

  • 定义:将表的数据行分配到不同的数据库实例中

  • 应用场景:数据量大、高并发

  • 举例:订单表按用户ID哈希分片

    sql 复制代码
    -- 按用户ID哈希分片到不同数据库
    -- order_db_1:存储用户ID哈希值为0-9999的订单
    -- order_db_2:存储用户ID哈希值为10000-19999的订单

2. 水平分表

  • 定义:将表的数据行分成多个表,每个表只包含部分行

  • 应用场景:数据量大、时间序列数据

  • 举例:订单表按时间范围分表

    sql 复制代码
    -- 按年份分表
    CREATE TABLE orders_2023 (...);  -- 2023年订单
    CREATE TABLE orders_2024 (...);  -- 2024年订单

ShardingSphere-JDBC

简介

"ShardingSphere-JDBC 作为嵌入式 JDBC 驱动,主要作用是解决数据库的水平扩展问题:通过数据分片分散单表压力,通过读写分离提升读性能,通过分布式事务保证跨库一致性,同时对应用层完全透明,只需修改数据源配置,无需修改业务代码。"

"ShardingSphere-JDBC 与 ShardingSphere-Proxy 的区别在于:JDBC 是嵌入式驱动,适合 Java 应用;Proxy 是独立代理,适合多语言应用。两者共同构成 ShardingSphere 的完整解决方案,但 JDBC 通常性能更高、延迟更低。"


水平分表

具体 Maven 依赖:

xml 复制代码
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/>
    </parent>
    <groupId>com.lxx</groupId>
    <artifactId>sharding-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sharding-demo</name>
    <description>sharding-demo</description>

    <properties>
        <!-- 明确指定Java 8 -->
        <java.version>8</java.version>
        <!-- 确保Maven编译器使用Java 8 -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <!-- 明确指定Java 8 -->
                    <source>1.8</source>
                    <target>1.8</target>
                    <!-- 确保没有release配置,因为Java 8不支持 -->
                    <!-- <release>8</release> -->
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

需求说明:

  1. 创建数据库 course_db
  2. 创建表 course_1 、 course_2
  3. 约定规则:如果添加的课程 id 为偶数添加到 course_1 中,奇数添加到 course_2 中。
sql 复制代码
create database course_db;

use course_db;

create table course_1 (
    cid bigint(20) primary key ,
    cname varchar(50) not null,
    user_id bigint(20) not null ,
    status varchar(10) not null
) engine = InnoDB;

create table course_2 (
    cid bigint(20) primary key ,
    cname varchar(50) not null,
    user_id bigint(20) not null ,
    status varchar(10) not null
) engine = InnoDB;

配置对应实体类以及 Mapper

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Course {
    private Long cid;

    private String cname;

    private Long userId;

    private String status;
}
java 复制代码
@Repository
public interface CourseMapper extends BaseMapper<Course> {
}

启动类配置

java 复制代码
@MapperScan("com.lxx.shardingdemo.mapper")
@SpringBootApplication
public class ShardingDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShardingDemoApplication.class, args);
    }
}

配置 Sharding-JDBC 分片策略

application.yml内容:

yml 复制代码
################### 水平分表 #########################
spring:
  main:
    # 允许覆盖同名Bean定义(解决ShardingSphere与Druid的dataSource冲突)
    allow-bean-definition-overriding: true

  shardingsphere:
    # 数据源配置(使用Druid连接池)
    datasource:
      names: m1
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/course_db?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

    # 系统属性配置
    props:
      sql:
        # 开启SQL日志输出(用于调试分片策略)
        show: true

    # 分片规则配置
    sharding:
      tables:
        course:
          # 数据源:固定为 m1(只有一个数据源)
          # 表:course_$->{1..2} 表示在 m1 数据源中存在两个表:course_1 和 course_2
          # 实际数据节点:2个: m1.course_1、m1.course_2
          # 含义:只进行表分片(分表),不分库。所有数据都存储在 m1 这一个数据库中,但按规则分布在两个表中。
          actual-data-nodes: m1.course_$->{1..2}
          # 主键生成策略(使用Snowflake算法)
          key-generator:
            column: cid
            type: SNOWFLAKE
          # 分表策略(根据cid值进行分片)
          table-strategy:
            inline:
              sharding-column: cid
              # 分片算法表达式:cid为偶数→course_1,奇数→course_2
              algorithm-expression: course_$->{cid % 2 + 1}

测试

java 复制代码
    @Test
    public void addCourse() {
        for (int i = 0; i < 20; i++) {
            Course course = new Course();
            //cid由我们设置的策略,雪花算法进行生成
            course.setCname("Java");
            course.setUserId(100L);
            course.setStatus("Normal");
            courseMapper.insert(course);
        }
    }

运行结果:

cid:1220064809920233472 为数插入course_1,

cid:1220064809689546753 为数插入course_2

复制代码
Actual SQL: m1 ::: INSERT INTO course_1   (cname, user_id, status, cid) VALUES (?, ?, ?, ?) ::: [Java, 100, Normal, 1220064809920233472]
Actual SQL: m1 ::: INSERT INTO course_2   (cname, user_id, status, cid) VALUES (?, ?, ?, ?) ::: [Java, 100, Normal, 1220064809689546753]

测试

java 复制代码
    @Test
    public void findCourse() {
        QueryWrapper<Course> wrapper = new QueryWrapper<>();
        wrapper.eq("cid", 1220064809689546753L);
        Course course = courseMapper.selectOne(wrapper);
        System.out.println(course);
    }

运行结果:1220064809689546753L 为数查询course_2

复制代码
Actual SQL: m1 ::: SELECT  cid,cname,user_id,status  FROM course_2  

水平分库

需求:

  1. 创建两个数据库,edu_db_1、edu_db_2。
  2. 每个库中包含:course_1、course_2。
  3. 数据库规则:userid 为偶数添加到 edu_db_1 库,奇数添加到 edu_db_2。
  4. 表规则:如果添加的 cid 为偶数添加到 course_1 中,奇数添加到 course_2 中。
sql 复制代码
create database edu_db_1;
create database edu_db_2;

use edu_db_1;

create table course_1 (
   `cid` bigint(20) primary key,
   `cname` varchar(50) not null,
   `user_id` bigint(20) not null,
   `status` varchar(10) not null
);

create table course_2 (
   `cid` bigint(20) primary key,
   `cname` varchar(50) not null,
   `user_id` bigint(20) not null,
   `status` varchar(10) not null
);

use edu_db_2;

create table course_1 (
   `cid` bigint(20) primary key,
   `cname` varchar(50) not null,
   `user_id` bigint(20) not null,
   `status` varchar(10) not null
);

create table course_2 (
   `cid` bigint(20) primary key,
   `cname` varchar(50) not null,
   `user_id` bigint(20) not null,
   `status` varchar(10) not null
);

配置 Sharding-JDBC 分片策略

application.yml内容:

yml 复制代码
################### 水平分库 #########################
spring:
  main:
    # 允许覆盖同名Bean定义(解决ShardingSphere与Druid的dataSource冲突)
    allow-bean-definition-overriding: true

  shardingsphere:
    # 数据源配置(使用Druid连接池)
    datasource:
      # 分布式数据源别名列表
      names: m1,m2
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/edu_db_1?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      m2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/edu_db_2?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

    # 系统属性配置
    props:
      sql:
        # 开启SQL日志输出(用于调试分片策略)
        show: true

    # 分片规则配置
    sharding:
      tables:
        course:
          # 数据源:m$->{1..2} 表示数据源有 m1 和 m2 两个
          # 表:course_$->{1..2} 表示表有 course_1 和 course_2 两个
          # 实际数据节点:4个:m1.course_1、m1.course_2、m2.course_1、m2.course_2
          # 含义:进行双库双表的分片策略,既分库又分表。
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          # 主键生成策略(使用Snowflake算法)
          key-generator:
            column: cid
            type: SNOWFLAKE
          # 分库策略(根据user_id值进行分片)
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: m$->{user_id % 2 + 1}
          # 分表策略(根据cid值进行分片)
          table-strategy:
            inline:
              sharding-column: cid
              algorithm-expression: course_$->{cid % 2 + 1}

测试

java 复制代码
    @Test
    public void addCourse() {
        Course course1 = new Course();
        course1.setCname("python");
        course1.setUserId(100L);
        course1.setStatus("Normal");
        courseMapper.insert(course1);

        Course course2 = new Course();
        course2.setCname("python");
        course2.setUserId(111L);
        course2.setStatus("Normal");
        courseMapper.insert(course2);
    }

运行结果:

user_id:100 为 数插入m1数据库 , cid:1220068117682585601 为 数插入course_2

user_id:111 为 数插入m2数据库 , cid:1220068118945071104 为 数插入course_1

复制代码
Actual SQL: m1 ::: INSERT INTO course_2   (cname, user_id, status, cid) VALUES (?, ?, ?, ?) ::: [python, 100, Normal, 1220068117682585601]
Actual SQL: m2 ::: INSERT INTO course_1   (cname, user_id, status, cid) VALUES (?, ?, ?, ?) ::: [python, 111, Normal, 1220068118945071104]

测试

java 复制代码
    @Test
    public void findCourse() {
        QueryWrapper<Course> wrapper = new QueryWrapper<>();
        wrapper.eq("cid", 1220068118945071104L);
        wrapper.eq("user_id",111);
        Course course = courseMapper.selectOne(wrapper);
        System.out.println(course);
    }

运行结果:user_id:111 为 数查询m2数据库 , cid:1220068118945071104 为 数查询course_1

复制代码
Actual SQL: m2 ::: SELECT  cid,cname,user_id,status  FROM course_1  

垂直分库

需求:

我们再额外创建一个 user_db 数据库。当我们查询用户信息就去 user_db,课程信息就去 edu_db_1、edu_db_2。

sql 复制代码
create database user_db;

use user_db;

create table t_user(
   `user_id` bigint(20) primary key,
   `username` varchar(100) not null,
   `status` varchar(50) not null
);

配置对应实体类以及 Mapper

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {
    private Long userId;
    
    private String username;
    
    private String status;
}
java 复制代码
@Repository
public interface UserMapper extends BaseMapper<User> {
}

配置 Sharding-JDBC 分片策略

application.yml内容:

yml 复制代码
##################### 垂直分库 #########################
spring:
  main:
    # 允许覆盖同名Bean定义(解决ShardingSphere与Druid的dataSource冲突)
    allow-bean-definition-overriding: true

  shardingsphere:
    # 数据源配置(m0/m1/m2为分库别名,m0专用于用户表,m1/m2用于课程表)
    datasource:
      names: m0,m1,m2  # 分布式数据源别名列表

      # 专库专表:用户表t_user独立使用m0数据源
      m0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/user_db?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

      # 课程表分库1:edu_db_1
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/edu_db_1?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

      # 课程表分库2:edu_db_2
      m2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/edu_db_2?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

    # 系统属性配置(调试关键参数)
    props:
      sql:
        # 开启SQL日志输出(仅用于开发环境调试分片策略)
        show: true

    # 分片规则配置(双库双表分片策略)
    sharding:
      # 专库专表配置:t_user表独立使用m0数据源
      tables:
        t_user: # 用户表(专库专表,不参与分库分表)
          # 实际数据节点:m0数据库中的t_user表
          actual-data-nodes: m0.t_user
          # 主键生成策略
          key-generator:
            column: user_id
            type: SNOWFLAKE

        course: # 课程表(双库双表分片)
          # 实际数据节点:m1/m2分库 + course_1/course_2分表
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          # 主键生成策略
          key-generator:
            column: cid
            type: SNOWFLAKE

          # 分库策略(根据user_id分片,确保相同user_id在同库)
          database-strategy:
            inline:
              sharding-column: user_id  # 分片键(决定路由到哪个分库)
              algorithm-expression: m$->{user_id % 2 + 1}  # 分库算法:user_id % 2 → m1/m2
              # 说明:user_id为偶数→m1,奇数→m2(如0→m1, 1→m2, 2→m1)

          # 分表策略(根据cid分片,确保相同cid在同表)
          table-strategy:
            inline:
              sharding-column: cid  # 分片键(决定路由到哪个分表)
              algorithm-expression: course_$->{cid % 2 + 1}  # 分表算法:cid % 2 → course_1/course_2
              # 说明:cid为偶数→course_1,奇数→course_2(如0→course_1, 1→course_2)

测试

java 复制代码
    @Test
    public void addUserAndCourse() {
            User user1 = new User();
            user1.setUsername("Jack");
            user1.setStatus("Normal");
            userMapper.insert(user1);
         	 
        	User use2r = new User();
            user2.setUsername("Max");
            user2.setStatus("Normal");
            userMapper.insert(user2);

            Course course1 = new Course();
            course1.setCname("python");
            course1.setUserId(100L);
            course1.setStatus("Normal");
            courseMapper.insert(course1);

            Course course2 = new Course();
            course2.setCname("python");
            course2.setUserId(111L);
            course2.setStatus("Normal");
            courseMapper.insert(course2);
    }

运行结果:

插入t_user表都使用数据源m0

user_id:100 为 数插入m1数据库 , cid:1220075920027549697 为 数插入course_2

user_id:111 为 数插入m2数据库 , cid:1220075920530866176 为 数插入course_1

复制代码
Actual SQL: m0 ::: INSERT INTO t_user   (username, status, user_id) VALUES (?, ?, ?) ::: [Jack, Normal, 1220075919247409153]
Actual SQL: m0 ::: INSERT INTO t_user   (username, status, user_id) VALUES (?, ?, ?) ::: [Max, Normal, 1220075919805251584]
Actual SQL: m1 ::: INSERT INTO course_2   (cname, user_id, status, cid) VALUES (?, ?, ?, ?) ::: [python, 100, Normal, 1220075920027549697]
Actual SQL: m2 ::: INSERT INTO course_1   (cname, user_id, status, cid) VALUES (?, ?, ?, ?) ::: [python, 111, Normal, 1220075920530866176]

公共表

概念

  1. 存储固定数据的表,表数据很少发生变化,查询时经常要进行关联。
  2. 在每个数据库中都创建出相同结构公共表。
  3. 操作公共表时,同时操作添加了公共表的数据库中的公共表,添加记录时,同时添加,删除时,同时删除。

在多个数据库中创建公共表

sql 复制代码
# use user_db;
# use edu_db_1;
use edu_db_2;

create table t_dict(
   `dict_id` bigint(20) primary key,
   `status` varchar(100) not null,
   `value` varchar(100) not null
);

配置公共表的实体类和 mapper

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_dict")
public class Dict {
    private Long dictId;
    private String status;
    private String value;
}
java 复制代码
@Repository
public interface DictMapper extends BaseMapper<Dict> {
}

配置 Sharding-JDBC 分片策略

application.yml内容:

yml 复制代码
##################### 垂直分库 #########################
spring:
  main:
    # 允许覆盖同名Bean定义(解决ShardingSphere与Druid的dataSource冲突)
    allow-bean-definition-overriding: true

  shardingsphere:
    # 数据源配置(m0/m1/m2为分库别名,m0专用于用户表,m1/m2用于课程表)
    datasource:
      names: m0,m1,m2  # 分布式数据源别名列表

      # 专库专表:用户表t_user独立使用m0数据源
      m0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/user_db?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

      # 课程表分库1:edu_db_1
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/edu_db_1?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

      # 课程表分库2:edu_db_2
      m2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/edu_db_2?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

    # 系统属性配置(调试关键参数)
    props:
      sql:
        # 开启SQL日志输出(仅用于开发环境调试分片策略)
        show: true

    # 分片规则配置(双库双表分片策略)
    sharding:
      # 专库专表配置:t_user表独立使用m0数据源
      tables:
        t_user: # 用户表(专库专表,不参与分库分表)
          # 实际数据节点:m0数据库中的t_user表
          actual-data-nodes: m0.t_user
          # 主键生成策略
          key-generator:
            column: user_id
            type: SNOWFLAKE

        course: # 课程表(双库双表分片)
          # 实际数据节点:m1/m2分库 + course_1/course_2分表
          actual-data-nodes: m$->{1..2}.course_$->{1..2}
          # 主键生成策略
          key-generator:
            column: cid
            type: SNOWFLAKE

          # 分库策略(根据user_id分片,确保相同user_id在同库)
          database-strategy:
            inline:
              sharding-column: user_id  # 分片键(决定路由到哪个分库)
              algorithm-expression: m$->{user_id % 2 + 1}  # 分库算法:user_id % 2 → m1/m2
              # 说明:user_id为偶数→m1,奇数→m2(如0→m1, 1→m2, 2→m1)

          # 分表策略(根据cid分片,确保相同cid在同表)
          table-strategy:
            inline:
              sharding-column: cid  # 分片键(决定路由到哪个分表)
              algorithm-expression: course_$->{cid % 2 + 1}  # 分表算法:cid % 2 → course_1/course_2
              # 说明:cid为偶数→course_1,奇数→course_2(如0→course_1, 1→course_2)

        t_dict:
          key-generator:
            column: dict_id  # 主键列名
            type: SNOWFLAKE

      # 公共表配置
      broadcast-tables: t_dict

测试

java 复制代码
    @Test
    public void addDict() {
        Dict dict = new Dict();
        dict.setStatus("Normal");
        dict.setValue("启用");
        dictMapper.insert(dict);
    }

运行结果:m0、m1、m2 同时插入

复制代码
Actual SQL: m0 ::: INSERT INTO t_dict   (status, value, dict_id) VALUES (?, ?, ?) ::: [Normal, 启用, 1220090484970487809]
Actual SQL: m1 ::: INSERT INTO t_dict   (status, value, dict_id) VALUES (?, ?, ?) ::: [Normal, 启用, 1220090484970487809]
Actual SQL: m2 ::: INSERT INTO t_dict   (status, value, dict_id) VALUES (?, ?, ?) ::: [Normal, 启用, 1220090484970487809]

测试

java 复制代码
   @Test
    public void deleteDict() {
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("dict_id", 1220090484970487809L);
        dictMapper.delete(wrapper);
    }

运行结果:m0、m1、m2 同时删除

复制代码
Actual SQL: m0 ::: DELETE FROM t_dict  WHERE  dict_id = ? ::: [1220090484970487809]
Actual SQL: m1 ::: DELETE FROM t_dict  WHERE  dict_id = ? ::: [1220090484970487809]
Actual SQL: m2 ::: DELETE FROM t_dict  WHERE  dict_id = ? ::: [1220090484970487809]

读写分离

什么是读写分离

读写分离 是一种数据库架构设计模式,将数据库的 写操作(INSERT/UPDATE/DELETE)和读操作(SELECT) 分别路由到不同的数据库实例上:

  • 写操作 :发送到主库(Master)
  • 读操作 :发送到从库(Slave)

与主从复制的关系

  • 主从复制是技术基础:主库将变更同步到从库
  • 读写分离是应用层逻辑:决定读操作应路由到哪个库

主从复制原理

基于二进制日志(binlog)的异步复制机制 ,通过I/O线程SQL线程协作,将主库(Master)的数据变更同步到从库(Slave)。

工作流程(4步)

  1. 主库记录变更:执行写操作 → 记录到binlog
  2. 主库发送binlog:binlog dump线程发送binlog给从库I/O线程
  3. 从库存储binlog:I/O线程将binlog写入relay log
  4. 从库执行变更:SQL线程解析relay log,执行SQL更新数据

搭建MySQL主从复制

开启bin-log日志

修改主服务器Master

sh 复制代码
修改配置文件:vim /etc/my.cnf
#主服务器唯一ID
server-id=1
#启用二进制日志
log-bin=mysql-bin

注意 :一定要区分[mysqld]和[mysql],以上配置放在[mysqld]

修改从服务器slave

sh 复制代码
修改配置文件:vim /etc/my.cnf
#从服务器唯一ID
server-id=2
#启用中继日志,从主服务器上同步日志文件记录到本地
relay-log=mysql-relay

重启两台服务器的mysql

sh 复制代码
service mysql restart

以上完毕之后我们登录主服务器的 MySQL

sh 复制代码
mysql -u root -p

在主服务器上建立帐户并授权slave

sql 复制代码
GRANT REPLICATION SLAVE ON *.* to 'slave'@'%' identified by '123456';

GRANT REPLICATION SLAVE:授予 复制从库权限(REPLICATION SLAVE)

ON *.*:第一个 *所有数据库 ,第二个 *所有表 。该用户可以对主库上所有数据库和表进行复制操作

TO 'slave'@'%''slave'用户名 (创建的数据库用户);'%'主机名通配符 (表示允许从任何IP地址连接)

IDENTIFIED BY '123456':设置用户密码

查询Master的状态

sql 复制代码
show master status;

+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      430 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

注意:

执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化。

配置从服务器Slave

sql 复制代码
change master to master_host='192.168.1.101',master_user='slave',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=430;

注意: 注意不要断开,430数字前后无单引号。

启动从服务器复制功能

sql 复制代码
start slave;

检查从服务器复制功能状态

sql 复制代码
show slave status \G;

注意:

Slave_IO及Slave_SQL进程必须正常运行,即YES状态,否则都是错误的状态(如:其中一个NO均属错误)。

主从服务器测试

主服务器Mysql,建立数据库,并在这个库中建表插入一条数据

sql 复制代码
mysql> create database test;
Query OK, 1 row affected (0.00 sec)

mysql> use test;
Database changed

mysql> create table dog(id int(3),name varchar(255));
Query OK, 0 rows affected (0.00 sec)

mysql> insert into dog values(001,'zhuti');
Query OK, 1 row affected (0.00 sec)

从服务器Mysql查询

sql 复制代码
mysql>show databases;
mysql>use test;
mysql>select * from dog; 

Sharding-JDBC 实现读写分离

Sharding-JDBC 实现读写分离则是根据sql 语句语义分析当 sql 语句有 insert、update、delete 时,Sharding-JDBC 就把这次操作在主数据库上执行;当 sql 语句有 select 时,就会把这次操作在从数据库上执行,从而实现读写分离过程。

但 Sharding-JDBC 并不会做数据同步,数据同步是配置 MySQL 后由 MySQL 自己完成的。

搭建环境成功后我们在主库和从库上都建库建表

sql 复制代码
create database user_db;

use user_db;

create table t_user(
   `user_id` bigint(20) primary key,
   `username` varchar(100) not null,
   `status` varchar(50) not null
);

配置 Sharding-JDBC 分片策略

application.yml内容:

yml 复制代码
################### 读写分离 #########################
spring:
  main:
    # 允许覆盖同名Bean定义(解决ShardingSphere与Druid的dataSource冲突)
    allow-bean-definition-overriding: true

  shardingsphere:
    # 数据源配置(m0为主库,s0为从库,用于用户表读写分离)
    datasource:
      names: m0,s0  # 分布式数据源别名列表(m0=主库, s0=从库)

      # 主库配置(user_db数据库,用于写操作)
      m0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.101:3306/user_db?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

      # 从库配置(user_db数据库,用于读操作)
      s0:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.102:3306/user_db?characterEncoding=UTF8&autoReconnect=true&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

    # 系统属性配置(调试关键参数)
    props:
      sql:
        # 开启SQL日志输出(仅用于开发环境调试,生产环境建议关闭)
        show: true

    # 读写分离规则配置(主从复制)
    sharding:
      master-slave-rules:
        ds0: # 读写分离数据源别名(逻辑数据源名)
          master-data-source-name: m0  # 主库数据源别名
          slave-data-source-names: s0  # 从库数据源别名(可配置多个,用逗号分隔)
          # 说明:写操作路由到m0,读操作路由到s0(默认读从库)

      tables:
        t_user:
          # 实际数据节点:ds0数据库中的t_user
          actual-data-nodes: ds0.t_user

          # 主键生成策略(使用雪花算法生成分布式ID)
          key-generator:
            column: user_id
            type: SNOWFLAKE

测试

java 复制代码
    @Test
    public void addUser() {
        User user = new User();
        user.setUsername("Jack");
        user.setStatus("Normal");
        userMapper.insert(user);
    }

运行结果:在主库m0插入

复制代码
Actual SQL: m0 ::: INSERT INTO t_user   (username, status, user_id) VALUES (?, ?, ?) ::: [Jack, Normal, 1220097776008822785]

测试

java 复制代码
    @Test
    public void findUser() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", 1220097776008822785L);
        userMapper.selectOne(wrapper);
    }

运行结果:在从库s0查询

复制代码
Actual SQL: s0 ::: SELECT  user_id,username,status  FROM t_user  WHERE  user_id = ? ::: [1220097776008822785]

ShardingSphere-JDBC

待续......

相关推荐
超级数据查看器2 小时前
超级数据查看器 更新日志(包含的功能)
android·java·数据库·sqlite·安卓
哲霖软件2 小时前
机械设备ERP选型指南:5款产品技术特性与落地要点
运维·数据库
茁壮成长的露露2 小时前
MongoDB分片集群部署
数据库·mongodb
indexsunny2 小时前
互联网大厂Java面试实战:Spring Boot与微服务在电商场景中的应用解析
java·数据库·spring boot·微服务·maven·flyway·电商
zhujian826372 小时前
二十七、【鸿蒙 NEXT】如何实时查看数据库
数据库·华为·harmonyos·查看数据库
数据知道2 小时前
PostgreSQL实战:窗口函数详解
数据库·postgresql
狂龙骄子2 小时前
MySQL表字段批量修改SQL实战技巧
数据库·sql·mysql·alter table·批量修改·sql实战技巧
catchadmin2 小时前
2026 年 PHP 函数式编程 优势与实际应用
数据库·php
roman_日积跬步-终至千里2 小时前
【SQL】SQL 语句的解析顺序:理解查询执行的逻辑
java·数据库·sql