SpringBoot框架简单整合ShardingSphere-JDBC实现MySQL分库分表和读写分离及加密混合

news2025/2/28 13:17:32

1. 主从配置

1.1 主机1(IP:192.168.186.77)

1.1.1 docker-compose.yml

version: '3.8'

services:
  mysql-master:
    image: mysql:latest
    container_name: mysql-master
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_USER: master
      MYSQL_PASSWORD: 123456
      MYSQL_DATABASE: db1  
    ports:
      - "3306:3306"
    volumes:
      - mysql-master-data:/var/lib/mysql
    command: --server-id=1 --log-bin=mysql-bin --binlog-format=ROW
    cap_add:
      - SYS_NICE
    security_opt:
      - seccomp:unconfined

  mysql-slave:
    image: mysql:latest
    container_name: mysql-slave
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_USER: slave
      MYSQL_PASSWORD: 123456
      MYSQL_DATABASE: db1
    ports:
      - "3307:3306"
    volumes:
      - mysql-slave-data:/var/lib/mysql
    command: --server-id=2 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.index
    depends_on:
      - mysql-master
    cap_add:
      - SYS_NICE
    security_opt:
      - seccomp:unconfined

volumes:
  mysql-master-data:
  mysql-slave-data:

说明:端口3306充当主库,端口3307充当从库,创建的时候同时创建新用户和密码,如用户master,同时创建的时候也创建数据库db1。在MySQL 的主从复制架构中,每个实例都必须有一个唯一的 server-id。即使实例位于不同的 IP 地址上,每个实例的 server-id 也必须是唯一的。

1.1.2 启动主从数据库

docker-compose up -d

1.1.3 主数据库用户授权

# 给mater用户授全部权限
GRANT ALL PRIVILEGES ON *.* TO 'master'@'%';
FLUSH PRIVILEGES;

1.1.4 配置主从数据库

1.1.4.1 配置主数据库
# 创建主从配置之间的用户
CREATE USER 'master_slave'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'master_slave'@'%';
FLUSH PRIVILEGES;
# 查看FILE 和 Position 文件
SHOW MASTER STATUS;

说明:FILE 和 Position 是配置从数据库的关键,需要自行查看并修改对应坐标。 

1.1.4.2 配置从数据库 
CHANGE MASTER TO
    MASTER_HOST ='192.168.186.77', # 主服务器的 IP 地址
    MASTER_USER ='master_slave', # 主服务器上配置的复制用户
    MASTER_PASSWORD ='123456', # 复制用户的密码
    MASTER_LOG_FILE ='mysql-bin.000003', # 主服务器的日志文件名
    MASTER_LOG_POS =1358, # 日志文件的位置
    MASTER_SSL=1;
START SLAVE;
SHOW SLAVE STATUS;

 说明:出现 Slave_IO_Running 和 Slave_SQL_Running 都是YES说明主从配置成功。

 1.1.4.3 创建数据库表
use db1;
CREATE TABLE t_order_0
(
    order_id   INT PRIMARY KEY,
    user_id    INT,
    order_date DATE,
    status     VARCHAR(255)
);

CREATE TABLE t_order_1
(
    order_id   INT PRIMARY KEY,
    user_id    INT,
    order_date DATE,
    status     VARCHAR(255)
);

 说明 :需要在不同IP的主数据库上执行该数据库语句,创建真实物理表,从数据库会自动同步复制数据库的结构。ShardingSphere会创建逻辑数据库数据表根据分库规则实现分库分表。

注意:主机2和主机3的配置流程几乎相同,如果个人能力允许可以跳过该部分。

1.2 主机2(IP:192.168.186.216)

1.2.1 docker-compose.yml

version: '3.8'

services:
  mysql-master:
    image: mysql:latest
    container_name: mysql-master
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_USER: master
      MYSQL_PASSWORD: 123456
      MYSQL_DATABASE: db2  
    ports:
      - "3306:3306"
    volumes:
      - mysql-master-data:/var/lib/mysql
    command: --server-id=3 --log-bin=mysql-bin --binlog-format=ROW
    cap_add:
      - SYS_NICE
    security_opt:
      - seccomp:unconfined

  mysql-slave:
    image: mysql:latest
    container_name: mysql-slave
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_USER: slave
      MYSQL_PASSWORD: 123456
      MYSQL_DATABASE: db2  
    ports:
      - "3307:3306"
    volumes:
      - mysql-slave-data:/var/lib/mysql
    command: --server-id=4 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.index
    depends_on:
      - mysql-master
    cap_add:
      - SYS_NICE
    security_opt:
      - seccomp:unconfined

volumes:
  mysql-master-data:
  mysql-slave-data:

说明:通过server-id 区分不同的主从数据库。

1.2.2 启动主从数据库

docker-compose up -d

1.2.3 主数据库用户授权

# 给mater用户授全部权限
GRANT ALL PRIVILEGES ON *.* TO 'master'@'%';
FLUSH PRIVILEGES;

1.2.4 配置主从数据库

1.2.4.1 配置主数据库
# 创建主从配置之间的用户
CREATE USER 'master_slave'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'master_slave'@'%';
FLUSH PRIVILEGES;
# 查看FILE 和 Position 文件
SHOW MASTER STATUS;

说明:FILE 和 Position 是配置从数据库的关键,需要自行查看并修改对应坐标。

 1.2.4.2 配置从数据库 
CHANGE MASTER TO
    MASTER_HOST ='192.168.186.216', # 主服务器的 IP 地址
    MASTER_USER ='master_slave', # 主服务器上配置的复制用户
    MASTER_PASSWORD ='123456', # 复制用户的密码
    MASTER_LOG_FILE ='mysql-bin.000003', # 主服务器的日志文件名
    MASTER_LOG_POS =1781, # 日志文件的位置
    MASTER_SSL=1;
START SLAVE;
SHOW SLAVE STATUS;

说明:出现 Slave_IO_Running 和 Slave_SQL_Running 都是YES说明主从配置成功。

1.2.4.3 创建数据库表
use db2;
CREATE TABLE t_order_0
(
    order_id   INT PRIMARY KEY,
    user_id    INT,
    order_date DATE,
    status     VARCHAR(255)
);

CREATE TABLE t_order_1
(
    order_id   INT PRIMARY KEY,
    user_id    INT,
    order_date DATE,
    status     VARCHAR(255)
);

说明 :需要在不同IP的主数据库上执行该数据库语句,创建真实物理表,从数据库会自动同步复制数据库的结构。ShardingSphere会创建逻辑数据库数据表根据分库规则实现分库分表。

1.3 主机3(IP:192.168.186.18) 

1.3.1 docker-compose.yml

version: '3.8'

services:
  mysql-master:
    image: mysql:latest
    container_name: mysql-master
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_USER: master
      MYSQL_PASSWORD: 123456
      MYSQL_DATABASE: db3  
    ports:
      - "3306:3306"
    volumes:
      - mysql-master-data:/var/lib/mysql
    command: --server-id=5 --log-bin=mysql-bin --binlog-format=ROW
    cap_add:
      - SYS_NICE
    security_opt:
      - seccomp:unconfined

  mysql-slave:
    image: mysql:latest
    container_name: mysql-slave
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_USER: slave
      MYSQL_PASSWORD: 123456
      MYSQL_DATABASE: db3 
    ports:
      - "3307:3306"
    volumes:
      - mysql-slave-data:/var/lib/mysql
    command: --server-id=6 --log-bin=mysql-bin --binlog-format=ROW --relay-log=relay-bin --relay-log-index=relay-bin.index
    depends_on:
      - mysql-master
    cap_add:
      - SYS_NICE
    security_opt:
      - seccomp:unconfined

volumes:
  mysql-master-data:
  mysql-slave-data:

说明:通过server-id不同的主从数据库。

1.3.2 启动主从数据库

docker-compose up -d

1.3.3 主数据库用户授权

# 给mater用户授全部权限
GRANT ALL PRIVILEGES ON *.* TO 'master'@'%';
FLUSH PRIVILEGES;

1.3.4 配置主从数据库

1.3.4.1 配置主数据库
# 创建主从配置之间的用户
CREATE USER 'master_slave'@'%' IDENTIFIED BY '123456' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'master_slave'@'%';
FLUSH PRIVILEGES;
# 查看FILE 和 Position 文件
SHOW MASTER STATUS;

说明:FILE 和 Position 是配置从数据库的关键,需要自行查看并修改对应坐标。  

1.3.4.2 配置从数据库  

说明:出现 Slave_IO_Running 和 Slave_SQL_Running 都是YES说明主从配置成功。

1.3.4.3 创建数据库表
use db3;
CREATE TABLE t_order_0
(
    order_id   INT PRIMARY KEY,
    user_id    INT,
    order_date DATE,
    status     VARCHAR(255)
);

CREATE TABLE t_order_1
(
    order_id   INT PRIMARY KEY,
    user_id    INT,
    order_date DATE,
    status     VARCHAR(255)
);

1.4 其他

ERROR:

Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction 'ANONYMOUS' at master log mysql-bin.000003, end_log_pos 3890. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.

        如果遇到主从复制错误(我遇到是给用户重新授权的时候出现的),可以尝试以下解决方法: 

-- 停止从库复制
STOP SLAVE;

-- 跳过一个错误的事务
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;

-- 重新启动从库复制
START SLAVE;

2. ShardingSphere-JDBC

2.1 项目结构

2.2 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 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>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.example</groupId>
    <artifactId>ShardingSphere-JDBC</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <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>

        <!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc</artifactId>
            <version>5.5.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.shardingsphere</groupId>
                    <artifactId>shardingsphere-test-util</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.3 Order.java

package org.example.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import java.util.Date;

@Entity
@Table(name = "t_order")
@Data
public class Order {
    @Id
    @Column(name = "order_id")
    private int orderId;

    @Column(name = "user_id")
    private int userId;

    @Column(name = "order_date")
    private Date orderDate;

    @Column(name = "status")
    private String status;
}

 2.4 orderRepository.java

package org.example.repository;

import org.example.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface orderRepository extends JpaRepository<Order, Long> {
}

2.5 ShardingSphereConfig.java

package org.example.config;

import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import javax.sql.DataSource;


import java.io.IOException;
import java.sql.SQLException;


@Configuration
public class ShardingSphereConfig{
    @Value("shardingsphere.yaml")
    private ClassPathResource config;
    @Bean
    public DataSource dataSource() throws SQLException, IOException {
        return YamlShardingSphereDataSourceFactory.createDataSource(config.getContentAsByteArray());
    }
}

说明:通过@Value注入shardingsphere.yaml并创建dataSource的Bean。

数据分片 :: ShardingSphere

2.6 application.yaml 

spring:
  application:
      name: ShardingSphere-JDBC

2.7 logback.xml 

<configuration>
    <!-- 定义过滤器,用于过滤出包含 "Logic SQL:" 或 "Actual SQL:" 的日志消息 -->
    <appender name="SQL_FILTER" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 只打印消息部分 -->
            <pattern>%msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
                <expression>
                    return message.contains("Logic SQL:") || message.contains("Actual SQL:");
                </expression>
            </evaluator>
            <OnMismatch>DENY</OnMismatch>
            <OnMatch>ACCEPT</OnMatch>
        </filter>
    </appender>

    <!-- 配置针对 ShardingSphere-SQL 的日志输出 -->
    <logger name="ShardingSphere-SQL" level="INFO" additivity="false">
        <appender-ref ref="SQL_FILTER" />
    </logger>
</configuration>

2.8 shardingsphere.yaml

2.8.1 逻辑数据库名称

databaseName: my_database # 数据库配置名称。

2.8.2 数据源配置

dataSources:
  ds_77_master:
    # 主数据库服务器的配置,IP 为 192.168.186.77,端口为 3306。
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    # HikariCP 数据源的完整类名。

    url: jdbc:mysql://192.168.186.77:3306/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    # 用于连接 MySQL 数据库的 JDBC URL。
    # serverTimezone=UTC 确保服务器时区设置为 UTC。
    # useSSL=false 禁用 SSL。
    # allowPublicKeyRetrieval=true 允许客户端从服务器检索公钥。

    username: master
    # 用于连接数据库的用户名。

    password: 123456
    # 数据库用户的密码。

    connectionTimeoutMilliseconds: 30000
    # 客户端等待从连接池获取连接的最大时间(以毫秒为单位)。
    # 默认值为 30000 毫秒(30 秒)。

    idleTimeoutMilliseconds: 60000
    # 连接在连接池中闲置的最长时间(以毫秒为单位)。
    # 默认值为 600000 毫秒(10 分钟)。

    maxLifetimeMilliseconds: 1800000
    # 连接在连接池中的最长存活时间(以毫秒为单位)。
    # 默认值为 1800000 毫秒(30 分钟)。

    maxPoolSize: 50
    # 连接池将维持的最大连接数。
    # 默认值为 10。

    minPoolSize: 1
    # 连接池将维持的最小闲置连接数。
    # 默认值为 1。

  ds_77_slave:
    # 从数据库服务器的配置,IP 为 192.168.186.77,端口为 3307。
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.77:3307/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: slave
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

  ds_216_master:
    # 主数据库服务器的配置,IP 为 192.168.186.216,端口为 3306。
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.216:3306/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: master
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

  ds_216_slave:
    # 从数据库服务器的配置,IP 为 192.168.186.216,端口为 3307。
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.216:3307/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: slave
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

  ds_18_master:
    # 主数据库服务器的配置,IP 为 192.168.186.18,端口为 3306。
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.18:3306/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: master
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

  ds_18_slave:
    # 从数据库服务器的配置,IP 为 192.168.186.18,端口为 3307。
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.18:3307/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: slave
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1

 2.8.3 读写分离规则

rules:
  - !READWRITE_SPLITTING
    # 配置读写分离规则。
    dataSources:
      # 定义数据源组。
      readwrite_77:
        # 名为 readwrite_77 的读写分离数据源组配置。
        writeDataSourceName: ds_77_master  # 写操作的数据源名称,指向 ds_77_master 数据源。
        readDataSourceNames:               # 读操作的数据源名称列表,这里指定为 ds_77_slave 数据源。
          - ds_77_slave
        transactionalReadQueryStrategy: DYNAMIC  # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。
        loadBalancerName: random                 # 负载均衡器名称,指向名为 random 的负载均衡策略。

      readwrite_216:
        # 名为 readwrite_216 的读写分离数据源组配置。
        writeDataSourceName: ds_216_master  # 写操作的数据源名称,指向 ds_216_master 数据源。
        readDataSourceNames:                # 读操作的数据源名称列表,这里指定为 ds_216_slave 数据源。
          - ds_216_slave
        transactionalReadQueryStrategy: DYNAMIC  # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。
        loadBalancerName: random                 # 负载均衡器名称,指向名为 random 的负载均衡策略。

      readwrite_18:
        # 名为 readwrite_18 的读写分离数据源组配置。
        writeDataSourceName: ds_18_master  # 写操作的数据源名称,指向 ds_18_master 数据源。
        readDataSourceNames:               # 读操作的数据源名称列表,这里指定为 ds_18_slave 数据源。
          - ds_18_slave
        transactionalReadQueryStrategy: DYNAMIC  # 事务性读查询策略,DYNAMIC 表示根据实际情况动态选择数据源。
        loadBalancerName: random                 # 负载均衡器名称,指向名为 random 的负载均衡策略。

    loadBalancers:
      # 定义负载均衡策略。
      random:
        # 名为 random 的负载均衡策略配置。
        type: RANDOM  # 负载均衡策略类型,这里设置为随机(RANDOM)。

说明:transactionalReadQueryStrategy对应参数 PRIMARYFIXEDDYNAMIC 对应不同的读写分离策略:

  1. PRIMARY: 只从主库读取。这通常用于需要确保读取到最新数据的情况。

  2. FIXED: 固定从库读取。这种情况下会根据配置的从库列表选择特定的从库读取数据。

  3. DYNAMIC: 动态从库读取。根据负载均衡策略动态选择从库读取数据。

     如果想让读操作从从库读取,可以配置 FIXEDDYNAMIC 策略。

2.8.4 分片规则

- !SHARDING
  # 配置分片规则。
  tables:
    t_order:
      # 针对表 t_order 的分片配置。
      actualDataNodes: readwrite_77.t_order_${0..1}, readwrite_216.t_order_${0..1}, readwrite_18.t_order_${0..1}
      # 实际数据节点,表示 t_order 表在三个数据源组(readwrite_77, readwrite_216, readwrite_18)的分片情况。
      tableStrategy:
        standard:
          shardingColumn: order_id  # 分片键为 order_id。
          shardingAlgorithmName: t_order_inline  # 分片算法名称为 t_order_inline。
      keyGenerateStrategy:
        column: order_id  # 主键生成策略,针对 order_id 列。
        keyGeneratorName: snowflake  # 主键生成器名称为 snowflake。

  defaultDatabaseStrategy:
    standard:
      # 默认数据库策略。
      shardingColumn: user_id  # 分片键为 user_id。
      shardingAlgorithmName: database_inline  # 分片算法名称为 database_inline。

  defaultTableStrategy:
    none:  # 默认表策略为 none,表示不进行默认表分片。

  bindingTables:
    - t_order  # 绑定表配置,表示 t_order 是一个绑定表。

  shardingAlgorithms:
    database_inline:
      # 分片算法名称为 database_inline。
      type: INLINE  # 算法类型为 INLINE。
      props:
        algorithm-expression: "readwrite_${(user_id % 3 == 0) ? '77' : ((user_id % 3 == 1) ? '216' : '18')}"
        # 算法表达式,根据 user_id 的值将数据分片到不同的数据源组:
        # 如果 user_id % 3 == 0,则选择 readwrite_77;
        # 如果 user_id % 3 == 1,则选择 readwrite_216;
        # 否则选择 readwrite_18。

    t_order_inline:
      # 分片算法名称为 t_order_inline。
      type: INLINE  # 算法类型为 INLINE。
      props:
        algorithm-expression: "t_order_${order_id % 2}"
        # 算法表达式,根据 order_id 的值将数据分片到 t_order_0 或 t_order_1:
        # 如果 order_id % 2 == 0,则选择 t_order_0;
        # 否则选择 t_order_1。

  keyGenerators:
    snowflake:
      # 主键生成器名称为 snowflake。
      type: SNOWFLAKE  # 主键生成器类型为 SNOWFLAKE。
      props:
        worker-id: 123  # SNOWFLAKE 算法的 worker-id 配置,值为 123。

- !ENCRYPT
  # 配置加密规则。
  encryptors:
    aes_encryptor:
      # 加密器名称为 aes_encryptor。
      type: AES  # 加密器类型为 AES(对称加密算法)。
      props:
        aes-key-value: 123456abc  # AES 加密的密钥值,必须是长度为 16、24 或 32 字节的密钥。

  tables:
    t_order:
      # 针对表 t_order 的加密配置。
      columns:
        status:
          # 针对 status 列进行加密配置。
          cipher:
            name: status  # 加密后列的名称仍为 status。
            encryptorName: aes_encryptor  # 使用名称为 aes_encryptor 的加密器进行加密。

props:
  sql-show: true  # 显示生成的 SQL 语句,便于调试。

2.8.5 加密规则

- !ENCRYPT
  # 配置加密规则。
  encryptors:
    aes_encryptor:
      # 加密器名称为 aes_encryptor。
      type: AES  # 加密器类型为 AES(对称加密算法)。
      props:
        aes-key-value: 123456abc  # AES 加密的密钥值,必须是长度为 16、24 或 32 字节的密钥。

  tables:
    t_order:
      # 针对表 t_order 的加密配置。
      columns:
        status:
          # 针对 status 列进行加密配置。
          cipher:
            name: status  # 加密后列的名称仍为 status。
            encryptorName: aes_encryptor  # 使用名称为 aes_encryptor 的加密器进行加密。

2.8.6 其他

props:
  sql-show: true  # 显示生成的 SQL 语句,便于调试。

2.8.7 完整文件 

databaseName: my_database
dataSources:
  ds_77_master:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.77:3306/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: master
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_77_slave:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.77:3307/db1?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: slave
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_216_master:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.216:3306/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: master
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_216_slave:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.216:3307/db2?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: slave
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_18_master:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.18:3306/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: master
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_18_slave:
    dataSourceClassName: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.186.18:3307/db3?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
    username: slave
    password: 123456
    connectionTimeoutMilliseconds: 30000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
rules:
  - !READWRITE_SPLITTING
    dataSources:
      readwrite_77:
        writeDataSourceName: ds_77_master
        readDataSourceNames:
          - ds_77_slave
        transactionalReadQueryStrategy: DYNAMIC
        loadBalancerName: random
      readwrite_216:
        writeDataSourceName: ds_216_master
        readDataSourceNames:
          - ds_216_slave
        transactionalReadQueryStrategy: DYNAMIC
        loadBalancerName: random
      readwrite_18:
        writeDataSourceName: ds_18_master
        readDataSourceNames:
          - ds_18_slave
        transactionalReadQueryStrategy: DYNAMIC
        loadBalancerName: random
    loadBalancers:
      random:
        type: RANDOM
  - !SHARDING
    tables:
      t_order:
        actualDataNodes: readwrite_77.t_order_${0..1}, readwrite_216.t_order_${0..1}, readwrite_18.t_order_${0..1}
        tableStrategy:
          standard:
            shardingColumn: order_id
            shardingAlgorithmName: t_order_inline
        keyGenerateStrategy:
          column: order_id
          keyGeneratorName: snowflake
    defaultDatabaseStrategy:
      standard:
        shardingColumn: user_id
        shardingAlgorithmName: database_inline
    defaultTableStrategy:
      none:
    bindingTables:
      - t_order
    shardingAlgorithms:
      database_inline:
        type: INLINE
        props:
          algorithm-expression: "readwrite_${(user_id % 3 == 0) ? '77' : ((user_id % 3 == 1) ? '216' : '18')}"
      t_order_inline:
        type: INLINE
        props:
          algorithm-expression: "t_order_${order_id % 2}"
    keyGenerators:
      snowflake:
        type: SNOWFLAKE
        props:
          worker-id: 123
  - !ENCRYPT
    encryptors:
      aes_encryptor:
        type: AES
        props:
          aes-key-value: 123456abc
    tables:
      t_order:
        columns:
          status:
            cipher:
              name: status
              encryptorName: aes_encryptor
props:
  sql-show: true

2.9 ShardingSphereJdbcApplicationTests.java

package org.example;

import org.example.entity.Order;
import org.example.repository.orderRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;

@SpringBootTest
class ShardingSphereJdbcApplicationTests {
    @Autowired
    orderRepository repo;
    @Test
    void contextLoads() {
        repo.findAll().forEach(System.out::println);
    }
    @Test
    void insert(){
        Order newOrder = new Order();
        newOrder.setOrderId(1);
        newOrder.setUserId(10);
        newOrder.setOrderDate(new Date());
        newOrder.setStatus("INSERT");
        repo.save(newOrder);
        System.out.println("Inserted Order: " + newOrder);
    }

    @Test
    void delete(){
        repo.deleteById(1L);
    }
}

2.10 验证结果

2.10.1 分片结果

# 根据 user_id 的值将数据分片到不同的数据源组:
        # 如果 user_id % 3 == 0,则选择 readwrite_77;
        # 如果 user_id % 3 == 1,则选择 readwrite_216;
        # 否则选择 readwrite_18。

# 根据 order_id 的值将数据分片到 t_order_0 或 t_order_1:
        # 如果 order_id % 2 == 0,则选择 t_order_0;
        # 否则选择 t_order_1。

Logic SQL: insert into t_order (order_date,status,user_id,order_id) values (?,?,?,?)
Actual SQL: ds_216_master ::: insert into t_order_1 (order_date,status,user_id,order_id) values (?, ?, ?, ?) ::: [2024-07-26 10:05:21.914, 1jyA91B85/gs5gPPkYF3WA==, 10, 1]
Inserted Order: Order(orderId=1, userId=10, orderDate=Fri Jul 26 10:05:21 CST 2024, status=INSERT)

解释:10%3=1,1%2=1,所以位于主机2(ds_216_master )的数据库, t_order_1表。 

2.10.2 加密结果

Logic SQL: insert into t_order (order_date,status,user_id,order_id) values (?,?,?,?)
Actual SQL: ds_216_master ::: insert into t_order_1 (order_date,status,user_id,order_id) values (?, ?, ?, ?) ::: [2024-07-26 10:05:21.914, 1jyA91B85/gs5gPPkYF3WA==, 10, 1]
Inserted Order: Order(orderId=1, userId=10, orderDate=Fri Jul 26 10:05:21 CST 2024, status=INSERT)

 ​​​​

2.10.3 读写分离结果 

Logic SQL: select o1_0.order_id,o1_0.order_date,o1_0.status,o1_0.user_id from t_order o1_0
Actual SQL: ds_77_slave ::: select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_0 o1_0 UNION ALL select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_1 o1_0
Actual SQL: ds_216_slave ::: select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_0 o1_0 UNION ALL select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_1 o1_0
Actual SQL: ds_18_slave ::: select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_0 o1_0 UNION ALL select o1_0.order_id,o1_0.order_date,o1_0.status AS status,o1_0.user_id from t_order_1 o1_0
Order(orderId=1, userId=10, orderDate=2024-07-26 00:00:00.0, status=INSERT)

3. 总结

        通过Docker-Compose启动3对IP不同的主从数据库,通过JPA和默认的HikariDataSource数据源结合MySQL将ShardingSphere-JDBC整合到SpringBoot框架,实现了读写分离,分库分片,以及加密和加密对特定的字段,仅供学习交流,不具备严谨性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1959581.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机毕业设计Hadoop+Spark旅游景点可视化 旅游景点推荐系统 景区游客满意度预测与优化 Apriori算法 景区客流量预测 旅游大数据 景点规划

### 开题报告 **论文题目&#xff1a;** 基于Spark的旅游景点可视化系统的设计与实现 **研究背景与意义&#xff1a;** 随着旅游业的快速发展&#xff0c;人们对旅游信息的获取和处理需求越来越高。传统的旅游信息系统虽然能够提供静态的数据查询和展示功能&#xff0c;但在…

短视频矩阵管理系统开发

在短视频内容爆炸式增长的今天&#xff0c;如何高效管理多个账号&#xff0c;同时保持内容的创新性和互动性&#xff0c;成为了创作者和品牌面临的重大挑战。短视频矩阵管理系统的开发&#xff0c;正是为了解决这一问题&#xff0c;提供一个全面的解决方案。 多账号管理&#…

本地调试指引文档

在开发组件库时&#xff0c;我们经常需要在真实的项目中测试组件库的功能&#xff0c;所以需要进行本地调试&#xff0c;本文介绍两种组件库本地调试流程&#xff0c; 1.使用beta版本 2.使用npm link 两种都可以作为本地调试的方案&#xff0c;本文作为一个参考资料&#xff0…

【机器学习】机器学习的分类算法以及使用逻辑回归算法进行分类

引言 机器学习中的分类算法是一种监督学习算法&#xff0c;其目的是根据已知的输入和输出&#xff08;即特征和标签&#xff09;来训练模型&#xff0c;以便能够预测未标记数据的类别标签 文章目录 引言一、分类算法1.1 逻辑回归&#xff08;Logistic Regression&#xff09;1.…

新手小白,开放式耳机该如何挑选?自费无广开放式耳机评测

作为一个在耳机领域探索多年的人&#xff0c;看到这个问题&#xff0c;我觉得有必要分享一些实用的经验和见解&#xff0c;希望能帮助到各位新手朋友。 首先要来说的就是在挑选开放式耳机的时候需要注意的几个点: 一、佩戴的稳固性。开放式耳机的设计较为独特&#xff0c;如果…

详细指南丨Apache环境下如何配置HTTPS证书

在Apache环境下部署SSL证书通常涉及几个步骤&#xff0c;包括生成私钥、创建CSR&#xff08;证书签名请求&#xff09;、安装SSL证书以及配置Apache服务器。下面是详细的步骤说明&#xff1a; 1. 准备工作 确认服务器和域名&#xff1a;需要有一台已经设置好的Apache服务器和…

后台管理系统(springboot+vue3+mysql)

系列文章目录 1.SpringBoot整合RabbitMQ并实现消息发送与接收 2. 解析JSON格式参数 & 修改对象的key 3. VUE整合Echarts实现简单的数据可视化 4. List&#xff1c;HashMap&#xff1c;String,String&#xff1e;&#xff1e;实现自定义字符串排序&#xff08;key排序、Val…

规划决策算法(四)---Frenet坐标系

知乎&#xff1a;坐标系转换 1.Frenet 坐标系 什么是 Frenet 坐标系&#xff1a; 为什么使用 Frenet 坐标系&#xff1a; 通常情况&#xff0c;我们只会关注车辆当前距离左右车道线的距离&#xff0c;来判断是否偏离车道&#xff0c;是否需要打方向盘进行方向微调。而不是基于…

学习测试15-实战6-根据说明书建工程

CAN协议说明书&#xff1a;含义 一&#xff0c;得到表 1&#xff0c;先建信号 2&#xff0c;建报文&#xff0c;将对应信号拖入其中 3&#xff0c;建节点&#xff0c;将报文添加进TX msg里 调整起始位 数据库建立完成 二&#xff0c;不需要面板&#xff0c;直接导入数据库&…

OpenCV图像滤波(4)构建图像金字塔函数buildPyramid()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在计算机视觉和图像处理中&#xff0c;构建图像金字塔&#xff08;Image Pyramid&#xff09;是一种常用的技术&#xff0c;它生成一系列分辨率逐…

怎么保护电脑文件夹?文件夹保护方法大盘点

文件夹是管理电脑数据的重要工具&#xff0c;可以有效避免数据混乱。而为了避免文件夹数据泄露&#xff0c;我们需要严格保护文件夹。下面我们就来盘点一下文件夹的保护方法。 文件夹隐藏 隐藏文件夹是一种简单有效的保护方式&#xff0c;通过隐藏文件夹来避免其他人发现&…

【算法】道格拉斯

一、引言 道格拉斯算法是一种用于曲线拟合的数学方法&#xff0c;特别是在处理曲线插值问题时非常有用。道格拉斯-普克算法&#xff08;Douglas-Peucker Algorithm&#xff09;&#xff0c;简称D-P算法&#xff0c;是一种用于简化多边形或折线的高效算法&#xff0c;由David Do…

如何优化PyTorch以加快模型训练速度?

PyTorch是当今生产环境中最流行的深度学习框架之一。随着模型变得日益复杂、数据集日益庞大&#xff0c;优化模型训练性能对于缩短训练时间和提高生产力变得至关重要。 本文将分享几个最新的性能调优技巧&#xff0c;以加速跨领域的机器学习模型的训练。这些技巧对任何想要使用…

【ROS 最简单教程 002/300】ROS 集成开发环境安装 (虚拟机版): Noetic

&#x1f497; 有遇到安装问题可以留言呀 ~ 当时踩了挺多坑&#xff0c;能帮忙解决的我会尽力 &#xff01; 1. 安装操作系统环境 Linux ❄️ VM / VirtualBox Ubuntu20.04 &#x1f449; 保姆级图文安装教程指路&#xff0c;有经验的话 可以用如下资源自行安装 ITEMREFERENCE…

遥感领域新方向!Mamba+RS论文汇总!

本文总结了将Mamba应用至遥感领域的相关论文&#xff08;14篇&#xff09;&#xff0c;涉及到的论文见文末链接&#xff0c;具体如下&#xff1a; 文章目录 1. 遥感图像处理2. 多/高光谱图像分类3. 变化检测/语义分割4. 遥感图像融合/超分辨率 1. 遥感图像处理 论文题目&#…

AWD神器—watchbird后台rce挖掘

简介 在传统的AWD攻防中&#xff0c;Waf扮演着重要的角色&#xff0c;Watchbird作为一款专门为AWD而生的PHP防火墙&#xff0c;具有部署简单&#xff0c;功能强大等特点&#xff0c;一出世便受到了广大CTFer的喜爱&#xff0c;目前在GitHub上已有600多star。本篇则详细介绍如果…

传输层UDP协议

传输层UDP协议 1. 再谈端口号2. UDP协议1.1 UDP协议字段1.2 将报头和数据进行分离 3. UDP的特点 1. 再谈端口号 端口号(Port)标识了一个主机上进行通信的不同的应用程序; 在 TCP/IP 协议中, 用 “源 IP”, “源端口号”, “目的 IP”, “目的端口号”, “协议号” 这样一个五元…

AI大模型评测方法总结!

大语言模型评测对应用和后续发展至关重要&#xff0c;其评测范式包括经典和新型两种。经典评测范式涵盖自然语言理解和生成任务&#xff0c;本文介绍了流行的经典评测基准及新型评测范式下的代表性基准和实例&#xff0c;并总结了现有评测的不足。随后&#xff0c;文章提出了全…

不入耳耳机和入耳耳机哪个好?四款不入耳蓝牙耳机推荐指南

那当然是不入耳耳机好啦&#xff0c;那先讲讲入耳和不入耳的区别&#xff1a; 佩戴舒适度&#xff1a; 入耳蓝牙耳机是直接插入耳道&#xff0c;所以同时也会堵塞耳道长期积攒耳垢、造成耳道不适。 不入耳蓝牙耳机则通常设计为耳挂式或耳廓式&#xff0c;挂在耳朵外部能保持…

antd pro实现后台管理系统的建立(一)

一、初始化项目 1、全局安装pro-cli初始化脚手架 # 使用 npm或者cnpm或tyarn npm i ant-design/pro-cli -g这里建议使用npm或者tyarn&#xff0c;cnpm和npm部分不兼容&#xff0c;创建项目时会报部分依赖缺失的问题 2、创建项目pro create myapp pro create myappsimple 是基…