Springboot 集成 Sharding-Jdbc

背景

一般来说数据库单表超过2000W性能就会急剧下降,我们就需要做相应的处理,分库分表,或者使用分布式数据库,而企业中较为常见的是分库分表。
分库分表目前主流的框架有两种,一种是MyCat,另一种是 ShardingSphere,就目前两种框架的整体功能来看应该是差不多,都是仅支持基本SQL92语法,复杂特殊的语法一般不支持。而Mycat是以中间件的方式给你做SQL聚合,需要搭建一个MyCat中间件,同时也要保证Mycat的高可用,如果MyCat一单挂掉,整个系统就挂了,使用MyCat变相增加了系统的复杂性和可维护性。相反 ShardingSphere就不一样了,以jar的形式分库分表,我们只需要引入相关依赖作相应配置即可完成分库分表,也是目前主流使用的分库分表解决方式。本次我们就来研究一些Sharding-Jdbc

分库分表

这里我们目前的打算分四个库,每个库2个表,以订单表为例

数据库结构如下:

db_00--
      |--t_order_0000
      |--t_order_0001
db_01--
      |--t_order_0000
      |--t_order_0001
db_02--
      |--t_order_0000
      |--t_order_0001
db_03--
      |--t_order_0000
      |--t_order_0001

建表准备

分别在 db_00,db_01,db_02.db_03创建 t_order_0000, t_order_0001

DROP TABLE IF EXISTS `t_order_0000`;
CREATE TABLE `t_order_0000` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL,
`order_id` bigint(20) DEFAULT NULL,
`user_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1239 DEFAULT CHARSET=utf8;


-- ----------------------------
-- Table structure for t_order_0001
-- ----------------------------
DROP TABLE IF EXISTS `t_order_0001`;
CREATE TABLE `t_order_0001` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT NULL,
`order_id` bigint(20) DEFAULT NULL,
`user_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1239 DEFAULT CHARSET=utf8;

同时在ds0单独见一个order_config表

DROP TABLE IF EXISTS `order_config`;
CREATE TABLE `order_config` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  `pay_timeout` int(11) DEFAULT NULL COMMENT '支付超时时间;单位:分钟',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单配置表';

在这里插入图片描述

引入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 实现对 MyBatis 的自动化配置 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!-- 实现对 Sharding-JDBC 的自动化配置 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC2</version>
        </dependency>
        <!-- 方便等会写单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

    </dependencies>

说明: sharding-jdbc-spring-boot-starter 暂时不要使用最新的 4.0.0-RC3,会有一些奇怪bug,
连接池尽量不要加入 druid,不然也会出现sharing-jdbc 的DataSource冲突,如果非要用,可自行百度找解决方法,
这是官网给出的说明
在这里插入图片描述

配置文件

Sharding-Jdbc 使用起来非常简单,难点就是核心配置文件,本次我们使用默认的分片策略


# ds1
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds0?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
# ds1
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds1?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
# ds2
spring.shardingsphere.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.jdbc-url=jdbc:mysql://localhost:3306/ds2?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=123456

# ds3
spring.shardingsphere.datasource.ds3.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds3.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds3.jdbc-url=jdbc:mysql://localhost:3306/ds3?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.shardingsphere.datasource.ds3.username=root
spring.shardingsphere.datasource.ds3.password=123456

#数据源名称,多数据源以逗号分隔
spring.shardingsphere.datasource.names=ds0,ds1,ds2,ds3
#未配置分片规则的表将通过默认数据源定位-适用于单库单表,该表无需配置分片规则
spring.shardingsphere.sharding.default-data-source-name=ds0

# 分库 以 user_id 为分片键,因为分四个库,所以取模 4,虚拟表名为 t_order
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression=ds$->{user_id % 4}
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column=user_id


# 分表 分片键位order_id,每个库分为两表,所以取模2
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression=t_order_000$->{order_id % 2}
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column=order_id
# 这里由于分库分表字段不相同配置,不然会导致使用user_id 查询找不到相应的表,如果我们分库分表都使用 user_id 则不需要这个配置
spring.shardingsphere.sharding..tables.t_order.actual-data-nodes = ds$->{0..3}.t_order_000$->{0..1}

#是否开启SQL显示,默认值: false
spring.shardingsphere.props.sql.show= true

# mybatsi 配置
# 别名
mybatis.type-aliases-package=com.zou.entity
mybatis.mapper-locations=classpath:mapper/*.xml
# 开启驼峰自动转大写
mybatis.configuration.mapUnderscoreToCamelCase=true



其他实体类和dao

Order

@Data
@ToString
public class Order {

    private String id;
    private Long userId;
    private Long orderId;
    private String userName;

}

OrderConfig

@Data
public class OrderConfig {


    private Integer id;
    private Integer payTimeout;
}

OrderDao

@Repository
public interface OrderDao {

    // 查询某个用户订单列表
    List<Order> getOrders(Order order);

    // 插入订单信息
    int addOrder(Order orderInfo);
}

OrderConfigDao

@Repository
public interface OrderConfigDao {
    List<OrderConfig> getOrderConfig();
}

OrderMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zou.dao.OrderDao">


    <select id="getOrders" parameterType="Order"
            resultType="Order">
        select
            t.id ,
            t.user_id ,
            t.order_id ,
            t.user_name
        from t_order t
        where t.user_id=#{userId}
	</select>

    <insert id="addOrder" parameterType="Order" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_order (
            user_id,user_name,order_id
        ) VALUES (
            #{userId},#{userName},#{orderId}
        )
    </insert>

</mapper>

OrderConfigMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zou.dao.OrderConfigDao">


    <select id="getOrderConfig" parameterType="Integer" resultType="OrderConfig">
        SELECT *
        FROM order_config
    </select>

</mapper>

测试

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

    @Autowired
    OrderDao orderDao;


    @Test
    public void insert() {
        for (int i = 0; i < 1000; i++) {
            long userId = i;
            long orderId = i + 1;
            Order order = new Order();
            order.setUserName("阿离");
            order.setUserId(userId);
            order.setOrderId(orderId);
            int result = orderDao.addOrder(order);
        }
    }

   

}

插入数据逻辑:
userId = 0 --> 0%4 = 0 对应数据库 db0
orderId = 1 --> 1%2 = 1 对应表 t_order_0001

所以第一条数据就落到 db0数据库的 t_order_0001表
在这里插入图片描述

其他的也是如此

查询

 @Test
    public void testQueryList() {
        Order order = new Order();
        order.setUserId(2l);

        List<Order> list = orderDao.getOrders(order);
        list.forEach(s -> System.out.println(s));
    }

由于我们只指定了分库片 userId查询,所以这里就会查询 ds2数据库,但是无法确定是那张表,所以会查询所有表,然后将所有表的结果归并返回,我们看sql可以看出
在这里插入图片描述

@Test
    public void getOrders() {
        Order order = new Order();
        order.setUserId(8l);
        order.setOrderId(8l);
       orderDao.getOrders(order);

    }

如果我们指定 userId 和 orderId就可以找到对应的库和表,就只会查询一张表
在这里插入图片描述

如果我们查询默认没有分库分表的普通表 order_config,则会自动去我们配置的默认数据库去找到相应的表

@Autowired
    OrderConfigDao orderConfigDao;

    @Test
  public  void getOrderConfig() {
        orderConfigDao.getOrderConfig();

    }

源码地址

springcloud-shopping-parent

参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值