# Sharding-JDBC从入门到精通(6)-- Sharding-JDBC 水平分库 和 垂直分库。

Sharding-JDBC从入门到精通(6)-- Sharding-JDBC 水平分库 和 垂直分库。

一、Sharding-JDBC 水平分库-分片策略配置

1、分库策略定义方式如下

# 分库策略,如何将一个逻辑表映射到多个数据源
spring.shardingsphere.sharding.tables.<逻辑表名称>.qatabase-strategy.<分片策略>.<分片策略属性名>= #分片策略属性值

# 分表策略,如何将一个逻辑表映射为多个实际表
spring.shardingsphere.sharding.tables.<逻辑表名称>.table-strategy.<分片策略>.<分片策略属性名>= #分片策略属性值


# 分库策略:以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,否则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}

2、分片规则修改:

由于数据库需要拆分了两个,这里需要配置两个数据源。
分库需要配置分库的策略,和分表策略的意义类似,通过分库策略实现数据操作针对分库的数据库进行操作。

# 配置 sharding-jdbc 分片规则(2024-6-29 分片规则修改)
# 定义数据源(定义 多个 数据源名为 m1, m2)
spring.shardingsphere.datasource.names = m1,m2

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311

spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311

3、在 sharding_jdbc_simple 子工程(子模块)中,修改 application.properties 配置文件,添加配置分库策略。

#  dbsharding\sharding_jdbc_simple\src\main\resources\application.properties

server.port = 56081

spring.application.name = sharding-jdbc-simple-demo

server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = true

spring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true

# 配置 sharding-jdbc 分片规则(2024-6-29 分片规则修改)
# 定义数据源(定义 多个 数据源名为 m1, m2)
spring.shardingsphere.datasource.names = m1,m2

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311

spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311

# 分库策略:以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,否则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}


# 指定 t_order 表的数据分布情况,配置数据节点(t_order 映射到 t_order_1 或者 t_order_2)
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}

# 指定 t_order 表的主键生成策略为 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE

# 指定 t_order 表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}

# 打开 sql 输出日志
spring.shardingsphere.props.sql.show = true

swagger.enable = true

logging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug

4、Sharding-JDBC 支持以下几种分片策略:

不管理分库还是分表,策略基本一样。

  • standard: 标准分片策略,对应 Standardshardingstrategy。提供对 SQL 语句中的 =,IN 和 BETWEEN AND 的分片操作支持。StandardShardingStrategy 只支持单分片键,提供 PreciseShardingAlgorithm 和 RangeShardingAlgorithm 两个分片算法。PreciseShardingAlgorithm 是必选的,用于处理 = 和 IN 的分片。RangeShardingAlgorithm 是可选的,用于处理 BETWEEN AND 分片,如果不配置 RangeshardingAlgorithm,SQL 中的 BETWEEN AND 将按照全库路由处理。

  • complex: 符合分片策略,对应 ComplexshardingStrategy。复合分片策略。提供对 SQL 语句中的 =,IN 和 BETWEEN AND 的分片操作支持。ComplexshardingStrategy 支持多分片键,由于多分片键之间的关系复杂因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。

  • inline: 行表达式分片策略,对应 InlineshardingStrategy。使用 Groovy 的表达式,提供对 SQL 语句中的 = 和 IN 的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如:t_user_$->{u id % 8} 表示 t_user 表根据u_id 模8,而分成8张表,表名称为 t_user_0 到 t_user_7。

  • hint: Hint 分片策略,对应 HintShardingStrategy。通过 Hint 而非 SQL 解析的方式分片的策略。对于分片字段非 SQL决 定,而由其他外置条件决定的场景,可使用 SQLHint 灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint 支持通过 Java API 和 SQL 注释(待实现)两种方式使用。

  • none : 不分片策略,对应 NoneShardingStrategy。不分片的策略。

5、创建两个数据库:order_db_1, order_db_2

CREATE DATABASE `order_db_1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
CREATE DATABASE `order_db_2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

6、在 order_db_1, order_db_2 中分别 创建 t_order_1 和 t_order_2 两个表:


# 在 数据库 order_db_1 中,创建两张表。

USE `order_db_1`;

# 创建  t_order_1 表

DROP TABLE IF EXISTS `t_order_1`;

CREATE TABLE `t_order_1` (
`order_id` BIGINT(20) NOT NULL COMMENT '订单id',
`price` DECIMAL(10,2) NOT NULL COMMENT '订单价格',
`user_id` BIGINT(20) NOT NULL COMMENT '下单用户id', 
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


# 创建  t_order_2 表

DROP TABLE IF EXISTS `t_order_2`;

CREATE TABLE `t_order_2` (
`order_id` BIGINT(20) NOT NULL COMMENT '订单id',
`price` DECIMAL(10,2) NOT NULL COMMENT '订单价格',
`user_id` BIGINT(20) NOT NULL COMMENT '下单用户id', 
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


# 在 数据库 order_db_2 中,创建两张表。

USE `order_db_2`;

# 创建  t_order_1 表

DROP TABLE IF EXISTS `t_order_1`;

CREATE TABLE `t_order_1` (
`order_id` BIGINT(20) NOT NULL COMMENT '订单id',
`price` DECIMAL(10,2) NOT NULL COMMENT '订单价格',
`user_id` BIGINT(20) NOT NULL COMMENT '下单用户id', 
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


# 创建  t_order_2 表

DROP TABLE IF EXISTS `t_order_2`;

CREATE TABLE `t_order_2` (
`order_id` BIGINT(20) NOT NULL COMMENT '订单id',
`price` DECIMAL(10,2) NOT NULL COMMENT '订单价格',
`user_id` BIGINT(20) NOT NULL COMMENT '下单用户id', 
`status` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY(`order_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

二、Sharding-JDBC 水平分库-插入订单

1、在 sharding_jdbc_simple 子工程(子模块)中,修改 application.properties 配置文件,添加配置分库策略。

#  dbsharding\sharding_jdbc_simple\src\main\resources\application.properties

server.port = 56081

spring.application.name = sharding-jdbc-simple-demo

server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = true

spring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true

# 配置 sharding-jdbc 分片规则(2024-6-29 分片规则修改)
# 定义数据源(定义 多个 数据源名为 m1, m2)
spring.shardingsphere.datasource.names = m1,m2

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311

spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311

# 分库策略:以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,否则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}


# 指定 t_order 表的数据分布情况,配置数据节点(t_order 映射到 t_order_1 或者 t_order_2)
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}

# 指定 t_order 表的主键生成策略为 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE

# 指定 t_order 表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}

# 打开 sql 输出日志
spring.shardingsphere.props.sql.show = true

swagger.enable = true

logging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug

2、在 sharding_jdbc_simple 子工程(子模块)中,测试类 OrderDao 的测试类 OrderDaoTest.java 进行测试

/**
 *   dbsharding\sharding_jdbc_simple\src\test\java\djh\it\dbsharding\simple\dao\OrderDaoTest.java
 *
 *   2024-6-28 创建 接口 OrderDao 的测试类 OrderDaoTest.java 进行测试
 *
 *   快速生成 接口 OrderDao 类的测试类:
 *   1)右键 接口 OrderDao 选择 【Generate...】
 *   2)选择【Test..】
 *   3)Testing library : JUnit4
 *      Class name : OrderDaoTest
 *      SUPERCLASS : 空
 *      Destination package : djh.it.dbsharding.simple.dao
 *   4)点击 OK。
 */
package djh.it.dbsharding.simple.dao;

import djh.it.dbsharding.simple.ShardingJdbcSimpleBootstrap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class OrderDaoTest {

    @Autowired
    OrderDao orderDao;

    @Test  //查询
    public void testSelectOrderByIds(){
        List<Long> ids = new ArrayList<>();
        ids.add(1013467489922711552L);  //此order_id 在 mysql 数据库的 t_order_1 表中,
        ids.add(1013465458055053313L);  //此order_id 在 mysql 数据库的 t_order_2 表中,
        List<Map> maps = orderDao.selectOrderByIds(ids);
        System.out.println(maps);
    }

    @Test  //插入数据
    public void testInsertOrder(){
//            // 1)此数据会插入到 m1 数据库: 1L % 2 + 1 = 2 得到 M2,由此可得 向 order_db_2 数据库中插入 20 条数据。
//            orderDao.insertOrder(new BigDecimal(i ),1L, "success2");

            // 2)此数据会插入到 m1 数据库: 4L % 2 + 1 = 1 得到 M1,由此可得 向 order_db_1 数据库中插入 20 条数据。
        for(int i=1; i<20; i++){
            orderDao.insertOrder(new BigDecimal(i ),4L, "success2");
        }
    }
}

3、运行测试类 OrderDaoTest.java 插入数据 testInsertOrder 方法,进行测试。

1)1L % 2 + 1 = 2 得到 M2,由此可得 向 t_order_2 数据库中插入 20 条数据。
2)此数据会插入到 m1 数据库: 4L % 2 + 1 = 1 得到 M1,由此可得 向 order_db_1 数据库中插入 10 条数据。

插入数据.png

三、Sharding-JDBC 水平分库-查询订单

1、在 sharding_jdbc_simple 子工程(子模块)中,修改 application.properties 配置文件,添加配置分库策略。

#  dbsharding\sharding_jdbc_simple\src\main\resources\application.properties

server.port = 56081

spring.application.name = sharding-jdbc-simple-demo

server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = true

spring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true

# 配置 sharding-jdbc 分片规则(2024-6-29 分片规则修改)
# 定义数据源(定义 多个 数据源名为 m1, m2)
spring.shardingsphere.datasource.names = m1,m2

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311

spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311

# 分库策略:以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,否则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}


# 指定 t_order 表的数据分布情况,配置数据节点(t_order 映射到 t_order_1 或者 t_order_2): 只能路由到 m1 数据库
#spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}
# 指定 t_order 表的数据分布情况,配置数据节点(t_order 映射到 t_order_1 或者 t_order_2): 动态路由到 m1 数据库 或 m2 数据库。
 spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m$->{1..2}.t_order_$->{1..2}

# 指定 t_order 表的主键生成策略为 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE

# 指定 t_order 表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}

# 打开 sql 输出日志
spring.shardingsphere.props.sql.show = true

swagger.enable = true

logging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug

2、在 sharding_jdbc_simple 子工程(子模块)中,修改 dao 接口类 OrderDao.java 添加 //查询数据:根据订单ID 和 用户 id 查询订单 方法。

/**
 *   dbsharding\sharding_jdbc_simple\src\main\java\djh\it\dbsharding\simple\dao\OrderDao.java
 *
 *   2024-5-28 创建 dao 接口类 OrderDao.java
 */
package djh.it.dbsharding.simple.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

@Mapper
@Component
public interface OrderDao {

    //查询数据:根据订单ID 和 用户 id 查询订单
    // ( SELECT * FROM t_order_1 WHERE user_id = 4 AND order_id IN (1014650592712196096, 1014650593467170816); )
    @Select( "<script>" +
            "select" +
            " * " +
            " from t_order t " +
            " where t.order_id in " +
            " <foreach collection=' orderIds' open='(' separator=',' close=')' item='id'>" +
            " #{id} " +
            " </foreach>" +
            " and user_id = ${userId}" +
            "</script>" )
    List<Map> selectOrderByIdsAndUserId(@Param("userId") Long userId, @Param("orderIds") List<Long> orderIds);

    //查询数据:根据订单ID ( SQL 语句:SELECT * FROM t_order_1 WHERE order_id IN (1013467489922711552, 1013467489960460288); )
    @Select( "<script>" +
            "select" +
            " * " +
            " from t_order t " +
            " where t.order_id in " +
            " <foreach collection=' orderIds' open='(' separator=',' close=')' item='id'>" +
            " #{id} " +
            " </foreach>" +
            "</script>" )
    List<Map> selectOrderByIds(@Param("orderIds") List<Long> orderIds);

    //插入数据
    @Insert("insert into t_order(price, user_id, status) values(#{price}, #{userId}, #{status})")
    int insertOrder(@Param("price") BigDecimal price, @Param("userId")Long userId, @Param("status")String status);
}

3、在 sharding_jdbc_simple 子工程(子模块)中,修改 测试类 OrderDao 的测试类 查询方法,进行多次查询测试。添加 根据订单ID 和 用户 id 查询订单 的方法。

/**
 *   dbsharding\sharding_jdbc_simple\src\test\java\djh\it\dbsharding\simple\dao\OrderDaoTest.java
 *
 *   2024-6-28 创建 接口 OrderDao 的测试类 OrderDaoTest.java 进行测试
 *
 *   快速生成 接口 OrderDao 类的测试类:
 *   1)右键 接口 OrderDao 选择 【Generate...】
 *   2)选择【Test..】
 *   3)Testing library : JUnit4
 *      Class name : OrderDaoTest
 *      SUPERCLASS : 空
 *      Destination package : djh.it.dbsharding.simple.dao
 *   4)点击 OK。
 */
package djh.it.dbsharding.simple.dao;

import djh.it.dbsharding.simple.ShardingJdbcSimpleBootstrap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class OrderDaoTest {

    @Autowired
    OrderDao orderDao;

    @Test  //查询--根据订单ID 和 用户 id 查询订单
    public void testSelectOrderByIdsAndUserId(){

        List<Long> ids = new ArrayList<>();

        // 水平分库测试如下:
        List<Long> ids2 = new ArrayList<>();

//        //1)查询不存在的 order_id(在 m1 或 m2 中,都不存在),添加了 userId , 会输出真实的2条SQL语句。
//        ids2.add(1013467489922711552L);  //此 order_id 在 mysql 的 m1 和 m2 数据库中 的 t_order_1 表 和 t_order_2 表中 都不存在
//        ids2.add(1013465458055053313L);  //此 order_id 在 mysql 的 m1 和 m2 数据库中 的 t_order_1 表 和 t_order_2 表中 都不存在
//        List<Map> maps = orderDao.selectOrderByIdsAndUserId(4L, ids2);
//        System.out.println(maps);

        //2)查询存在的 order_id(一条在 m1 数据库,一条在 m2 数据库中),添加了 userId , 会输出真实的2条SQL语句。
        ids2.add(1014650592712196096L);  //此 order_id 在 mysql 的 m1 数据库中 t_order_1 表 中。
        ids2.add(1014635737775079425L);  //此 order_id 在 mysql 的 m2 数据库中 t_order_2 表 中。
        List<Map> maps2 = orderDao.selectOrderByIdsAndUserId(4L, ids2);
        System.out.println(maps2);

    }

    @Test  //查询
    public void testSelectOrderByIds(){

        List<Long> ids = new ArrayList<>();

//        //查询数据库 order_db 中的数据 order_id(水平分表)
//        ids.add(1013467489922711552L);  //此order_id 在 mysql 数据库的 t_order_1 表中,
//        ids.add(1013465458055053313L);  //此order_id 在 mysql 数据库的 t_order_2 表中,
//        List<Map> maps = orderDao.selectOrderByIds(ids);
//        System.out.println(maps);

        // 水平分库测试如下:
        List<Long> ids2 = new ArrayList<>();

        //1)查询不存在的 order_id(在 m1 或 m2 中,都不存在),会输出真实的4条SQL语句。
        ids2.add(1013467489922711552L);  //此 order_id 在 mysql 的 m1 和 m2 数据库中 的 t_order_1 表 和 t_order_2 表中 都不存在
        ids2.add(1013465458055053313L);  //此 order_id 在 mysql 的 m1 和 m2 数据库中 的 t_order_1 表 和 t_order_2 表中 都不存在
        List<Map> maps = orderDao.selectOrderByIds(ids2);
        System.out.println(maps);


//        //2)查询存在的 order_id(一条在 m1 数据库,一条在 m2 数据库中),会输出真实的4条SQL语句。
//        ids2.add(1014650592712196096L);  //此 order_id 在 mysql 的 m1 数据库中 t_order_1 表 中。
//        ids2.add(1014635737775079425L);  //此 order_id 在 mysql 的 m2 数据库中 t_order_2 表 中。
//        List<Map> maps2 = orderDao.selectOrderByIds(ids2);
//        System.out.println(maps2);

//        //3)查询存在的 order_id(2条都在 m1 数据库,不同表中),会输出真实的4条SQL语句。
//        ids2.add(1014650592712196096L);  //此 order_id 在 mysql 的 m1 数据库中 t_order_1 表 中。
//        ids2.add(1014635737775079425L);  //此 order_id 在 mysql 的 m1 数据库中 t_order_2 表 中。
//        List<Map> maps2 = orderDao.selectOrderByIds(ids2);
//        System.out.println(maps2);

//        //4)查询存在的 order_id(2条都在 m1 数据库,同一表中),会输出真实的2条SQL语句。
//        ids2.add(1014650592712196096L);  //此 order_id 在 mysql 的 m1 数据库中 t_order_1 表 中。
//        ids2.add(1014635742883741696L);  //此 order_id 在 mysql 的 m1 数据库中 t_order_1 表 中。
//        List<Map> maps2 = orderDao.selectOrderByIds(ids2);
//        System.out.println(maps2);

//        //5)查询存在的 order_id(1条都在 m2 数据库),会输出真实的2条SQL语句。
//        ids2.add(1014635741851942912L);  //此 order_id 在 mysql 的 m2 数据库中 t_order_1 表 中。
//        List<Map> maps2 = orderDao.selectOrderByIds(ids2);
//        System.out.println(maps2);

    }

    @Test  //插入数据
    public void testInsertOrder(){
        //orderDao.insertOrder(new BigDecimal(11 ),1L, "SUCCESS");
        for(int i=1; i<10; i++){
//            // 1)此数据会插入到 m1 数据库: 1L % 2 + 1 = 2 得到 M2,由此可得 向 order_db_2 数据库中插入 20 条数据。
//            orderDao.insertOrder(new BigDecimal(i ),1L, "success2");

            // 2)此数据会插入到 m1 数据库: 4L % 2 + 1 = 1 得到 M1,由此可得 向 order_db_1 数据库中插入 10 条数据。
            orderDao.insertOrder(new BigDecimal(i ),4L, "success3");
        }
    }
}

4、运行测试类 OrderDaoTest.java 查询数据 testSelectOrderByIds 方法,进行测试。

1)查询不存在的 order_id(在 m1 或 m2 中,都不存在),会输出真实的4条SQL语句。

查询数据1.png

2)查询存在的 order_id(一条在 m1 数据库,一条在 m2 数据库中),会输出真实的4条SQL语句。

查询数据2.png

3)查询存在的 order_id(2条都在 m1 数据库,不同表中),会输出真实的4条SQL语句。

查询数据3.png

4)查询存在的 order_id(2条都在 m1 数据库 同一表中),会输出真实的2条SQL语句。

查询数据4.png

5)查询存在的 order_id(1条都在 m2 数据库),会输出真实的2条SQL语句。

查询数据5.png

6)查询不存在的 order_id(在 m1 或 m2 中,都不存在),添加了 userId ,

查询数据6.png

7)查询存在的 order_id(一条在 m1 数据库,一条在 m2 数据库中),添加了 userId , 会输出真实的2条SQL语句。

查询数据7.png

四、Sharding-JDBC 垂直分库-分片策略配置

1、创建数据库:user_db

CREATE DATABASE `user_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

2、在 suer_db 中分别 创建 t_user 表:


# 在 数据库 order_db_1 中,创建两张表。

USE `user_db`;

# 创建  t_user 表

DROP TABLE IF EXISTS `t_user`;

CREATE TABLE `t_user` (
`user_id` BIGINT(20) NOT NULL COMMENT '用户id',
`fullname` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户姓名',
`user_type` char(1) DEFAULT NULL COMMENT '用户类型', 
PRIMARY KEY(`user_id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

3、在 application.properties 配置文件中,配置数据源,

spring.shardingsphere.datasource.names = m0,m1,m2

spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true
spring.shardingsphere.datasource.m0.username = root
spring.shardingsphere.datasource.m0.password = 12311

# 分库策略:以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,否则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}

# 配置 user_db 数据节点
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes = m0.t_user

# 指定 t_user 表的分片策略,分片策略包括分片键和分片算法(未分库分表也需要配置)
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression = t_user

五、Sharding-JDBC 垂直分库-插入和查询测试

1、在 sharding_jdbc_simple 子工程(子模块)中,修改 application.properties 配置文件,添加 user_db 数据库的 配置分库分表策略。

#  dbsharding\sharding_jdbc_simple\src\main\resources\application.properties

server.port = 56081

spring.application.name = sharding-jdbc-simple-demo

server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = utf-8
spring.http.encoding.force = true

spring.main.allow-bean-definition-overriding = true
mybatis.configuration.map-underscore-to-camel-case = true

# 配置 sharding-jdbc 分片规则(2024-6-29 分片规则修改)
# 定义数据源(定义 多个 数据源名为 m1, m2)
spring.shardingsphere.datasource.names = m0,m1,m2

spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true
spring.shardingsphere.datasource.m0.username = root
spring.shardingsphere.datasource.m0.password = 12311

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 12311

spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = 12311

# 分库策略:以 user_id 为分片键,分片策略为 user_id % 2 + 1,user_id 为偶数操作 m1 数据源,否则操作 m2
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression = m$->{user_id % 2 + 1}


# 指定 t_order 表的数据分布情况,配置数据节点(t_order 映射到 t_order_1 或者 t_order_2): 只能路由到 m1 数据库
#spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..2}
# 指定 t_order 表的数据分布情况,配置数据节点(t_order 映射到 t_order_1 或者 t_order_2): 动态路由到 m1 数据库 或 m2 数据库。
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m$->{1..2}.t_order_$->{1..2}
# 配置 user_db 数据节点
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes = m$->{0}.t_user


# 指定 t_order 表的主键生成策略为 SNOWFLAKE(雪花算法)
spring.shardingsphere.sharding.tables.t_order.key-generator.column = order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type = SNOWFLAKE

# 指定 t_order 表的分片策略,分片策略包括分片键和分片算法
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 2 + 1}

# 指定 t_user 表的分片策略,分片策略包括分片键和分片算法(未分库分表也需要配置)
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression = t_user

# 打开 sql 输出日志
spring.shardingsphere.props.sql.show = true

swagger.enable = true

logging.level.root = info
logging.level.org.springframework.web = info
logging.level.djh.it.dbsharding = debug
logging.level.druid.sql = debug

2、在 sharding_jdbc_simple 子工程(子模块)中,创建 接口类 UserDao.java

/**
 *   dbsharding\sharding_jdbc_simple\src\main\java\djh\it\dbsharding\simple\dao\UserDao.java
 *
 *   2024-7-1 创建 接口类 UserDao.java
 */
package djh.it.dbsharding.simple.dao;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Mapper
@Component
public interface UserDao {

    //新增用户
    @Insert("insert into t_user(user_id, fullname) value(#{userId}, #{fullname})")
    int insertUser(@Param("userId") Long userId, @Param("fullname") String fullname);

    //根据id列表查询多个用户
    @Select({ "<script>" ,
            " select" ,
            " * " ,
            " from t_user t " ,
            " where t.user_id in ",
            " <foreach collection=' userIds' open='(' separator=',' close=')' item='id'>" +
            " #{id} " ,
            " </foreach>" ,
            "</script>" })
    List<Map> selectUserId(@Param("userIds") List<Long> userIds);
}

3、在 sharding_jdbc_simple 子工程(子模块)中,创建 接口类 UserDao.java 的测试类 UserDaoTest.java 进行插入数据 和 查询数据测试。

/**
 *   dbsharding\sharding_jdbc_simple\src\test\java\djh\it\dbsharding\simple\dao\UserDaoTest.java
 *
 *   2024-7-1 创建 接口类 UserDao.java 的测试类 UserDaoTest.java 进行插入数据 和 查询数据测试 UserDaoTest.java
 */
package djh.it.dbsharding.simple.dao;

import djh.it.dbsharding.simple.ShardingJdbcSimpleBootstrap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class UserDaoTest {

    @Autowired
    UserDao userDao;

    @Test
    public void testInsertUser() {
        for(int i=0; i<10; i++){
            Long id = i + 1L;
            userDao.insertUser(id, "姓名" + id);
        }
    }

    @Test
    public void testSelectUserByIds() {
        List<Long> userIds = new ArrayList<>();
        userIds.add(1L);
        userIds.add(2L);
        List<Map> users = userDao.selectUserId(userIds);
        System.out.println(users);
    }
}

4、运行测试类 UserDaoTest.java,查看结果。

Sharding-JDBC垂直分库-插入和查询测试.png

在这里插入图片描述

上一节关联链接请点击
# Sharding-JDBC从入门到精通(5)-- Sharding-JDBC 执行原理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用MySQL+node+vue做一个学生信息管理系统(二):创建MySQL数据表、创建HTML用户列表页面

MySQL代码 CREATE DATABASE students;USE students;CREATE TABLE student( id INT COMMENT 学号, name VARCHAR(32) COMMENT 姓名, sex VARCHAR(8) COMMENT 性别, class VARCHAR(64) COMMENT 班级 )SHOW TABLES;下面介绍一下Vue框架的element-ui的使用方法&#xff0c;这里就不…

【第21章】MyBatis-Plus多数据源支持

文章目录 前言一、dynamic-datasource1. 特性2. 约定3. 使用方法3.1 引入依赖3.2 配置数据源3.3 使用 DS 切换数据源 二、mybatis-mate1.特性2.使用方法2.1 配置数据源2.2 使用 Sharding 切换数据源2.3 切换指定数据库节点 三、实战1. 引入库2. 配置3. 使用 DS 切换数据源4. 测…

秋招突击——7/5——复习{}——新作{跳跃游戏II、划分字母区间、数组中的第K个大的元素(模板题,重要)、前K个高频元素}

文章目录 引言正文贪心——45 跳跃游戏II个人实现参考实现 划分字母区间个人实现 参考实现数组中的第K个最大元素个人实现参考做法 前K个高频元素个人实现参考实现 总结 引言 今天就开始的蛮早的&#xff0c;现在是九点多&#xff0c;刚好开始做算法&#xff0c;今天有希望能够…

封锁-封锁模式(共享锁、排他锁)、封锁协议(两阶段封锁协议)

一、引言 1、封锁技术是目前大多数商用DBMS采用的并发控制技术&#xff0c;封锁技术通过在数据库对象上维护锁来实现并发事务非串行调度的冲突可串行化 2、基于锁的并发控制的基本思想是&#xff1a; 当一个事务对需要访问的数据库对象&#xff0c;例如关系、元组等进行操作…

RocketMQ-订阅一致及解决方案

背景 这里借用Rocketmq官方的一句话来描述订阅关系一致: 订阅关系一致指的是同一个消费者分组Group ID下&#xff0c;所有Consumer实例所订阅的Topic和Tag必须完全一致。如果订阅关系不一致&#xff0c;可能导致消息消费逻辑混乱&#xff0c;消息被重复消费或遗漏。 具体的问题…

BS结构的毕业设计题目管理系统-计算机毕业设计源码92342

目 录 摘要 1 绪论 1.1 研究背景 1.2目的及意义 1.3论文结构与章节安排 2 毕业设计题目管理系统设计分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分…

3D打印推动透气钢革命

在科技日新月异的今天&#xff0c;3D打印技术如同一股强劲的潮流&#xff0c;正悄然改变着制造业。从简单的塑料玩具到复杂的工业部件&#xff0c;再到高精尖的医疗器械&#xff0c;3D打印技术凭借其独特的优势&#xff0c;不断拓宽着应用的边界。今天&#xff0c;我们一起深度…

Linux-DNS

DNS域名解析服务 1.DNS介绍 DNS 是域名系统 (Domain Name System) 的缩写&#xff0c;是因特网的一项核心服务&#xff0c;它作为可以将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP数串。…

MySQL/SqlServer 跨服务器 增删改查(CRUD) 的一种方法

前言&#xff1a;主要是利用SqlServer 的链接服务器功能 1.准备一台 SqlServer Server&#xff0c;服务如下图&#xff1a; 这台服务器专门用于 链接服务器 IP&#xff1a;10.x.x.3 和数据源服务器&#xff08;10.x.x.5&#xff09; 在一个局域网 1.1 版本 是 2017 2.在 10.…

算法体系-26 第二十六节:第26节:单调栈结构 (5节)

一 单调栈知识讲解 1.1描述 一个数组里面想的到每个位置与他最近的左边和右边比他小的最近的信息 1.2 分析 通过单调栈的特点&#xff0c;for遍历数组中的每个数&#xff0c;当前数来的时候对比单调栈中的数进行每个数的左右判断完满足条件的进行更新到当前i种的 int[][] re…

MySQL索引教程(01):创建索引

文章目录 MySQL 创建索引索引介绍MySQL CREATE INDEX 语法MySQL 索引类型MySQL CREATE INDEX 实例结论 MySQL 创建索引 对于一个具有大量数据行的表&#xff0c;如果你根据某个查询条件检索数据时很慢&#xff0c;可能是因为你没有在检索条件相关的列上创建索引。 索引类似于…

平价猫粮新选择!福派斯鲜肉猫粮,让猫咪享受美味大餐!

福派斯鲜肉猫粮&#xff0c;作为一款备受铲屎官们青睐的猫粮品牌&#xff0c;凭借其卓越的品质和高性价比&#xff0c;为众多猫主带来了健康与美味的双重享受。接下来&#xff0c;我们将从多个维度对这款猫粮进行解析&#xff0c;让各位铲屎官更加全面地了解它的魅力所在。 1️…

查看电脑显卡(NVIDIA)应该匹配什么版本的CUDA Toolkit

被串行计算逼到要吐时&#xff0c;决定重拾CUDa了&#xff0c;想想那光速般的处理感觉&#xff08;夸张了&#xff09;不要太爽&#xff0c;记下我的闯关记录。正好我的电脑配了NVIDIA独显&#xff0c;GTX1650&#xff0c;有菜可以炒呀&#xff0c;没有英伟达的要绕道了。回到正…

详细分析SQL语句中的硬解析、软解析、软软解析基本知识

目录 前言1. 基本知识2. Demo 前言 从实战中探索 图为全局搜索且在高并发下&#xff0c;会引发硬解析&#xff0c;导致CPU崩溃 1. 基本知识 解析 (parsing) 是数据库在处理 SQL 语句时必不可少的一步&#xff0c;它将 SQL 语句转换为数据库可以执行的低级指令 硬解析 (Hard…

昇思25天学习打卡营第18天|Pix2Pix实现图像转换

Pix2Pix概述 Pix2Pix是基于条件生成对抗网络实现的一种深度学习图像转换模型。Pix2Pix是将cGAN应用于有监督的图像到图像翻译&#xff0c;包括生成器和判别器。 基础原理 cGAN的生成器是将输入图片作为指导信息&#xff0c;由输入图像不断尝试生成用于迷惑判别器的“假”图像…

c++ 附赠课程的知识点记录

&#xff08;1&#xff09; 静态变量的赋值 再一个例子&#xff1a; &#xff08;2&#xff09; 一般在定义类的赋值运算符函数时&#xff0c; operator ( const A& a ) 函数&#xff0c;应避免自赋值的情况&#xff0c;就是把对象 a 又赋值给 对象a 如同 a a 这样的情况…

类和对象深入理解

目录 static成员概念静态成员变量面试题补充代码1代码2代码3如何访问private中的成员变量 静态成员函数静态成员函数没有this指针 特性 友元友元函数友元类 内部类特性1特性2 匿名对象拷贝对象时的一些编译器优化 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接…

C++ | Leetcode C++题解之第217题存在重复元素

题目&#xff1a; 题解&#xff1a; class Solution { public:bool containsDuplicate(vector<int>& nums) {unordered_set<int> s;for (int x: nums) {if (s.find(x) ! s.end()) {return true;}s.insert(x);}return false;} };

【PB案例学习笔记】-27制作一个控制任务栏显示与隐藏的小程序

写在前面 这是PB案例学习笔记系列文章的第27篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

视频参考帧和重构帧复用

1、 视频编码中的参考帧和重构帧 从下图的编码框架可以看出&#xff0c;每编码一帧需要先使用当前帧CU(n)减去当前帧的参考帧CU&#xff08;n&#xff09;得到残差。同时&#xff0c;需要将当前帧的重构帧CU*&#xff08;n&#xff09;输出&#xff0c;然后再读取重构帧进行预测…