Sharding-JDBC入门

10 篇文章 0 订阅

Sharding-JDBC入门

​ 最近在做一个java后台重构项目时碰到了一个问题,就是系统的数据量越来越大,单表的压力剧增,已经不能满足正常的数据操作了。这个时候就需要有分库分表的处理了,由于资源和业务限制,最终只选择了分表。本来想着分表简单,对数据入库和索引时添加逻辑判断,选择对应表处理就完事了,结果项目里有位老哥说了,我们可以尝试用Sharding-JDBC做分表。这突然引起了我的兴趣,于是乎边开始研究一下Sharding-JDBC了。

​ 本片主要是对Sharding-JDBC做一个入门,想要深入研究见官网(Apache ShardingSphere):

http://shardingsphere.apache.org/index_zh.html

在这里插入图片描述

1.基本概念

1.1分库和分表

1.1.1水平分库和垂直分库

垂直分库:指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器上,它的核心理念

是专库专用。

​ 简单说就是,在数据不断增长的情况向将不同的业务数据放到不同的数据库中,减少数据库压力。例如将订单数据和商品数据放到不同的数据库中。

水平分库:是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。

​ 上面的垂直分表解决了不同业务在同一个数据库访问的压力,但是如果某个业务量非常巨大,对单个业务数据库的访问量也是大数据量级别,那么依然存在问题,这时候就可以将某个业务库的数据库进行水平拆分。入将商品库水平两个库,将商品安装不同规则放入到两个数据库中。

1.1.2水平分表和垂直分表

垂直分表:将一个表按照字段分成多表,每个表存储其中一部分字段。

​ 垂直分表就是根据数据存储的特点将一张表的数据氛围多张表,比如讲商品表氛围商品基本信息(商品id,商品名称)表和商品详情表(商品简介、商品参数等)。

水平分表:是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。

​ 其实理念和水平分库类似,就是为了把单表的压力分散到多表上。每一张表的结构和字段是一样的。

1.2Sharding-JDBC基本术语

1.2.1逻辑表

水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为 10 张表,分别是 t_order_0t_order_9,他们的逻辑表名为 t_order

1.2.2真实表

在分片的数据库中真实存在的物理表。即上个示例中的 t_order_0t_order_9

1.2.3数据节点

数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0

1.2.4绑定表

指分片规则一致的主表和子表。例如:t_order 表和 t_order_item 表,均按照 order_id 分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。

1.2.5广播表

指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。

2.简单案例

​ 这里我们主要实现一个基于sharding-jdbc的分表案例。

2.1创建数据库和表

init.sql:

CREATE DATABASE `shardingjdbc_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
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; 
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;

生成数据库和表。

2.2搭建Maven工程

2.2.1新建工程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-54mVRUCw-1612013347876)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210130103730123.png)]

2.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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hexin</groupId>
    <artifactId>sharding-jdbc-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

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

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

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

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

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

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

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

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
2.2.3配置文件

application.properties配置文件:

spring.main.allow‐bean‐definition‐overriding = true

mybatis.configuration.map-underscore-to-camel-case = true

#m1数据源
spring.shardingsphere.datasource.names = m1

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://192.168.142.110:3306/shardingjdbc_db?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = 123456
# 指定t_order表的数据分布情况,配置数据节点 m1.t_order_1,m1.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

(1)首先定义数据源m1,并对m1进行实际的参数配置。

(2)指定t_order表的数据分布情况,他分布在m1.t_order_1,m1.t_order_2

(3)指定t_order表的主键生成策略为SNOWFLAKE,SNOWFLAKE是一种分布式自增算法,保证id全局唯一

(4)定义t_order分片策略,order_id为偶数的数据落在t_order_1,为奇数的落在t_order_2,分表策略的表达式为t_order_$->{order_id % 2 + 1}

2.2.4主启动类
package com.hexin;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableAutoConfiguration(exclude={DruidDataSourceAutoConfigure.class})
@SpringBootApplication
public class ShardingJDBCApplication {
    public static void main(String[] args) {
        SpringApplication.run(ShardingJDBCApplication.class, args);
    }
}

注意:

​ 需要加上@EnableAutoConfiguration(exclude={DruidDataSourceAutoConfigure.class})。

Spring Boot会自动根据你jar包的依赖来自动配置项目,因为我们导入了druid的jar,Spring Boot会创建默认的内存数据库的数据源DataSource。这时候需要用exclude={DataSourceAutoConfiguration.class}, 让spring-boot不要根据Maven中依赖自动配置了。

2.2.5dao
package com.hexin.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 {
    /**
     * 新增订单 * @param price 订单价格
     * @param userId 用户id
     * @param status 订单状态 * @return
     */
    @Insert("insert into t_order(price,user_id,status) value(#{price},#{userId},#{status})")
    int insertOrder(@Param("price") BigDecimal price, @Param("userId")Long userId, @Param("status")String status);

    /**
     * 根据id列表查询多个订单
     * @param orderIds 订单id列表
     * @return
     */
    @Select({"<script>"
            + "select  *  from t_order t"
            + " where t.order_id in "
            + "<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>"
            + " #{id} "
            + "</foreach>"
            + "</script>"})
    List<Map> selectOrderbyIds(@Param("orderIds") List<Long> orderIds);

}

2.2.5测试
package com.hexin;

import com.hexin.dao.OrderDao;
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 = {ShardingJDBCApplication.class})
public class OrderTest {

    @Autowired
    private OrderDao orderDao;

    @Test
    public void testInsertOrder(){
        for (int i = 0 ; i<10; i++){
            orderDao.insertOrder(new BigDecimal((i+1)*5),1L,"WAIT_PAY");
        }
    }

    @Test
    public void testSelectOrderbyIds(){
        List<Long> ids = new ArrayList<>();
        ids.add(373771636085620736L);
        ids.add(373771635804602369L);
        List<Map> maps = orderDao.selectOrderbyIds(ids);
        System.out.println(maps);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jyfTtX5-1612013347878)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20210130211114086.png)]

通过日志可以发现order_id为奇数的被插入到t_order_2表,为偶数的被插入到t_order_1表,达到预期目标。

3.小结

​ 通过上面的案例,我们看一下Sharding-JDBC在拿到用户要执行的sql之后干了哪些事儿:

(1)解析sql,获取片键值,在本例中是order_id

(2)Sharding-JDBC通过规则配置 t_order_$->{order_id % 2 + 1},知道了当order_id为偶数时,应该往

t_order_1表插数据,为奇数时,往t_order_2插数据。

(3)于是Sharding-JDBC根据order_id的值改写sql语句,改写后的SQL语句是真实所要执行的SQL语句。

(4)执行改写后的真实sql语句

(5)将所有真正执行sql的结果进行汇总合并,返回。

以上就是sharding-jdbc的入门案例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值