Sharding-jdbc使用(一:水平分表)

说明:Sharding-jdbc是常见的分库分表工具,本文介绍Sharding-jdbc的基础使用。

分库分表

首先,介绍一下分库分表:

(1)分库

水平分库:以字段为依据,按照一定策略(hash、range),将一个库中的数据拆分到多个库中;

垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中;


(2)分表

水平分表:按照策略,将记录路由到不同的表上,常见的策略有范围、hash、字段值;

垂直分表:根据业务相关性,拆分表字段,将一张表拆分为多张表;


水平分就是横着一刀,垂直分就是竖着一刀。

垂直分库、垂直分表,我认为是设计时考虑的,像微服务架构,根据业务场景拆分多个小的服务,每个服务都可以有自己的数据库,是垂直分库。

而垂直分表,某张表的字段过长,可以考虑将表字段按照业务相关性拆分成多张表,另外,当表中有text类型的字段时,为了避免影响其他字段索引率,也需要独立出来一张表,用主键对应(阿里巴巴《Java开发手册》),是垂直分表的体现。

(3)需要考虑的问题

分库:

  • 跨库事务;

  • 跨库的JOIN;


分表:

  • 多张表如何保证主键不重复;

  • 多张表的count、order by、group by 及 聚合函数问题;

Sharding-jdbc使用

这里介绍使用Sharding-jdbc实现水平分表

(1)创建数据库表

先创建两张数据库表

sql 复制代码
# 创建数据库
create schema order_db collate utf8mb3_general_ci;

# 创建表
use order_db;

CREATE TABLE `t_order_1`
(
    `order_id` bigint                                                       NOT NULL COMMENT '订单id',
    `price`    decimal(10, 2)                                               NOT NULL COMMENT '订单价格',
    `user_id`  bigint                                                       NOT NULL COMMENT '下单用户id',
    `status`   varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '订单状态',
    PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb3
  ROW_FORMAT = DYNAMIC;

CREATE TABLE `t_order_2`
(
    `order_id` bigint                                                       NOT NULL COMMENT '订单id',
    `price`    decimal(10, 2)                                               NOT NULL COMMENT '订单价格',
    `user_id`  bigint                                                       NOT NULL COMMENT '下单用户id',
    `status`   varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '订单状态',
    PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb3
  ROW_FORMAT = DYNAMIC;

(2)创建项目

创建一个Spring Boot项目,pom如下:

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

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

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- shardingJDBC核心依赖 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
            <version>5.2.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>snakeyaml</artifactId>
                    <groupId>org.yaml</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>cosid-core</artifactId>
                    <groupId>me.ahoo.cosid</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- 版本冲突 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.33</version>
        </dependency>

        <dependency>
            <groupId>me.ahoo.cosid</groupId>
            <artifactId>cosid-core</artifactId>
            <version>1.19.3</version>
        </dependency>

        <!--XA 分布式事务 -->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-transaction-xa-core</artifactId>
            <version>5.2.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>transactions-jdbc</artifactId>
                    <groupId>com.atomikos</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>transactions-jta</artifactId>
                    <groupId>com.atomikos</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- SpringBoot依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <artifactId>snakeyaml</artifactId>
                    <groupId>org.yaml</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--测试类依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--durid数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.20</version>
        </dependency>

        <!-- mysql连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- mybatisplus依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.3</version>
        </dependency>
    </dependencies>
</project>

(3)编写代码

创建pojo对象,注意表名是t_order,没有加后缀

java 复制代码
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_order")
public class Order implements Serializable {

    @TableId
    private Long orderId;

    private BigDecimal price;

    private Long userId;

    private String status;
}

创建Service和Mapper

(OrderService)

java 复制代码
import com.baomidou.mybatisplus.extension.service.IService;
import com.hezy.pojo.Order;

public interface OrderService extends IService<Order> {
}

(OrderServiceImpl)

java 复制代码
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hezy.mapper.OrderMapper;
import com.hezy.pojo.Order;
import com.hezy.service.OrderService;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
}

(OrderMapper)

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hezy.pojo.Order;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}

启动类

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

(4)配置文件

配置文件中,定义了数据库配置及水平分表的策略,采用取模(%2)的方式

yml 复制代码
spring:
  main:
    # 允许Bean重复定义覆盖
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      # 数据源名称,多个数据源时使用逗号(,)分割
      names: m1
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/order_db?serverTimezone=Asia/Shanghai&characterEncoding=utf8
        username: root
        password: 123456
    sharding:
      tables:
        t_order:
          # 配置数据节点,m1.t_order_1,m1.t_order_2,使用上面names的配置名称
          actual-data-nodes: m1.t_order_$->{1..2}
          key-generator:
            # 配置主键,及主键生成算法
            column: order_id
            # 雪花算法
            type: SNOWFLAKE
          table-strategy:
            inline:
              # 配置分片键
              sharding-column: order_id
              # 配置分片策略( order_id % 2 + 1 的值就是数据实际要进入的数据表 )
              # 利用"取模"计算的方式进行分片,将分片键除以分片表的个数,得到的模就是该数据要进入的数据表
              # 示例中,共有2张分片表,则此处求取分片键的值与分片表的模数,表示为 order_id % 2,又因为分片表的初始值以 1 开始,则再加上1
              algorithm-expression: t_order_$->{order_id % 2 + 1}
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 配置数据库字段与实体类映射方式,是否是小驼峰命名法
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      # 配置数据表前缀
      table-prefix: t_

(5)测试

写一个测试类,如下:

java 复制代码
import com.hezy.pojo.Order;
import com.hezy.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

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

@SpringBootTest
public class OrderMapperTest {

    @Autowired
    private OrderService orderService;

    @Test
    public void insertTest() {
        for (int i = 0; i < 10; i++) {
            Order order = new Order();
            order.setStatus("正常");
            order.setPrice(new BigDecimal(20));
            order.setUserId(1L);
            boolean save = orderService.save(order);
            System.out.println(save);
        }
    }

    @Test
    public void findAllTest() {
        List<Order> list = orderService.list();
        System.out.println(list);
    }
}

执行插入方法,插入完成

可见数据分散插入到对应的表中

试一下查询,也是查出10条记录,而不是单张表的5条记录。

注意

如果是手写SQL,不用Mybatis-Plus的API,那么写SQL的时候注意不要带上表名后缀

java 复制代码
    @Select("select * from t_order")
    List<Order> findALl();

不要写成下面这样,这样就是查单张表的5条记录了。

java 复制代码
    @Select("select * from t_order_1")
    List<Order> findALl();

参考

代码:

博客:

相关推荐
磊叔的技术博客7 分钟前
LLM 系列(四):神奇的魔法数 27
后端·llm
星沁城12 分钟前
149. 直线上最多的点数
java·算法·leetcode
爱奥尼欧1 小时前
【C++语法】类和对象(4)——日期类和const成员函数
数据库·c++
前端付豪1 小时前
美团 Flink 实时路况计算平台全链路架构揭秘
前端·后端·架构
MikeWe1 小时前
理解深度学习框架计算图的动态图与静态图:机制、实现与应用
后端
Android洋芋1 小时前
从零到一构建企业级TTS工具:实战指南与优化策略
后端
chanalbert1 小时前
AI大模型提示词工程研究报告:长度与效果的辩证分析
前端·后端·ai编程
Android洋芋1 小时前
深度解析Android音频焦点处理与实战开发:从无声问题到企业级解决方案
后端
海风极客1 小时前
Go语言开发小技巧&易错点100例(十七)
后端·面试·github
海风极客1 小时前
Go语言开发小技巧&易错点100例(十六)
后端·面试·github