Sharding-JDBC的使用

摘要:
例如,根据性别,男性分为数据库A,女性分为数据库B。总结:纵向划分是将一个完整的表或数据库的结构拆分,横向划分是将数据行分配到不同的数据表或数据库;通常,垂直分区是在数据库设计之初设计的,例如:专用于数据库传输、将大表拆分为小表等。

一、背景

公司领导安排研究一下Sharding-JDBC,用于项目的分表分库,因此有了这一篇文章,接下来分享ShardingSphere的整个学习步骤,有不正确的请指出。

二、工作准备

技术架构

SpringBoot2.2.4 + shardingsphere4.0.0-RC1 + Maven3.5.4  + MySQL8.0.11 + lombok(插件)

该项目是基于水平分表和分库

扩展:

  • 垂直分表:按照字段使用的频率将一个完整的表按照字段分成多个表;

如:标题、内容等字段使用的频率较多,把表的结构拆分到不同的表中

  • 垂直分库:专库专用,把不同类型的表分到不同的数据库;

如:系统模块涉及到的表分一个数据库,订单模块涉及到的表分到一个数据库

  • 水平分表:按一定的规则将同一个表的数据按一定规则拆到不同的表中 ;

如:按照年纪为偶数的人分到A表,奇数的人分到B表

  • 水平分库:同一个表的数据按一定规则拆到不同的数据库中 。

如:按照性别男的分到A数据库,女的分到B数据库

小结:

垂直分是将一个完整的表或者库进行结构的拆分,水平分是将数据行分发到不同的数据表或库中;

通常垂直分是在数据库设计之初就进行设计了,如:转库专用,大表(字段较多的表)拆成小表等。

三、环境搭建

1、创建项目

使用IDEA创建简单的Spring Boot的项目,选择如下骨架:

Sharding-JDBC的使用第1张

2、创建数据库表

CREATE TABLE `tab_user0`  (
  `id` bigint(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `sex` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  `status` tinyint(1) NULL DEFAULT NULL COMMENT '是否删除0未删除1删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;


CREATE TABLE `tab_user1`  (
  `id` bigint(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `sex` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  `status` tinyint(1) NULL DEFAULT NULL COMMENT '是否删除0未删除1删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

3、添加相关类

1、controller

package com.dyaqi.shardingsphere.controller;

import com.dyaqi.shardingsphere.entity.User;
import com.dyaqi.shardingsphere.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author Dyaqi
 * @date 2021/1/8 9:48
 * @功能描述: 接口测试
 */
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 批量保存用户
     * @return
     */
    @PostMapping("save-user")
    public Object saveUser(@RequestBody List<User> userList) {
        return userService.saveUser(userList);
    }

    /**
     * 获取用户列表
     * @return
     */
    @GetMapping("list-user")
    public Object getUserList() {
        return userService.getUserList();
    }

    /**
     * 获取用户
     * @return
     */
    @GetMapping("byid-user")
    public Object getUserById(Long id) {
        return userService.getUserById(id);
    }

    /**
     * 删除用户
     */
    @DeleteMapping
    public String delUser(){
        return userService.delUser();
    }
}

2、service

package com.dyaqi.shardingsphere.service;

import com.dyaqi.shardingsphere.entity.User;

import java.util.List;

/**
 * @author Dyaqi
 * @date 2021/1/8 9:49
 * @功能描述:
 */
public interface UserService {
    Object saveUser(List<User> userList);

    Object getUserList();

    Object getUserById(Long id);

    String delUser();
}
package com.dyaqi.shardingsphere.service.impl;

import com.dyaqi.shardingsphere.entity.User;
import com.dyaqi.shardingsphere.mapper.UserMapper;
import com.dyaqi.shardingsphere.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @author Dyaqi
 * @date 2021/1/8 9:49
 * @功能描述:
 */
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public Object saveUser(List<User> userList) {
        for (User user : userList) {
            user.setCreateTime(new Date());
            user.setUpdateTime(new Date());
            user.setStatus(0);
            userMapper.saveUser(user);
        }
        return "保存成功!";
    }

    @Override
    public Object getUserList() {
        return userMapper.getUserList();
    }

    @Override
    public Object getUserById(Long id) {
        return userMapper.getUserById(id);
    }

    @Override
    public String delUser() {
        userMapper.delUser();
        return "OJBK";
    }
}

3、mapper

package com.dyaqi.shardingsphere.mapper;

import com.dyaqi.shardingsphere.entity.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * @author Dyaqi
 * @date 2021/1/8 9:49
 * @功能描述:
 */
@Mapper
public interface UserMapper {

    void saveUser(User user);

    List<User> getUserList();

    User getUserById(Long id);

    void delUser();
}
<?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.dyaqi.shardingsphere.mapper.UserMapper">
    <resultMap   type="com.dyaqi.shardingsphere.entity.User">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="sex" jdbcType="INTEGER" property="sex"/>
        <result column="sexStr" jdbcType="VARCHAR" property="sexStr"/>
        <result column="age" jdbcType="INTEGER" property="age"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
        <result column="status" jdbcType="INTEGER" property="status"/>
    </resultMap>
    <sql id="Base_Column_List">
        u.id,
        u.name,
        u.sex,
        u.age,
        u.create_time,
        u.update_time,
        u.status
    </sql>

    <insert   parameterType="com.dyaqi.shardingsphere.entity.User">
        insert into tab_user (
        name,
        sex,
        age,
        create_time,
        update_time,
        status)
        values
        (
        #{name},
        #{sex},
        #{age},
        #{createTime},
        #{updateTime},
        #{status})
    </insert>

    <select   resultMap="BaseResultMap" parameterType="java.lang.Integer">
        select
        <include refid="Base_Column_List"/>
        from tab_user u order by u.id
    </select>

    <select   resultMap="BaseResultMap" parameterType="java.lang.Long">
        select
        <include refid="Base_Column_List"/>,
        d.label AS 'sexStr'
        from tab_user u
        left join sys_dict d on u.sex = d.value AND d.type = 'sex'
        where u.id = #{id,jdbcType=BIGINT}
    </select>

    <delete id="delUser">
        delete from tab_user
    </delete>
</mapper>

4、user

package com.dyaqi.shardingsphere.entity;

import lombok.Data;

import java.util.Date;

/**
 * @author Dyaqi
 * @date 2021/1/8 9:50
 * @功能描述: 用户表
 */
@Data
public class User {
    /**
     * 主键
     */
    private Long id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 性别
     */
    private Integer sex;

    /**
     * 性别
     */
    private String sexStr;

    /**
     * 年龄
     */
    private Integer age;

    /**
     *
     */
    private Date createTime;

    /**
     *
     */
    private Date updateTime;

    /**
     *
     */
    private Integer status;

    public User() {
    }

    public User(Long id, String name, Integer sex, Integer age) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
}

三、实现分表功能

1、目的

要实现的是tab_user的分表,将tab_user中id为偶数的分发到tab_user0中,奇数的分发到tab_user1中。

2、配置pom文件

添加关于sharding-jdbc的坐标

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

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-namespace</artifactId>
    <version>4.0.0-RC1</version>
</dependency>

3、配置properties文件

添加数据库信息

server.port=80

#指定mybatis信息
mybatis.config-location=classpath:mybatis-config.xml

spring.shardingsphere.datasource.names=ds0

# 配置数据源
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/demo0?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456

#配置数据节点
spring.shardingsphere.sharding.tables.tab_user.actual-data-nodes=ds0.tab_user$->{0..1}

#指定主键
spring.shardingsphere.sharding.tables.tab_user.table-strategy.inline.sharding-column=id
#配置分表规则:主键除以2取模
spring.shardingsphere.sharding.tables.tab_user.table-strategy.inline.algorithm-expression=tab_user$->{id % 2}

#打印sql
spring.shardingsphere.props.sql.show=true
#Bean覆盖被允许
spring.main.allow-bean-definition-overriding=true

注:

其中配置中的tab_user即为要分的表,此处也可修改为abc,但在编写SQL时,操作的表名就要变为abc

分表规则解释:当id为偶数时,id % 2 表达式的结果为0,则表达式结果就会取代$,得到结果表示为tab_user0,此时指定的为tab_user0表。

4、配置mybatis-config文件

指定mybatis的关系

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.dyaqi.shardingsphere"/>
    </typeAliases>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

5、测试分析

调用新增接口(http://localhost/save-user ) 发送如下数据,则数据被分发到两张表中,水平分表实现。

[
	{
		"id": 1,
		"name": "小小",
		"sex": 0,
		"age": 14
	},
	{
		"id": 2,
		"name": "爸爸",
		"sex": "男",
		"age": 45
	},
	{
		"id": 3,
		"name": "妈妈",
		"sex": 0,
		"age": 43
	},
	{
		"id": 4,
		"name": "爷爷",
		"sex": 0,
		"age": 61
	},
	{
		"id": 5,
		"name": "奶奶",
		"sex": 0,
		"age": 62
	}
]

2

3

四、实现分库分表

1、目的

要实现将女性成员发送到demo0库,男性成员发送到demo1库,同时将tab_user中id为偶数的分发到tab_user0中,奇数的分发到tab_user1中。

2、配置properties文件

以下信息为和分表有不同的配置

# 配置真实数据源
spring.shardingsphere.datasource.names=ds0,ds1

# 配置数据源
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/demo0?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456

spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/demo1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456

#配置数据节点
spring.shardingsphere.sharding.tables.tab_user.actual-data-nodes=ds$->{0..1}.tab_user$->{0..1}

#根据性别分库
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=sex
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=ds$->{sex % 2}

#根据id分表
spring.shardingsphere.sharding.tables.tab_user.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.tab_user.table-strategy.inline.algorithm-expression=tab_user$->{id % 2}

注:

​ 数据节点:着重注意;

​ 分库时,和分表逻辑一致。

3、测试分析

测试的数据还用上边的;

按照目的,我们可猜测结果为:demo0库中的tab_user1表有三条记录小小、妈妈、奶奶,demo1库中tab_user0表有两条记录爸爸、爷爷。

4

5

五、加入分布式主键

1、目的

在实际操作中id是系统自动生成的并非前端传递的,因此要利用Sharding-Jdbc的配置去实现分布式主键生成。

2、配置properties文件

Sharding-Jdbc支持的分布式主键有两种,雪花算法(Long型)和UUID(String型)。

# 使用雪花算法作为默认id
spring.shardingsphere.sharding.tables.tab_user.key-generator.column=id
spring.shardingsphere.sharding.tables.tab_user.key-generator.type=SNOWFLAKE

3、测试分析

6

7

六、加入广播表和绑定表

1、目的

在真实的项目中,会有部分表数据量少,但是又经常会在关联查询中使用到,例如字典表。因此在demo0和demo1数据库中都存在sys_dict表,用于关联查询。

2、SQL

CREATE TABLE `sys_dict`  (
  `id` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '编号',
  `value` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '数据值',
  `label` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '标签名',
  `type` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '类型',
  `description` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '描述',
  `sort` decimal(10, 0) NOT NULL COMMENT '排序(升序)',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `sys_dict_value`(`value`) USING BTREE,
  INDEX `sys_dict_label`(`label`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '字典表' ROW_FORMAT = Dynamic;

INSERT INTO `sys_dict` VALUES ('1', '1', '男', 'sex', '性别', 1);
INSERT INTO `sys_dict` VALUES ('2', '0', '女', 'sex', '性别', 2);

结果为:

8

3、配置properties文件

#广播表, 其主节点是ds0
spring.shardingsphere.sharding.broadcast-tables=sys_dict

4、测试分析

在获取详情接口http://localhost/byid-user 中,存在关联查询,查询后结果如下:

9

七、不分表的设置

1、目的

在真实使用中,有一部分表不与拆分表有任何关系,没有必要将该表进行配置,经过配置后按照普通方式调用即可。

2、SQL

CREATE TABLE `region`  (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地理区域编码',
  `region_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地理区域名称',
  `level` tinyint(1) NULL DEFAULT NULL COMMENT '地理区域级别(省、市、县)',
  `parent_region_code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '上级地理区域编码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `region` VALUES (1, '110000', '北京', 0, NULL);
INSERT INTO `region` VALUES (2, '410000', '河南省', 0, NULL);
INSERT INTO `region` VALUES (3, '110100', '北京市', 1, '110000');
INSERT INTO `region` VALUES (4, '410100', '郑州市', 1, '410000');

结果为:

10

3、配置properties文件

# 设置默认的数据库,不分表的默认从该表查询
spring.shardingsphere.sharding.default-data-source-name=ds0

4、测试分析

在http://localhost/region 接口中,只查询region表,查询后结果如下:

11

八、ShardingSphere官网

本文章只介绍这么多,更多内容请见官网:https://shardingsphere.apache.org/document/current/cn/overview/ ,若需要项目中完整的代码请留言。

免责声明:文章转载自《Sharding-JDBC的使用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇如何用myeclipse和eclipse搭建安卓开发环境chrome卡的不完全解决办法(关闭GPU显卡硬件加速办法)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

sharding-jdbc

https://www.cnblogs.com/fengpinglangjingruma/p/14005759.html sharding-jdbc 提供了4种分片算法: 1、精确分片算法 精确分片算法(PreciseShardingAlgorithm)用于单个字段作为分片键,SQL中有 = 与 IN 等条件的分片,需要在标准分片策略(StandardSh...

Sharding-jdbc 读写分离(三)- springboot+mybatis+Hikari

概念 在上一篇文章介绍了如何使用Sharing-JDBC实现数据库的分表分库。这章节将介绍读写分离,读写分离的好处就是在并发量比较大的情况下,将查询数据库的压力 分担到多个从库中,能够满足高并发的要求。比如上一篇实现的那样,架构图如下: 代码 sharding.jdbc.datasource.names=db0,db1 # 数据源db0 sharding...

Sharding-Jdbc 3.1.0遇到的问题与处理

目录 读前请注意 提前configMapContext的注入便于shardingAlgorithm使用 莫名的NullPointerException 基于暗示(Hint)的路由,表级别的分片策略不生效,只走默认分片策略. sharding-jdbc不支持union语句,强制放行. addDatabaseShardingValue需要填写表名,我不想填...

Sharding-jdbc 分库分表专题内容(二)springboot+mybatis+Hikari

一、单库分表 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:schemaL...