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

注意 :一定要区分mysqldmysql,以上配置放在[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

待续......

相关推荐
普通网友几秒前
Python FastAPI 异步数据库管理
数据库·fastapi
晓子文集17 分钟前
Tushare接口文档:期货合约信息表(fut_basic)
大数据·数据库·金融·金融数据·量化投资
三言老师1 小时前
CentOS7.9:Redis‑Cluster集群部署结构化实战教程
linux·运维·服务器·数据库
旺仔学长 哈哈1 小时前
Spring Boot 智能停车场管理系统---附源码+数据库文档
数据库·spring boot·后端·智能停车场
ClickHouseDB2 小时前
ClickHouse托管Postgres:OLTP+OLAP,新能力解锁最佳数据平台
java·前端·数据库
数智化管理手记2 小时前
主数据重复、错漏频发?一站式主数据管理平台如何落地?
大数据·运维·数据库·人工智能·数据挖掘
吴声子夜歌2 小时前
MongoDB 4.2——选择片键
数据库·mongodb
天天进步一点点3 小时前
QSqlQuery删除sqlite数据库执行drop数据表报错database table is locked unable to fetch row问题
数据库·oracle·sqlite
音视频工程实战3 小时前
PromptQL 新手入门与实战指南
数据库·sql·算法
清川渡水3 小时前
NL2SQL 的正确打开方式:从「AI 猜 SQL」到「置信度闭环」的工程化落地
数据库·人工智能·sql