zoukankan      html  css  js  c++  java
  • Sharding-JDBC的使用

    一、背景

    公司领导安排研究一下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的项目,选择如下骨架:

    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 id="BaseResultMap" 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 id="saveUser" 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 id="getUserList" resultMap="BaseResultMap" parameterType="java.lang.Integer">
            select
            <include refid="Base_Column_List"/>
            from tab_user u order by u.id
        </select>
    
        <select id="getUserById" 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/ ,若需要项目中完整的代码请留言。

  • 相关阅读:
    js实现全选反选功能
    seajs笔记
    延迟加载图片
    JavaScript 数组基本操作
    图片上传,头像上传
    命令模式
    动态加载js css 插件
    发布-订阅模式
    js 切换全屏
    JavaScript 经典之一 闭包
  • 原文地址:https://www.cnblogs.com/dyaqi/p/14580197.html
Copyright © 2011-2022 走看看