精华内容
下载资源
问答
  • ShardingJdbc

    2021-10-21 15:07:00
    文章目录 ShardingShpere数据库中间件 一、ShardingJdbc 1.1、概述 1.2、关于改名问题 1.3、认识shardingjdbc 1.4、认识shardingjdbc功能架构图 1.5、认识Sharding-Proxy 1.6、三个组件的比较 1.7、ShardingJdbc混合...

    文章目录


    https://www.bilibili.com/video/BV1ei4y1K7dn

    ShardingShpere数据库中间件

    Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

    一、ShardingJdbc

    1.1、概述

    官网:http://shardingsphere.apache.org/index_zh.html
    下载地址:https://shardingsphere.apache.org/document/current/cn/downloads/
    快速入门:https://shardingsphere.apache.org/document/current/cn/quick-start/shardingsphere-jdbc-quick-start/

    在这里插入图片描述

    以下来自官网的原话:

    Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款既能够独立部署,又支持混合部署配合使用的产品组成。 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。

    Apache ShardingSphere 旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 关系型数据库当今依然占有巨大市场份额,是企业核心系统的基石,未来也难于撼动,我们更加注重在原有基础上提供增量,而非颠覆。

    Apache ShardingSphere 5.x 版本开始致力于可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。 目前,数据分片、读写分离、数据加密、影子库压测等功能,以及 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目。 开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个 SPI 作为系统的扩展点,仍在不断增加中。

    ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目。

    1.2、关于改名问题

    在3.0以后就更改成了ShardingSphere。

    1.3、认识shardingjdbc

    在这里插入图片描述

    定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

    适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。

    支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。

    支持任意实现 JDBC 规范的数据库,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。

    1.4、认识shardingjdbc功能架构图

    在这里插入图片描述

    1.5、认识Sharding-Proxy

    在这里插入图片描述
    定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL 版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。

    • 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用。
    • 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端。

    1.6、三个组件的比较

    Sharding-JdbcSharding-ProxySharding-Sidecar
    数据库任意MYSQLMYSQL
    连接消耗数
    异构语言仅Java任意任意
    性能损耗低损耗高损耗低
    中心化
    静态入口

    1.7、ShardingJdbc混合架构

    在这里插入图片描述

    ShardingSphere-JDBC 采用无中心化架构,适用于 Java 开发的高性能的轻量级 OLTP(连接事务处理) 应用;ShardingSphere-Proxy 提供静态入口以及异构语言的支持,适用于 OLAP(连接数据分析) 应用以及对分片数据库进行管理和运维的场景。

    Apache ShardingSphere 是多接入端共同组成的生态圈。 通过混合使用 ShardingSphere-JDBC 和 ShardingSphere-Proxy,并采用同一注册中心统一配置分片策略,能够灵活的搭建适用于各种场景的应用系统,使得架构师更加自由地调整适合与当前业务的最佳系统架构。

    1.8、ShardingShpere的功能清单

    • 功能列表
      • 数据分片
      • 分库 & 分表
      • 读写分离
      • 分片策略定制化
      • 无中心化分布式主键
    • 分布式事务
      • 标准化事务接口
      • XA 强一致事务
      • 柔性事务
      • 数据库治理
    • 分布式治理
      • 弹性伸缩
      • 可视化链路追踪
      • 数据加密

    1.9、 ShardingSphere数据分片内核剖析

    ShardingSphere 的 3 个产品的数据分片主要流程是完全一致的。 核心由 SQL 解析 => 执行器优化 => SQL 路由 => SQL 改写 => SQL 执行 => 结果归并的流程组成。

    在这里插入图片描述

    SQL 解析

    分为词法解析和语法解析。 先通过词法解析器将 SQL 拆分为一个个不可再分的单词。再使用语法解析器对 SQL 进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。

    执行器优化

    合并和优化分片条件,如 OR 等。

    SQL 路由

    根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。

    SQL 改写

    将 SQL 改写为在真实数据库中可以正确执行的语句。SQL 改写分为正确性改写和优化改写。

    SQL 执行

    通过多线程执行器异步执行。

    结果归并

    将多个执行结果集归并以便于通过统一的 JDBC 接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。

    二、ShardingJdbc准备

    Linux安装MySQL5.7

    2.1、yum安装mysql

    1-1、下载mysql的rpm地址

    http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/

    在这里插入图片描述

    1-2、配置Mysql扩展源

    rpm -ivh http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql57-community-release-el7-10.noarch.rpm
    

    1-3、yum安装mysql

    yum install mysql-community-server -y
    

    1-4、启动Mysql,并加入开机自启

    systemctl start mysqld
    systemctl stop mysqld
    systemctl enable mysqld
    

    1-5、使用Mysq初始密码登录数据库

    在这里插入图片描述

    >grep "password" /var/log/mysqld.log
    > mysql -uroot -pma1S8xjuEA/F
    
    或者一步到位的做法如下
    >mysql -uroot -p$(awk '/temporary password/{print $NF}' /var/log/mysqld.log)
    

    1-6、修改数据库密码

    数据库默认密码规则必须携带大小写字母、特殊符号,字符长度大于8否则会报错。

    因此设定较为简单的密码时需要首先修改set global validate_password_policy和_length参数值。

    在这里插入图片描述

    mysql> set global validate_password_policy=0;
    Query OK, 0 rows affected (0.00 sec)
    mysql> set global validate_password_length=1;
    Query OK, 0 rows affected (0.00 sec)
    

    1-7、修改密码

    mysql> set password for root@localhost = password('mkxiaoer');
    Query OK, 0 rows affected, 1 warning (0.00 sec)
    
    或者
    mysql>ALTER USER 'root'@'localhost' IDENTIFIED BY 'new password';
    

    1-8、登录测试

    [root@http-server ~]# mysql -uroot -pmkxiaoer
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    4 rows in set (0.00 sec)
    mysql> exit
    

    1-9、可视化工具的登录授权:(如果授权不成功,请查看防火墙)

    操作完成上面的,现在还不能用可视化的客户端进行连接,需要我们进行授权:

    mysql > grant all on *.* to root@'%' identified by '数据库密码';
    mysql > flush privileges;
    

    操作完毕,接下来可以使用navicat或者sqlylog进行远程连接了.

    sqlylog的下载:https://sqlyog.en.softonic.com/

    三、MySql完成主从复制

    ShardingJdbc准备 - MySql完成主从复制

    概述

    主从复制(也称 AB 复制)允许将来自一个MySQL数据库服务器(主服务器)的数据复制到一个或多个MySQL数据库服务器(从服务器)。

    复制是异步的 从站不需要永久连接以接收来自主站的更新。

    根据配置,您可以复制数据库中的所有数据库,所选数据库甚至选定的表。

    3.1、MySQL中复制的优点包括:

    • 横向扩展解决方案 - 在多个从站之间分配负载以提高性能。在此环境中,所有写入和更新都必须在主服务器上进行。但是,读取可以在一个或多个从设备上进行。该模型可以提高写入性能(因为主设备专用于更新),同时显着提高了越来越多的从设备的读取速度。
    • 数据安全性 - 因为数据被复制到从站,并且从站可以暂停复制过程,所以可以在从站上运行备份服务而不会破坏相应的主数据。
    • 分析 - 可以在主服务器上创建实时数据,而信息分析可以在从服务器上进行,而不会影响主服务器的性能。
    • 远程数据分发 - 您可以使用复制为远程站点创建数据的本地副本,而无需永久访问主服务器。

    3.2、Replication 的原理

    在这里插入图片描述

    前提是作为主服务器角色的数据库服务器必须开启二进制日志

    主服务器上面的任何修改都会通过自己的 I/O tread(I/O 线程)保存在二进制日志 Binary log 里面。

    • 从服务器上面也启动一个 I/O thread,通过配置好的用户名和密码, 连接到主服务器上面请求读取二进制日志,然后把读取到的二进制日志写到本地的一个Realy log(中继日志)里面。
    • 从服务器上面同时开启一个 SQL thread 定时检查 Realy log(这个文件也是二进制的),如果发现有更新立即把更新的内容在本机的数据库上面执行一遍。
      每个从服务器都会收到主服务器二进制日志的全部内容的副本。
    • 从服务器设备负责决定应该执行二进制日志中的哪些语句。
      除非另行指定,否则主从二进制日志中的所有事件都在从站上执行。
      如果需要,您可以将从服务器配置为仅处理一些特定数据库或表的事件。

    3.3、具体配置如下

    3.3.1、 Master节点配置/etc/my.cnf (master节点执行)

    > vim /etc/my.cnf
    [mysqld]
    ## 同一局域网内注意要唯一
    server-id=100  
    ## 开启二进制日志功能,可以随便取(关键)
    log-bin=mysql-bin
    ## 复制过滤:不需要备份的数据库,不输出(mysql库一般不同步)
    binlog-ignore-db=mysql
    ## 为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存
    binlog_cache_size=1M
    ## 主从复制的格式(mixed,statement,row,默认格式是statement)
    binlog_format=mixed
    

    3.3.2、Slave节点配置/etc/my.cnf (slave节点执行)

    > vim /etc/my.cnf
    [mysqld]
    ## 设置server_id,注意要唯一
    server-id=102
    ## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
    log-bin=mysql-slave-bin
    ## relay_log配置中继日志
    relay_log=edu-mysql-relay-bin
    ##复制过滤:不需要备份的数据库,不输出(mysql库一般不同步)
    binlog-ignore-db=mysql
    ## 如果需要同步函数或者存储过程
    log_bin_trust_function_creators=true
    ## 为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存
    binlog_cache_size=1M
    ## 主从复制的格式(mixed,statement,row,默认格式是statement)
    binlog_format=mixed
    ## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
    ## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
    slave_skip_errors=1062
    

    03-03、在master服务器授权slave服务器可以同步权限(master节点执行)

    注意:在master服务器上执行

    mysql > mysql -uroot -pmaster的密码
    # 授予slave服务器可以同步master服务
    mysql > grant replication slave, replication client on *.* to 'root'@'slave服务的ip' identified by 'slave服务器的密码';
    mysql > flush privileges;
    # 查看MySQL现在有哪些用户及对应的IP权限(可以不执行,只是一个查看)
    mysql > select user,host from mysql.user;
    

    在这里插入图片描述

    3.3.4、查询master服务的binlog文件名和位置(master节点执行)

    mysql > show master status;
    

    在这里插入图片描述

    • 日志文件名:mysql-bin.000002
    • 复制的位置:2079

    3.3.5、slave进行关联master节点(slave节点执行)

    • 进入到slave节点:

      mysql > mysql -uroot -p你slave的密码
      
    • 开始绑定

      mysql> change master to master_host='master服务器ip', master_user='root', master_password='master密码', master_port=3306, master_log_file='mysql-bin.000002',master_log_pos=2079;
      

    这里注意一下 master_log_file 和 master_log_pos 都是通过 master服务器通过show master status获得。

    3.3.6、在slave节点上查看主从同步状态(slave节点执行)

    启动主从复制

    mysql> start slave;
    Query OK, 0 rows affected (0.00 sec)
    

    再查看主从同步状态

    mysql> show slave status\G;
    

    在这里插入图片描述

    其他命令 (slave节点执行)

    # 停止复制mysql> stop slave;
    

    3.3.7、主从复制测试

    1:在master下创建数据库和表,或者修改和新增,删除记录都会进行同步(master节点执行)
    2:点击查看slave节点信息(slave节点执行)

    3.3.8、切记

    在主从复制操作的时候,不要基于去创建数据库或者相关操作。然后又去删除。这样会造成主从复制的pos改变,而造成复制失败,如果出现此类问题,查看04-03的常见问题排查。

    3.4、主从复制相关问题排查

    3.4.1、主从复制Connecting问题

    在这里插入图片描述

    使用start slave开启主从复制过程后,如果SlaveIORunning一直是Connecting,则说明主从复制一直处于连接状态,这种情况一般是下面几种原因造成的,我们可以根据 Last_IO_Error提示予以排除。

    1. 网络不通
      检查ip,端口
    2. 密码不对
      检查是否创建用于同步的用户和用户密码是否正确
    3. pos不对
      检查Master的 Position

    3.4.2、MYSQL镜像服务器因错误停止的恢复 —Slave_SQL_Running: No

    先stop slave,然后执行了一下提示的语句,再
     > stop slave;
     > set global sql_slave_skip_counter=1;
     > start slave; 
     > show slave status\G ;
    

    3.4.3、从MYSQL服务器Slave_IO_Running: No的解决2

    • master节点执行,获取日志文件和post
    mysql > show master status;
    
    • slave节点进行重新绑定
    mysql > stop slave;
    mysql > CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000008', MASTER_LOG_POS=519086591;
    mysql > start slave;
    

    造成这类问题的原因一般是在主从复制的时候,基于创建表,然后又去删除和操作了数据表或者表。

    四、ShardingJdbc的配置及读写分离

    4.1、内容大纲

    • 新建一个springboot工程
    • 引入相关sharding依赖、ssm依赖、数据库驱动
    • 定义配置application.yml
    • 定义entity、mapper、controller
    • 访问测试查看效果
    • 小结

    4.2、具体实现步骤

    4.2.1、 新建一个springboot工程

    在这里插入图片描述

    4.2.2、 引入相关sharding依赖、ssm依赖、数据库驱动

    <properties>
        <java.version>1.8</java.version>
        <sharding-sphere.version>4.0.0-RC1</sharding-sphere.version>
    </properties>
    <dependencies>
        <!-- 依赖web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 依赖mybatis和mysql驱动 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--依赖lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--依赖sharding-->
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>${sharding-sphere.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-core-common</artifactId>
            <version>${sharding-sphere.version}</version>
        </dependency>
        <!--依赖数据源druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.22</version>
        </dependency>
    </dependencies>
    

    4.2.3、 定义配置application.yml

    server:
      port: 8085
    spring:
      main:
        allow-bean-definition-overriding: true
      shardingsphere:
        # 参数配置,显示sql
        props:
          sql:
            show: true
        # 配置数据源
        datasource:
          # 给每个数据源取别名,下面的ds1,ds2,ds3任意取名字
          names: ds1,ds2,ds3
          # 给master-ds1每个数据源配置数据库连接信息
          ds1:
            # 配置druid数据源
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://47.115.94.78:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
          # 配置ds2-slave
          ds2:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://114.215.145.201:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
          # 配置ds3-slave
          ds3:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://114.215.145.201:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
        # 配置默认数据源ds1
        sharding:
         # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
          default-data-source-name: ds1
        # 配置数据源的读写分离,但是数据库一定要做主从复制
        masterslave:
          # 配置主从名称,可以任意取名字
          name: ms
          # 配置主库master,负责数据的写入
          master-data-source-name: ds1
          # 配置从库slave节点
          slave-data-source-names: ds2,ds3
          # 配置slave节点的负载均衡均衡策略,采用轮询机制
          load-balance-algorithm-type: round_robin
    # 整合mybatis的配置XXXXX
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.xuexiangban.shardingjdbc.entity
    

    注意问题:

    # 配置默认数据源ds1
     sharding:
       # 默认数据源,主要用于写,注意一定要配置读写分离
       # 注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
       default-data-source-name: ds1
     # 配置数据源的读写分离,但是数据库一定要做主从复制
     masterslave:
       # 配置主从名称,可以任意取名字
       name: ms
       # 配置主库master,负责数据的写入
       master-data-source-name: ds1
       # 配置从库slave节点
       slave-data-source-names: ds2,ds3
       # 配置slave节点的负载均衡均衡策略,采用轮询机制
       load-balance-algorithm-type: round_robin
    

    如果上面的,那么shardingjdbc会采用随机的方式进行选择数据源。如果不配置default-data-source-name,那么就会把三个节点都当做从slave节点,那么新增,修改和删除会出错。

    4.2.4、 定义mapper、controller,entity

    entity

    @Data
    public class User {
        // 主键
        private Integer id;
        // 昵称
        private String nickname;
        // 密码
        private String password;
        // 性
        private Integer sex;
        // 性
        private String birthday;
    }
    

    mapper

    public interface UserMapper {
        
        @Insert("insert into ksd_user(nickname,password,sex,birthday) values(#{nickname},#{password},#{sex},#{birthday})")
        void addUser(User user);
    
        @Select("select * from ksd_user")
        List<User> findUsers();
    }
    

    controller

    @RestController
    @RequestMapping("/user")
    public class UserController {
        
        @Autowired
        private UserMapper userMapper;
        
        @GetMapping("/save")
        public String insert() {
            User user = new User();
            user.setNickname("zhangsan"+ new Random().nextInt());
            user.setPassword("1234567");
            user.setSex(1);
            user.setBirthday("1988-12-03");
            userMapper.addUser(user);
            return "success";
        }
        
        @GetMapping("/listuser")
        public List<User> listuser() {
            return userMapper.findUsers();
        }
    }
    

    4.2.5、 访问测试查看效果

    1:访问 http://localhost:8085/user/save 一直进入到ds1主节点

    2:访问 http://localhost:8085/user/listuser 一直进入到ds2、ds3节点,并且轮询进入。

    4.2.6、 日志查看

    img

    img

    img

    4.2.7、 小结

    回顾流程

    ShardingSphere 的 3 个产品的数据分片主要流程是完全一致的。 核心由 SQL 解析 => 执行器优化 => SQL 路由 => SQL 改写 => SQL 执行 => 结果归并的流程组成。

    img

    SQL 解析 :分为词法解析和语法解析。 先通过词法解析器将 SQL 拆分为一个个不可再分的单词。再使用语法解析器对 SQL 进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。

    执行器优化:合并和优化分片条件,如 OR 等。

    SQL 路由:根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。

    SQL 改写:将 SQL 改写为在真实数据库中可以正确执行的语句。SQL 改写分为正确性改写和优化改写。

    SQL 执行:通过多线程执行器异步执行。

    结果归并:将多个执行结果集归并以便于通过统一的 JDBC 接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。

    3.3、Props的其他相关配置

    acceptor.size: # accept连接的线程数量,默认为cpu核数2倍
    executor.size: #工作线程数量最大,默认值: 无限制
    max.connections.size.per.query: # 每个查询可以打开的最大连接数量,默认为1
    check.table.metadata.enabled: #是否在启动时检查分表元数据一致性,默认值: false
    proxy.frontend.flush.threshold: # proxy的服务时候,对于单个大查询,每多少个网络包返回一次
    proxy.transaction.type: # 默认LOCAL,proxy的事务模型 允许LOCAL,XA,BASE三个值,LOCAL无分布式事务,XA则是采用atomikos实现的分布式事务 BASE目前尚未实现
    proxy.opentracing.enabled: # 是否启用opentracing
    proxy.backend.use.nio: # 是否采用netty的NIO机制连接后端数据库,默认False ,使用epoll机制
    proxy.backend.max.connections: # 使用NIO而非epoll的话,proxy后台连接每个netty客户端允许的最大连接数量(注意不是数据库连接限制) 默认为8
    proxy.backend.connection.timeout.seconds: #使用nio而非epoll的话,proxy后台连接的超时时间,默认60s
    

    五、MySQL分库分表原理

    5.1、为什么要分库分表

    一般的机器(4核16G),单库的MySQL并发(QPS+TPS)超过了2k,系统基本就完蛋了。最好是并发量控制在1k左右。这里就引出一个问题,为什么要分库分表?

    分库分表目的:解决高并发,和数据量大的问题。

    1、高并发情况下,会造成IO读写频繁,自然就会造成读写缓慢,甚至是宕机。一般单库不要超过2k并发,NB的机器除外。

    2、数据量大的问题。主要由于底层索引实现导致,MySQL的索引实现为B+TREE,数据量其他,会导致索引树十分庞大,造成查询缓慢。第二,innodb的最大存储限制64TB。

    要解决上述问题。最常见做法,就是分库分表。
    分库分表的目的,是将一个表拆成N个表,就是让每个表的数据量控制在一定范围内,保证SQL的性能。 一个表数据建议不要超过500W。

    在这里插入图片描述

    5.2、分库分表

    又分为垂直拆分和水平拆分。

    **水平拆分:**统一个表的数据拆到不同的库不同的表中。可以根据时间、地区、或某个业务键维度,也可以通过hash进行拆分,最后通过路由访问到具体的数据。拆分后的每个表结构保持一致。

    **垂直拆分:**就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,可以根据业务维度进行拆分,如订单表可以拆分为订单、订单支持、订单地址、订单商品、订单扩展等表;也可以,根据数据冷热程度拆分,20%的热点字段拆到一个表,80%的冷字段拆到另外一个表。

    在这里插入图片描述

    5.3、不停机分库分表数据迁移

    一般数据库的拆分也是有一个过程的,一开始是单表,后面慢慢拆成多表。那么我们就看下如何平滑的从MySQL单表过度到MySQL的分库分表架构。

    1、利用mysql+canal做增量数据同步,利用分库分表中间件,将数据路由到对应的新表中。

    2、利用分库分表中间件,全量数据导入到对应的新表中。

    3、通过单表数据和分库分表数据两两比较,更新不匹配的数据到新表中。

    4、数据稳定后,将单表的配置切换到分库分表配置上。

    在这里插入图片描述

    5.4、小结

    垂直拆分:业务模块拆分、商品库,用户库,订单库

    水平拆分:对表进行水平拆分(也就是我们说的:分表)

    表进行垂直拆分:表的字段过多,字段使用的频率不一。(可以拆分两个表建立1:1关系)

    六、ShardingJdbc的分库和分表

    6.1、分库分表的方式

    **水平拆分:**统一个表的数据拆到不同的库不同的表中。可以根据时间、地区、或某个业务键维度,也可以通过hash进行拆分,最后通过路由访问到具体的数据。拆分后的每个表结构保持一致。

    **垂直拆分:**就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,可以根据业务维度进行拆分,如订单表可以拆分为订单、订单支持、订单地址、订单商品、订单扩展等表;也可以,根据数据冷热程度拆分,20%的热点字段拆到一个表,80%的冷字段拆到另外一个表。

    6.2、逻辑表

    逻辑表是指:水平拆分的数据库或者数据表的相同路基和数据结构表的总称。比如用户数据根据用户id%2拆分为2个表,分别是:ksd_user0和ksd_user1。他们的逻辑表名是:ksd_user。

    在shardingjdbc中的定义方式如下:

    spring:
      shardingsphere:
        sharding:
          tables:
            # ksd_user 逻辑表名
            ksd_user:
    

    6.3、分库分表数据节点 - actual-data-nodes

     tables:
            # ksd_user 逻辑表名
            ksd_user:
              # 数据节点:多数据源$->{0..N}.逻辑表名$->{0..N} 相同表
              actual-data-nodes: ds$->{0..2}.ksd_user$->{0..1}
              # 数据节点:多数据源$->{0..N}.逻辑表名$->{0..N} 不同表
              actual-data-nodes: ds0.ksd_user$->{0..1},ds1.ksd_user$->{2..4}
              # 指定单数据源的配置方式
              actual-data-nodes: ds0.ksd_user$->{0..4}
              # 全部手动指定
              actual-data-nodes: ds0.ksd_user0,ds1.ksd_user0,ds0.ksd_user1,ds1.ksd_user1,
    

    数据分片是最小单元。由数据源名称和数据表组成,比如:ds0.ksd_user0。

    寻找规则如下:

    在这里插入图片描述

    6.4、分库分表5种分片策略

    在这里插入图片描述

    数据源分片分为两种:

    • 数据源分片
    • 表分片

    这两个是不同维度的分片规则,但是它们额能用的分片策略和规则是一样的。它们由两部分构成:

    • 分片键
    • 分片算法

    第一种:none

    对应NoneShardingStragey,不分片策略,SQL会被发给所有节点去执行,这个规则没有子项目可以配置。

    第二种:inline 行表达时分片策略(核心,必须要掌握)

    对应InlineShardingStragey。使用Groovy的表达时,提供对SQL语句种的=和in的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开放,如:ksd_user${分片键(数据表字段)userid % 5} 表示ksd_user表根据某字段(userid)模 5.从而分为5张表,表名称为:ksd_user0到ksd_user4 。如果库也是如此。

    server:
      port: 8085
    spring:
      main:
        allow-bean-definition-overriding: true
      shardingsphere:
        # 参数配置,显示sql
        props:
          sql:
            show: true
        sharding:
          # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
          default-data-source-name: ds0
          # 配置分表的规则
          tables:
            # ksd_user 逻辑表名
            ksd_user:
              # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
              actual-data-nodes: ds$->{0..1}.ksd_user$->{0..1}
              # 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
              database-strategy:
                inline:
                  sharding-column: sex    # 分片字段(分片键)
                  algorithm-expression: ds$->{sex % 2} # 分片算法表达式
              # 拆分表策略,也就是什么样子的数据放入放到哪个数据表中。
              table-strategy:
                inline:
                  sharding-column: age    # 分片字段(分片键)
                  algorithm-expression: ksd_user$->{age % 2} # 分片算法表达式
    

    algorithm-expression行表达式:

    • ${begin…end} 表示区间范围。
    • ${[unit1,unit2,….,unitn]} 表示枚举值。
    • 行表达式种如果出现连续多个 e x p r e s s s i o n 或 {expresssion}或 expresssion->{expression}表达式,整个表达时最终的结果将会根据每个子表达式的结果进行笛卡尔组合。

    在这里插入图片描述

    完整案例和配置如下

    • 准备两个数据库ksd_sharding-db。名字相同,两个数据源ds0和ds1
    • 每个数据库下方ksd_user0和ksd_user1即可。
    • 数据库规则,性别为偶数的放入ds0库,奇数的放入ds1库。
    • 数据表规则:年龄为偶数的放入ksd_user0库,奇数的放入ksd_user1库。
    server:
      port: 8085
    spring:
      main:
        allow-bean-definition-overriding: true
      shardingsphere:
        # 参数配置,显示sql
        props:
          sql:
            show: true
        # 配置数据源
        datasource:
          # 给每个数据源取别名,下面的ds1,ds1任意取名字
          names: ds0,ds1
          # 给master-ds1每个数据源配置数据库连接信息
          ds0:
            # 配置druid数据源
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://47.115.94.78:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
          # 配置ds1-slave
          ds1:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://114.215.145.201:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
        # 配置默认数据源ds0
        sharding:
          # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
          default-data-source-name: ds0
          # 配置分表的规则
          tables:
            # ksd_user 逻辑表名
            ksd_user:
              # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
              actual-data-nodes: ds$->{0..1}.ksd_user$->{0..1}
              # 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
              database-strategy:
                inline:
                  sharding-column: sex    # 分片字段(分片键)
                  algorithm-expression: ds$->{sex % 2} # 分片算法表达式
              # 拆分表策略,也就是什么样子的数据放入放到哪个数据表中。
              table-strategy:
                inline:
                  sharding-column: age    # 分片字段(分片键)
                  algorithm-expression: ksd_user$->{age % 2} # 分片算法表达式
    # 整合mybatis的配置XXXXX
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.xuexiangban.shardingjdbc.entity
    

    结果如下图:

    在这里插入图片描述

    第三种:根据实时间日期 - 按照标准规则分库分表

    标准分片 - Standard(了解)

    • 对应StrandardShardingStrategy.提供对SQL语句中的=,in和恶between and 的分片操作支持。
    • StrandardShardingStrategy只支持但分片键。提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。
    • PreciseShardingAlgorithm是必选的呃,用于处理=和IN的分片
    • 和RangeShardingAlgorithm是可选的,是用于处理Betwwen and分片,如果不配置和RangeShardingAlgorithm,SQL的Between AND 将按照全库路由处理。

    定义分片的日期规则配置

    server:
      port: 8085
    spring:
      main:
        allow-bean-definition-overriding: true
      shardingsphere:
        # 参数配置,显示sql
        props:
          sql:
            show: true
        # 配置数据源
        datasource:
          # 给每个数据源取别名,下面的ds1,ds1任意取名字
          names: ds0,ds1
          # 给master-ds1每个数据源配置数据库连接信息
          ds0:
            # 配置druid数据源
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://47.115.94.78:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT%2b8
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
          # 配置ds1-slave
          ds1:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://114.215.145.201:3306/ksd-sharding-db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT%2b8
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
        # 配置默认数据源ds0
        sharding:
          # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
          default-data-source-name: ds0
          # 配置分表的规则
          tables:
            # ksd_user 逻辑表名
            ksd_user:
              # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
              actual-data-nodes: ds$->{0..1}.ksd_user$->{0..1}
              # 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
              database-strategy:
                standard:
                  shardingColumn: birthday
                  preciseAlgorithmClassName: com.xuexiangban.shardingjdbc.algorithm.BirthdayAlgorithm
              table-strategy:
                inline:
                  sharding-column: age    # 分片字段(分片键)
                  algorithm-expression: ksd_user$->{age % 2} # 分片算法表达式
    # 整合mybatis的配置XXXXX
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.xuexiangban.shardingjdbc.entity
    

    定义分片的日期规则

    public class BirthdayAlgorithm implements PreciseShardingAlgorithm<Date> {
        List<Date> dateList = new ArrayList<>();
        {
            Calendar calendar1 = Calendar.getInstance();
            calendar1.set(2020, 1, 1, 0, 0, 0);
            Calendar calendar2 = Calendar.getInstance();
            calendar2.set(2021, 1, 1, 0, 0, 0);
            Calendar calendar3 = Calendar.getInstance();
            calendar3.set(2022, 1, 1, 0, 0, 0);
            dateList.add(calendar1.getTime());
            dateList.add(calendar2.getTime());
            dateList.add(calendar3.getTime());
        }
        @Override
        public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
            // 获取属性数据库的值
            Date date = preciseShardingValue.getValue();
            // 获取数据源的名称信息列表
            Iterator<String> iterator = collection.iterator();
            String target = null;
            for (Date s : dateList) {
                target = iterator.next();
                // 如果数据晚于指定的日期直接返回
                if (date.before(s)) {
                    break;
                }
            }
            return target;
        }
    }
    

    测试查看结果

    http://localhost:8085/user/save?sex=3&age=3&birthday=2020-03-09 —- ds1
    http://localhost:8085/user/save?sex=3&age=3&birthday=2021-03-09 —- ds0

    第四种:ShardingSphere - 符合分片策略(了解)

    • 对应接口:HintShardingStrategy。通过Hint而非SQL解析的方式分片的策略。
    • 对于分片字段非SQL决定,而是由其他外置条件决定的场景,克使用SQL hint灵活的注入分片字段。例如:按照用户登录的时间,主键等进行分库,而数据库中并无此字段。SQL hint支持通过Java API和SQL注解两种方式使用。让后分库分表更加灵活。

    在这里插入图片描述

    第五种:ShardingSphere - hint分片策略(了解)

    • 对应ComplexShardingStrategy。符合分片策略提供对SQL语句中的-,in和between and的分片操作支持。
    • ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键组合以及分片操作符透传至分片算法,完全由开发者自己实现,提供最大的灵活度。

    在这里插入图片描述

    七、分布式主键配置

    7.1、ShardingSphere - 分布式主键配置

    ShardingSphere提供灵活的配置分布式主键生成策略方式。在分片规则配置模块克配置每个表的主键生成策略。默认使用雪花算法。(snowflake)生成64bit的长整型数据。支持两种方式配置

    • SNOWFLAKE
    • UUID

    这里切记:主键列不能自增长。数据类型是:bigint(20)

    spring:
      shardingsphere:
        sharding:
          tables:
            # ksd_user 逻辑表名
            ksd_user:
              key-generator:
                  # 主键的列明,
                column: userid
                type: SNOWFLAKE
    

    执行

    http://localhost:8085/user/save?sex=3&age=3&birthday=2020-03-09
    可以查看到新增的语句多了一个userid为576906137413091329的唯一值。这个值是通过雪花算法计算出来的唯一值

    2021-03-11 22:59:01.605  INFO 4900 --- [nio-8085-exec-1] ShardingSphere-SQL                       : Actual SQL: ds1 ::: insert into ksd_user1 (nickname, password, sex, age, birthday, userid) VALUES (?, ?, ?, ?, ?, ?) ::: [zhangsan-70137485, 1234567, 3, 3, 2020-03-09 00:00:00.0, 576906137413091329]
    

    img

    八、分库分表 - 年月案例

    实战完成按照年月分库分表。

    策略类

    public class YearMonthShardingAlgorithm implements PreciseShardingAlgorithm<String> {
        private static final String SPLITTER = "_";
        @Override
        public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
            String tbName = shardingValue.getLogicTableName() + "_" + shardingValue.getValue();
            System.out.println("Sharding input:" + shardingValue.getValue() + ", output:{}" + tbName);
            return tbName;
        }
    }
    

    entity

    @Data
    public class Order {
        // 主键
        private Long orderid;
        // 订单编号
        private String ordernumber;
        // 用户ID
        private Long userid;
        // 产品id
        private Long productid;
        // 创建时间
        private Date createTime;
    }
    

    Mapper

    @Mapper
    @Repository
    public interface UserOrderMapper {
    
        @Insert("insert into ksd_user_order(ordernumber,userid,create_time,yearmonth) values(#{ordernumber},#{userid},#{createTime},#{yearmonth})")
        @Options(useGeneratedKeys = true,keyColumn = "orderid",keyProperty = "orderid")
        void addUserOrder(UserOrder userOrder);
    }
    

    配置如下:

    server:
      port: 8085
    spring:
      main:
        allow-bean-definition-overriding: true
      shardingsphere:
        # 参数配置,显示sql
        props:
          sql:
            show: true
        # 配置数据源
        datasource:
          # 给每个数据源取别名,下面的ds1,ds1任意取名字
          names: ds0,ds1
          # 给master-ds1每个数据源配置数据库连接信息
          ds0:
            # 配置druid数据源
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://47.115.94.78:3306/ksd_order_db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT%2b8
            username: root
            password: mkxiaoer1986.
            maxPoolSize: 100
            minPoolSize: 5
          # 配置ds1-slave
          ds1:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://114.215.145.201:3306/ksd_order_db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT%2b8
            username: root
            password: mkxiaoer
            maxPoolSize: 100
            minPoolSize: 5
        # 配置默认数据源ds0
        sharding:
          # 默认数据源,主要用于写,注意一定要配置读写分离 ,注意:如果不配置,那么就会把三个节点都当做从slave节点,新增,修改和删除会出错。
          default-data-source-name: ds0
          # 配置分表的规则
          tables:
            # ksd_user 逻辑表名
            ksd_user:
              key-generator:
                column: id
                type: SNOWFLAKE
              # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
              actual-data-nodes: ds$->{0..1}.ksd_user$->{0..1}
              # 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
              database-strategy:
                standard:
                  shardingColumn: birthday
                  preciseAlgorithmClassName: com.xuexiangban.shardingjdbc.algorithm.BirthdayAlgorithm
              table-strategy:
                inline:
                  sharding-column: age    # 分片字段(分片键)
                  algorithm-expression: ksd_user$->{age % 2} # 分片算法表达式
            ksd_order:
              # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
              actual-data-nodes: ds0.ksd_order$->{0..1}
              key-generator:
                column: orderid
                type: SNOWFLAKE
              # 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
              table-strategy:
                inline:
                  sharding-column: orderid    # 分片字段(分片键)
                  algorithm-expression: ksd_order$->{orderid % 2} # 分片算法表达式
            ksd_user_order:
              # 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
              actual-data-nodes: ds0.ksd_user_order_$->{2021..2022}${(1..3).collect{t ->t.toString().padLeft(2,'0')} }
              key-generator:
                column: orderid
                type: SNOWFLAKE
              # 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
              table-strategy:
    #            inline:
    #              shardingColumn: yearmonth
    #              algorithmExpression: ksd_user_order_$->{yearmonth}
                standard:
                  shardingColumn: yearmonth
                  preciseAlgorithmClassName: com.xuexiangban.shardingjdbc.algorithm.YearMonthShardingAlgorithm
    # 整合mybatis的配置XXXXX
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.xuexiangban.shardingjdbc.entity
    

    test

    @SpringBootTest
    class ShardingJdbcApplicationTests {
        @Autowired
        private UserOrderService userOrderService;
        @Test
        void contextLoads() throws Exception {
            User user = new User();
            user.setNickname("zhangsan" + new Random().nextInt());
            user.setPassword("1234567");
            user.setSex(1);
            user.setAge(2);
            user.setBirthday(new Date());
            Order order = new Order();
            order.setCreateTime(new Date());
            order.setOrdernumber("133455678");
            order.setProductid(1234L);
            userOrderService.saveUserOrder(user, order);
        }
        @Autowired
        private UserOrderMapper userOrderMapper;
        @Test
        public void orderyearMaster() {
            UserOrder userOrder = new UserOrder();
            userOrder.setOrderid(10000L);
            userOrder.setCreateTime(new Date());
            userOrder.setOrdernumber("133455678");
            userOrder.setYearmonth("202103");
            userOrder.setUserid(1L);
            userOrderMapper.addUserOrder(userOrder);
        }
    }
    

    九、ShardingJdbc的事务管理

    9.1、分布式式事务的应用和实践

    官方地址:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/transaction/function/base-transaction-seata/

    https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/transaction/

    数据库事务需要满足ACID(原子性、一致性、隔离性、持久性)四个特性。

    • 原子性(Atomicity)指事务作为整体来执行,要么全部执行,要么全不执行。
    • 一致性(Consistency)指事务应确保数据从一个一致的状态转变为另一个一致的状态。
    • 隔离性(Isolation)指多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
    • 持久性(Durability)指已提交的事务修改数据会被持久保存。

    在单一数据节点中,事务仅限于对单一数据库资源的访问控制,称之为本地事务。几乎所有的成熟的关系型数据库都提供了对本地事务的原生支持。 但是在基于微服务的分布式应用环境下,越来越多的应用场景要求对多个服务的访问及其相对应的多个数据库资源能纳入到同一个事务当中,分布式事务应运而生。

    关系型数据库虽然对本地事务提供了完美的ACID原生支持。 但在分布式的场景下,它却成为系统性能的桎梏。如何让数据库在分布式场景下满足ACID的特性或找寻相应的替代方案,是分布式事务的重点工作。

    本地事务

    在不开启任何分布式事务管理器的前提下,让每个数据节点各自管理自己的事务。 它们之间没有协调以及通信的能力,也并不互相知晓其他数据节点事务的成功与否。 本地事务在性能方面无任何损耗,但在强一致性以及最终一致性方面则力不从心。

    两阶段提交

    XA协议最早的分布式事务模型是由X/Open国际联盟提出的X/Open Distributed Transaction Processing(DTP)模型,简称XA协议。

    基于XA协议实现的分布式事务对业务侵入很小。 它最大的优势就是对使用方透明,用户可以像使用本地事务一样使用基于XA协议的分布式事务。 XA协议能够严格保障事务ACID特性。

    严格保障事务ACID特性是一把双刃剑。 事务执行在过程中需要将所需资源全部锁定,它更加适用于执行时间确定的短事务。 对于长事务来说,整个事务进行期间对数据的独占,将导致对热点数据依赖的业务系统并发性能衰退明显。 因此,在高并发的性能至上场景中,基于XA协议的分布式事务并不是最佳选择。

    柔性事务

    如果将实现了ACID的事务要素的事务称为刚性事务的话,那么基于BASE事务要素的事务则称为柔性事务。 BASE是基本可用、柔性状态和最终一致性这三个要素的缩写。

    基本可用(Basically Available)保证分布式事务参与方不一定同时在线。
    柔性状态(Soft state)则允许系统状态更新有一定的延时,这个延时对客户来说不一定能够察觉。
    而最终一致性(Eventually consistent)通常是通过消息传递的方式保证系统的最终一致性。

    在ACID事务中对隔离性的要求很高,在事务执行过程中,必须将所有的资源锁定。 柔性事务的理念则是通过业务逻辑将互斥锁操作从资源层面上移至业务层面。通过放宽对强一致性要求,来换取系统吞吐量的提升。

    基于ACID的强一致性事务和基于BASE的最终一致性事务都不是银弹,只有在最适合的场景中才能发挥它们的最大长处。 可通过下表详细对比它们之间的区别,以帮助开发者进行技术选型。

    在这里插入图片描述

    1-1:导入分布式事务的依赖

     <!--依赖sharding-->
     <dependency>
         <groupId>io.shardingsphere</groupId>
         <artifactId>sharding-transaction-spring-boot-starter</artifactId>
         <version>3.1.0</version>
     </dependency>
    

    1-2:事务的几种类型

    本地事务

    • 完全支持非跨库事务,例如:仅分表,或分库但是路由的结果在单库中。
    • 完全支持因逻辑异常导致的跨库事务。例如:同一事务中,跨两个库更新。更新完毕后,抛出空指针,则两个库的内容都能回滚。
    • 不支持因网络、硬件异常导致的跨库事务。例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库宕机,则只有第二个库数据提交。

    两阶段XA事务

    • 支持数据分片后的跨库XA事务
    • 两阶段提交保证操作的原子性和数据的强一致性
    • 服务宕机重启后,提交/回滚中的事务可自动恢复
    • SPI机制整合主流的XA事务管理器,默认Atomikos,可以选择使用Narayana和Bitronix
    • 同时支持XA和非XA的连接池
    • 提供spring-boot和namespace的接入端

    不支持:

    • 服务宕机后,在其它机器上恢复提交/回滚中的数据

    Seata柔性事务

    • 完全支持跨库分布式事务
    • 支持RC隔离级别
    • 通过undo快照进行事务回滚
    • 支持服务宕机后的,自动恢复提交中的事务

    依赖:

    • 需要额外部署Seata-server服务进行分支事务的协调
      待优化项
    • ShardingSphere和Seata会对SQL进行重复解析

    1-3:导入分布式事务的依赖

    @Service
    public class UserOrderService {
        @Autowired
        private UserMapper userMapper;
        
        @Autowired
        private OrderMapper orderMapper;
        
        @ShardingTransactionType(TransactionType.XA)
        @Transactional(rollbackFor = Exception.class)
        public int saveUserOrder(User user, Order order) {
            userMapper.addUser(user);
            order.setUserid(user.getId());
            orderMapper.addOrder(order);
            //int a = 1/0; //测试回滚,统一提交的话,将这行注释掉就行
            return 1;
        }
    }
    

    测试

    @SpringBootTest
    class ShardingJdbcApplicationTests {
        @Autowired
        private UserOrderService userOrderService;
        @Test
        void contextLoads() throws Exception {
            User user = new User();
            user.setNickname("zhangsan" + new Random().nextInt());
            user.setPassword("1234567");
            user.setSex(1);
            user.setAge(2);
            user.setBirthday(new Date());
            Order order = new Order();
            order.setCreateTime(new Date());
            order.setOrdernumber("133455678");
            order.setProductid(1234L);
            userOrderService.saveUserOrder(user, order);
        }
    }
    

    十、总结

    10.1、基础规范

    • 表必须有主键,建议使用整型作为主键
    • 禁止使用外键,表之间的关联性和完整性通过应用层来控制
    • 表在设计之初,应该考虑到大致的数据级,若表记录小于1000W,尽量使用单表,不建议分表。
    • 建议将大字段,访问频率低,或者不需要作为筛选条件的字段拆分到拓展表中,(做好表垂直拆分)
    • 控制单实例表的总数,单个表分表数控制在1024以内。

    10.2、列设计规范

    • 正确区分tinyint、int、bigint的范围
    • 使用varchar(20)存储手机号,不要使用整数
    • 使用int存储ipv4 不要使用char(15)
    • 涉及金额使用decimal/varchar,并制定精度
    • 不要设计为null的字段,而是用空字符,因为null需要更多的空间,并且使得索引和统计变得更复杂。

    10.3、索引规范

    • 唯一索引使用uniq_[字段名]来命名
    • 非唯一索引使用idx_[字段名]来命名
    • 不建议在频繁更新的字段上建立索引
    • 非必要不要进行JOIN,如果要进行join查询,被join的字段必须类型相同,并建立索引。
    • 单张表的索引数量建议控制在5个以内,索引过多,不仅会导致插入更新性能下降,还可能导致MYSQL的索引出错和性能下降
    • 组合索引字段数量不建议超过5个,理解组合索引的最左匹配原则,避免重复建设索引。比如你建立了(x,y,z) 相当于你建立了(x),(x,y),(x,y,z)

    10.4、SQL规范

    • 禁止使用selet *,只获取必要字段,select* 会增加cpu/i0/内存、带宽的消耗。
    • insert 必须指定字段,禁止使用insert into Table values().指定字段插入,在表结果变更时,能保证对应应用程序无影响。
    • 隐私类型转换会使索引失效,导致全表扫描。(比如:手机号码搜索时未转换成字符串)
    • 禁止在where后面查询列使用内置函数或者表达式,导致不能命中索引,导致全表扫描
    • 禁止负向查询(!=,not like ,no in等)以及%开头的模糊查询,造成不能命中索引,导致全表扫描
    • 避免直接返回大结果集造成内存溢出,可采用分段和游标方式。
    • 返回结果集时尽量使用limit分页显示。
    • 尽量在order by/group by的列上创建索引。
    • 大表扫描尽量放在镜像库上去做
    • 禁止大表join查询和子查询
    • 尽量避免数据库内置函数作为查询条件
    • 应用程序尽量捕获SQL异常

    10.5、表的垂直拆分

    垂直拆分:业务模块拆分、商品库,用户库,订单库

    水平拆分:对表进行水平拆分(也就是我们说的:分表)

    表进行垂直拆分:表的字段过多,字段使用的频率不一。(可以拆分两个表建立1:1关系)

    • 将一个属性过多的表,一行数据较大的表,将不同的属性分割到不同的数据库表中。以降低单库表的大小。
      特点:
    • 每个表的结构不一致
    • 每个表的数量都是全量
    • 表和表之间一定会有一列会进行关联,一般都是主键

    原则:

    • 将长度较短,访问频率较高的字段放在一个表中,主表
    • 将长度较长、访问频率比较低的字段放一个表中
    • 将经常访问字段放一个表中。
    • 所有表的并集是全量数据。

    10.6、如何平滑添加字段

    场景:在开发时,有时需要给表加字段,在大数据量且分表的情况下,怎么样平滑添加。

    1:直接alter table add column,数据量大时不建议,(会产生写锁)

    alter table ksd_user add column api_pay_no varchar(32) not null  comment '用户扩展订单号'
    alter table ksd_user add column api_pay_no varchar(32) not null unique comment '用户扩展订单号'
    

    2:提前预留字段(不优雅:造成空间浪费,预留多少很难控制,拓展性差)

    3:新增一张表,(增加字段),迁移原表数据,在重新命名新表作为原表。

    4:放入extinfo(无法使用索引)

    5:提前设计,使用key/value方法存储,新增字段时 ,直接加一个key就好了(优雅)

    十一、Springboot整合ShardingJdbc3.0和案例分析

    目标

    使用Sharding-JDBC 分库分表,掌握什么是Sharding-JDBC.

    分析

    1. 什么是Sharding-JDBC

    Sharding-JDBC提供标准化的数据分片、分布式事务和数据库治理功能,定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。

    基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。

    支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。

    2. 为什么要分片?

    • 传统的将数据集中存储至单一数据节点的解决方案,在性能、可用性和运维成本这三方面已经难于满足互联网的海量数据场景。
    • 从性能方面来说,由于关系型数据库大多采用B+树类型的索引,在数据量超过阈值的情况下,索引深度的增加也将使得磁盘访问的IO次数增加,进而导致查询性能的下降;同时,高并发访问请求也使得集中式数据库成为系统的最大瓶颈。
    • 从可用性的方面来讲,服务化的无状态型,能够达到较小成本的随意扩容,这必然导致系统的最终压力都落在数据库之上。而单一的数据节点,或者简单的主从架构,已经越来越难以承担。数据库的可用性,已成为整个系统的关键。
    • 从运维成本方面考虑,当一个数据库实例中的数据达到阈值以上,对于DBA的运维压力就会增大。数据备份和恢复的时间成本都将随着数据量的大小而愈发不可控。一般来讲,单一数据库实例的数据的阈值在1TB之内,是比较合理的范围。
    • 在传统的关系型数据库无法满足互联网场景需要的情况下,将数据存储至原生支持分布式的NoSQL的尝试越来越多。 但NoSQL对SQL的不兼容性以及生态圈的不完善,使得它们在与关系型数据库的博弈中始终无法完成致命一击,而关系型数据库的地位却依然不可撼动。
    • 据分片指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中以达到提升性能瓶颈以及可用性的效果。 数据分片的有效手段是对关系型数据库进行分库和分表。分库和分表均可以有效的避免由数据量超过可承受阈值而产生的查询瓶颈。 除此之外,分库还能够用于有效的分散对数据库单点的访问量;分表虽然无法缓解数据库压力,但却能够提供尽量将分布式事务转化为本地事务的可能,一旦涉及到跨库的更新操作,分布式事务往往会使问题变得复杂。 使用多主多从的分片方式,可以有效的避免数据单点,从而提升数据架构的可用性。
    • 通过分库和分表进行数据的拆分来使得各个表的数据量保持在阈值以下,以及对流量进行疏导应对高访问量,是应对高并发和海量数据系统的有效手段。

    3. 分片的方式

    数据分片的拆分方式又分为垂直分片和水平分片。

    垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中(或者是把一张表数据拆分成n多个小表)。相对于垂直拆分,水平拆分不是将表的数据做分类,而是按照某个字段的某种规则来分散到多个库中每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是**按照数据行的切分**,就是将表中的某些行切分到一个数据库,而另外某些行又切分到其他的数据库中,主要有分表,分库两种模式 该方式提高了系统的稳定性跟负载能力,但是跨库join性能较差。

    4. Sharding-JDBC的核心/原理

    Sharding-JDBC数据分片主要流程是由SQL解析 执行器优化 SQL路由 SQL改写 SQL执行 结果归并的流程组成。

    SQL解析

    分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。

    SQL解析分为两步, 第一步为 词法解析, 词法解析的意思是就是将SQL进行拆分。

    例:

    select *from t_user where id = 1
    

    词法解析:

    [select] [*] [from] [t_user] [where] [id=1]
    

    第二步语法解析,语法解析器将SQL转换为抽象语法树。

    在这里插入图片描述

    执行器优化

    合并和优化分片条件,如OR等。

    SQL路由

    根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。

    举例说明,如果按照order_id的奇数和偶数进行数据分片,一个单表查询的SQL如下:

    SELECT *FROM t_order WHERE order_id IN (1, 2);
    

    那么路由的结果应为:

    SELECT* FROM t_order_0 WHERE order_id IN (1, 2);
    SELECT * FROM t_order_1 WHERE order_id IN (1, 2);
    

    SQL改写

    将SQL改写为在真实数据库中可以正确执行的语句,SQL改写分为正确性改写和优化改写。

     从一个最简单的例子开始,若逻辑SQL为:
     SELECT order_id FROM t_order WHERE order_id=1;
     假设该SQL配置分片键order_id,并且order_id=1的情况,将路由至分片表1。那么改写之后的SQL应该为:
     SELECT order_id FROM t_order_1 WHERE order_id=1;
    

    SQL执行

    通过多线程执行器异步执行。

    结果归并

    将多个执行结果集归并以便于通过统一的JDBC接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。

    SharedingJdbc完成数据的读写分离

    目标

    使用sharedingjdbc完成数据库的分库分表业务

    步骤

    1:新建一个springboot工程

    2:创建两个数据库order1,order2,分别创建t_address表如下:

    DROP TABLE IF EXISTS `t_address`;
    CREATE TABLE `t_address` (
      `id` bigint(20) NOT NULL,
      `code` varchar(64) DEFAULT NULL COMMENT '编码',
      `name` varchar(64) DEFAULT NULL COMMENT '名称',
      `pid` varchar(64) NOT NULL DEFAULT '0' COMMENT '父id',
      `type` int(11) DEFAULT NULL COMMENT '1国家2省3市4县区',
      `lit` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    **3: 开始整合SpringBoot,**这种方式比较简单只要加入sharding-jdbc-spring-boot-starter依赖,在application.yml中配置数据源,分片策略即可使用,这种方式简单,方便。pom.xml

    <dependency>
        <groupId>io.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    <dependency>
        <groupId>io.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-namespace</artifactId>
        <version>3.0.0</version>
    </dependency>
    

    appplication.yml

    mybatis:
      configuration:
        mapUnderscoreToCamelCase: true
    spring:
      main:
        allow-bean-definition-overriding: true
    # shardingjdbc分库分表
    sharding:
      jdbc:
        datasource:
          names: ds0,ds1
          ds0:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://localhost:3306/order1?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
            username: root
            password: root
          ds1:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://localhost:3306/order2?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
            username: root
            password: root
        config:
          sharding:
            props:
              sql.show: true
            tables:
              t_user: #t_user表【即分库,又分表】
                key-generator-column-name: id # 主键
                actual-data-nodes: ds${0..1}.t_user${0..1} #数据节点
                database-strategy: #分库策略
                  inline:
                    sharding-column: city_id
                    algorithm-expression: ds${city_id % 2}
                table-strategy: #分表策略
                  inline:
                    shardingColumn: sex
                    algorithm-expression: t_user${sex % 2}
              t_address: #t_address表【只分库】
                key-generator-column-name: id
                actual-data-nodes: ds${0..1}.t_address
                database-strategy:
                  inline:
                    shardingColumn: lit
                    algorithm-expression: ds${lit % 2}
    

    4:编写Vo

    package com.itheima.springbootshardingpro.vo;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class AddressVo {
        private Long id;
        private String code;
        private String name;
        private String pid;
        private Integer type;
        private Integer lit;
    }
    

    5:编写Dao

    @Mapper
    public interface IndexDao {
        @Options(useGeneratedKeys = true)
        @Insert("insert into t_address (code,name,pid,type,lit)values(#{code},#{name},#{pid},#{type},#{lit})")
        int insertAddress(AddressVo addressVo);
        @Select("select * from t_address order by lit")
        List<AddressVo> listAddress();
    }
    

    6: 编写controller

    @RestController
    public class IndexController {
        @Autowired
        private IndexDao indexDao;
        @PostMapping("/addAddress")
        public int addAddress(AddressVo addressVo){
            int row = indexDao.insertAddress(addressVo);
            return row;
        }
        @GetMapping("/listAddress")
        public PageInfo<AddressVo> listAddress(@RequestParam(required=false,defaultValue="1")Integer pageNum,
                                    @RequestParam(required=false,defaultValue="5")Integer pageSize){
            PageHelper.startPage(pageNum,pageSize);
            List<AddressVo> list =  indexDao.listAddress();
            PageInfo<AddressVo> info = new PageInfo<>(list);
            return info;
        }
    }
    

    此时,启动项目,测试

    展开全文
  • sharding-jdbc和sharding-proxy使用 sharding-jdbc使用 本项目选择sharding-jdbc作为分表分库的组件 sharding-jdbc后续发展为Sharding-Sphere,包含sharding-jdbc、Sharding-Proxy、Sharding-Sidecar 定位为轻量级...

    sharding-jdbc和sharding-proxy使用

    sharding-jdbc使用

    本项目选择sharding-jdbc作为分表分库的组件

    sharding-jdbc后续发展为Sharding-Sphere,包含sharding-jdbc、Sharding-Proxy、Sharding-Sidecar

    定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,需

    额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。

    • 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
    • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
    • 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。

    Sharding-JDBC Architecture

    1、引入maven依赖

    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>4.1.1</version>
    </dependency>
    <!-- 添加druid连接池;如果用druid-spring-boot-starter要在启动类加@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class}) -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    

    为什么配置了某个数据连接池的spring-boot-starter(比如druid)和sharding-jdbc-spring-boot-starter时,系统启动会报错?

    回答:

    1. 因为数据连接池的starter(比如druid)可能会先加载并且其创建一个默认数据源,这将会使得sharding-jdbc创建数据源时发生冲突。
    2. 解决办法为,去掉数据连接池的starter即可,sharing-jdbc自己会创建数据连接池。

    如果出现datasource问题按上述进行修改

    数据库表

    在这里插入图片描述
    本分库分表使用了两个数据库test和test2;每个数据库中有两张结构相同的user表分别为user_0,user_1;其中user表为逻辑表,user_1,user_0为实际存在的物理表

    2. 规则配置

    ShardingSphere-JDBC 可以通过 JavaYAMLSpring 命名空间Spring Boot Starter 这 4 种方式进行配置,开发者可根据场景选择适合的配置方式。 详情请参见配置手册

    server:
      port: 8090
    
    #spring:
    #  datasource:
    #    driver-class-name: com.mysql.cj.jdbc.Driver
    #    url: jdbc:mysql://127.0.0.1:3306/test
    #    username: root
    #    password: 123456
    
    
    spring:
      shardingsphere:
        datasource:
          names: ds0,ds1
          ds0:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3306/test
            username: root
            password: 123456
          ds1:
            type: com.alibaba.druid.pool.DruidDataSource
            driverClassName: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3306/test2
            username: root
            password: 123456
    
    
        sharding:
    #      默认查询的数据源(对于不分库分表的表使用此数据源)
          default-data-source-name: ds0
    #      默认分库策略
          default-database-strategy:
            inline:
              sharding-column: id
              algorithm-expression: ds$->{(id % 4) >1?1:0}
    #          分表策略
          tables:
            user:
              actual-data-nodes: ds$->{0..1}.user_$->{0..1}
              table-strategy:
                inline:
                  sharding-column: id
                  algorithm-expression: user_$->{id % 2}
              key-generator:
                column: id
                type: SNOWFLAKE
        props:
          sql.show: true
    

    去除原来的spring加载数据源的配置;添加多数据源配置如上所示;

    使用

    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    @Data
    @TableName("user")
    public class User {
    
        @TableId(type = IdType.AUTO)
        private Long id;
    
        private String name;
    
        private Integer age;
    }
    
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.example.demo.entity.User;
    
    public interface UserMapper extends BaseMapper<User> {
    
    }
    
    
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.example.demo.entity.Role;
    import com.example.demo.entity.User;
    import com.example.demo.mapper.RoleMapper;
    import com.example.demo.mapper.UserMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    @Service
    public class UserServer {
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private RoleMapper roleMapper;
    
        public List<User> findUserList() {
            return userMapper.selectList(Wrappers.<User>lambdaQuery().orderByAsc(User::getId));
        }
    
    
        public Long insert(User user) {
            userMapper.insert(user);
            return user.getId();
        }
    
        public boolean update(User user) {
            userMapper.updateById(user);
            return true;
        }
    
    
        public List<Role> findRoleList() {
            return roleMapper.selectList(Wrappers.emptyWrapper());
        }
    }
    

    查询user表数据时,可以同时查询出test.user_0,test.user_1,test2.user_0,test2.user_1中的数据并把结果合并展示;
    在这里插入图片描述
    查询结果:

    [{"id":1,"name":"cxh","age":20},{"id":2,"name":"cxh","age":20},{"id":3,"name":"cxh","age":20},{"id":4,"name":"cxh","age":20},{"id":1402161819524833282,"name":"test","age":20},{"id":1402161899086585857,"name":"test","age":20},{"id":1402161947165892610,"name":"test","age":20},{"id":1402162015398830082,"name":"test","age":20},{"id":1402162021056946178,"name":"test","age":20},{"id":1402162026698285058,"name":"test","age":20},{"id":1402162037330845698,"name":"test","age":20},{"id":1402162043290951681,"name":"cxh","age":20}]
    

    这里的user表为逻辑表,对应配置中的

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

    实际查询的物理表为user_0和user_1;

    其他方面都是正常使用

    注意事项

    行表达式标识符可以使用${...}$->{...},但前者与Spring本身的属性文件占位符冲突,因此在Spring环境中使用行表达式标识符建议使用$->{...}

    配置文件说明

    在这里插入图片描述

    数据源名称,多数据源以逗号分隔
    

    在这里插入图片描述

    type= #数据库连接池类名称
    driver-class-name= #数据库驱动类名
    url= #数据库url连接
    username= #数据库用户名
    password= #数据库密码
    xxx= #数据库连接池的其它属性
    

    在这里插入图片描述

    default-data-source-name= #未配置分片规则的表将通过默认数据源定位
    

    在这里插入图片描述

    spring.shardingsphere.sharding.default-database-strategy.xxx= #默认数据库分片策略,同分库策略
    spring.shardingsphere.sharding.default-table-strategy.xxx= #默认表分片策略,同分表策略
    actual-data-nodes= #由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式。缺省表示使用已知数据源与逻辑表名称生成数据节点,用于广播表(即每个库中都需要一个同样的表用于关联查询,多为字典表)或只分库不分表且所有库的表结构完全一致的情况
    

    其他配置

    spring.shardingsphere.datasource.names= #数据源名称,多数据源以逗号分隔
    
    spring.shardingsphere.datasource.<data-source-name>.type= #数据库连接池类名称
    spring.shardingsphere.datasource.<data-source-name>.driver-class-name= #数据库驱动类名
    spring.shardingsphere.datasource.<data-source-name>.url= #数据库url连接
    spring.shardingsphere.datasource.<data-source-name>.username= #数据库用户名
    spring.shardingsphere.datasource.<data-source-name>.password= #数据库密码
    spring.shardingsphere.datasource.<data-source-name>.xxx= #数据库连接池的其它属性
    
    spring.shardingsphere.sharding.tables.<logic-table-name>.actual-data-nodes= #由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式。缺省表示使用已知数据源与逻辑表名称生成数据节点,用于广播表(即每个库中都需要一个同样的表用于关联查询,多为字典表)或只分库不分表且所有库的表结构完全一致的情况
    
    #分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
    
    #用于单分片键的标准分片场景
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.sharding-column= #分片列名称
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.precise-algorithm-class-name= #精确分片算法类名称,用于=和IN。该类需实现PreciseShardingAlgorithm接口并提供无参数的构造器
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.range-algorithm-class-name= #范围分片算法类名称,用于BETWEEN,可选。该类需实现RangeShardingAlgorithm接口并提供无参数的构造器
    
    #用于多分片键的复合分片场景
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.complex.sharding-columns= #分片列名称,多个列以逗号分隔
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.complex.algorithm-class-name= #复合分片算法类名称。该类需实现ComplexKeysShardingAlgorithm接口并提供无参数的构造器
    
    #行表达式分片策略
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.inline.sharding-column= #分片列名称
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.inline.algorithm-expression= #分片算法行表达式,需符合groovy语法
    
    #Hint分片策略
    spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.hint.algorithm-class-name= #Hint分片算法类名称。该类需实现HintShardingAlgorithm接口并提供无参数的构造器
    
    #分表策略,同分库策略
    spring.shardingsphere.sharding.tables.<logic-table-name>.table-strategy.xxx= #省略
    
    spring.shardingsphere.sharding.tables.<logic-table-name>.key-generator.column= #自增列名称,缺省表示不使用自增主键生成器
    spring.shardingsphere.sharding.tables.<logic-table-name>.key-generator.type= #自增列值生成器类型,缺省表示使用默认自增列值生成器。可使用用户自定义的列值生成器或选择内置类型:SNOWFLAKE/UUID
    spring.shardingsphere.sharding.tables.<logic-table-name>.key-generator.props.<property-name>= #属性配置, 注意:使用SNOWFLAKE算法,需要配置worker.id与max.tolerate.time.difference.milliseconds属性。若使用此算法生成值作分片值,建议配置max.vibration.offset属性
    
    spring.shardingsphere.sharding.binding-tables[0]= #绑定表规则列表
    spring.shardingsphere.sharding.binding-tables[1]= #绑定表规则列表
    spring.shardingsphere.sharding.binding-tables[x]= #绑定表规则列表
    
    spring.shardingsphere.sharding.broadcast-tables[0]= #广播表规则列表
    spring.shardingsphere.sharding.broadcast-tables[1]= #广播表规则列表
    spring.shardingsphere.sharding.broadcast-tables[x]= #广播表规则列表
    
    spring.shardingsphere.sharding.default-data-source-name= #未配置分片规则的表将通过默认数据源定位
    spring.shardingsphere.sharding.default-database-strategy.xxx= #默认数据库分片策略,同分库策略
    spring.shardingsphere.sharding.default-table-strategy.xxx= #默认表分片策略,同分表策略
    spring.shardingsphere.sharding.default-key-generator.type= #默认自增列值生成器类型,缺省将使用org.apache.shardingsphere.core.keygen.generator.impl.SnowflakeKeyGenerator。可使用用户自定义的列值生成器或选择内置类型:SNOWFLAKE/UUID
    spring.shardingsphere.sharding.default-key-generator.props.<property-name>= #自增列值生成器属性配置, 比如SNOWFLAKE算法的worker.id与max.tolerate.time.difference.milliseconds
    
    spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.master-data-source-name= #详见读写分离部分
    spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[0]= #详见读写分离部分
    spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[1]= #详见读写分离部分
    spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.slave-data-source-names[x]= #详见读写分离部分
    spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.load-balance-algorithm-class-name= #详见读写分离部分
    spring.shardingsphere.sharding.master-slave-rules.<master-slave-data-source-name>.load-balance-algorithm-type= #详见读写分离部分
    
    spring.shardingsphere.props.sql.show= #是否开启SQL显示,默认值: false
    spring.shardingsphere.props.executor.size= #工作线程数量,默认值: CPU核数
    

    自定义的列值生成器

    sharding-jdbc提供了两种主键生成策略UUID、SNOWFLAKE ,默认使用SNOWFLAKE,

    还抽离出分布式主键生成器的接口org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator,方便用户自

    行实现自定义的自增主键生成器。

    package com.example.demo.config;
    
    
    import org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator;
    
    import java.util.Properties;
    
    //此处用于自定义主键生成算法
    public class SimpleShardingKeyGenerator implements ShardingKeyGenerator {
    
        private Long id = 5L;
        private Properties properties = new Properties();
    
        public SimpleShardingKeyGenerator() {
    
        }
    
        @Override
        public synchronized Comparable<?> generateKey() {
            System.out.println("1234567890");
            return id++;
        }
    //这里的返回值对应yaml中的type;具体见下图
        @Override
        public String getType() {
            return "SIMPLE";
    
        }
    
        @Override
        public Properties getProperties() {
            return this.properties;
        }
    
        @Override
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    
    }
    

    在这里插入图片描述
    自定义后配置,添加下面内容

    META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator`,文件内容为:#`your_package_name.SimpleShardingKeyGenerator
    com.example.demo.config.SimpleShardingKeyGenerator
    

    注意点:
    在这里插入图片描述
    对应的分库分表的实体类中,主键字段一定要加上上面的注解,否则的话自定义的主键生成算法不生效;默认使用SNOWFLAKE算法实现

    Sharding-Proxy使用

    Sharding-Proxy

    定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前

    先提供MySQL/PostgreSQL版本,它可以使用任何兼容MySQL/PostgreSQL协议的访问客户端(如:MySQL

    Command Client, MySQL Workbench, Navicat等)操作数据,对DBA更加友好。

    • 向应用程序完全透明,可直接当做MySQL/PostgreSQL使用。
    • 适用于任何兼容MySQL/PostgreSQL协议的的客户端。

    Sharding-Proxy Architecture

    下载sharding-proxy

    https://archive.apache.org/dist/shardingsphere/
    在这里插入图片描述
    本项目使用4.1.1版本
    在这里插入图片描述

    解压

    在这里插入图片描述

    添加mysql的jar包

    在这里插入图片描述

    把jar拷贝到bin目录下

    修改配置文件

    在这里插入图片描述
    主要修改conf下的config-sharding.yaml和server.yaml文件

    config-sharding.yaml

    #
    # Licensed to the Apache Software Foundation (ASF) under one or more
    # contributor license agreements.  See the NOTICE file distributed with
    # this work for additional information regarding copyright ownership.
    # The ASF licenses this file to You under the Apache License, Version 2.0
    # (the "License"); you may not use this file except in compliance with
    # the License.  You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    #
    
    ######################################################################################################
    # 
    # Here you can configure the rules for the proxy.
    # This example is configuration of sharding rule.
    #   
    # If you want to use sharding, please refer to this file; 
    # if you want to use master-slave, please refer to the config-master_slave.yaml.
    # 
    ######################################################################################################
    #
    #schemaName: sharding_db
    #
    #dataSources:
    #  ds_0:
    #    url: jdbc:postgresql://127.0.0.1:5432/demo_ds_0?serverTimezone=UTC&useSSL=false
    #    username: postgres
    #    password: postgres
    #    connectionTimeoutMilliseconds: 30000
    #    idleTimeoutMilliseconds: 60000
    #    maxLifetimeMilliseconds: 1800000
    #    maxPoolSize: 50
    #  ds_1:
    #    url: jdbc:postgresql://127.0.0.1:5432/demo_ds_1?serverTimezone=UTC&useSSL=false
    #    username: postgres
    #    password: postgres
    #    connectionTimeoutMilliseconds: 30000
    #    idleTimeoutMilliseconds: 60000
    #    maxLifetimeMilliseconds: 1800000
    #    maxPoolSize: 50
    #
    #shardingRule:
    #  tables:
    #    t_order:
    #      actualDataNodes: ds_${0..1}.t_order_${0..1}
    #      tableStrategy:
    #        inline:
    #          shardingColumn: order_id
    #          algorithmExpression: t_order_${order_id % 2}
    #      keyGenerator:
    #        type: SNOWFLAKE
    #        column: order_id
    #    t_order_item:
    #      actualDataNodes: ds_${0..1}.t_order_item_${0..1}
    #      tableStrategy:
    #        inline:
    #          shardingColumn: order_id
    #          algorithmExpression: t_order_item_${order_id % 2}
    #      keyGenerator:
    #        type: SNOWFLAKE
    #        column: order_item_id
    #  bindingTables:
    #    - t_order,t_order_item
    #  defaultDatabaseStrategy:
    #    inline:
    #      shardingColumn: user_id
    #      algorithmExpression: ds_${user_id % 2}
    #  defaultTableStrategy:
    #    none:
    
    ######################################################################################################
    #
    # If you want to connect to MySQL, you should manually copy MySQL driver to lib directory.
    #
    ######################################################################################################
    
    schemaName: sharding_db
    # 下面是一些分库分表的配置,可参考springboot项目中的配置
    dataSources:
      ds0:
        url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useSSL=false
        username: root
        password: 123456
        connectionTimeoutMilliseconds: 30000
        idleTimeoutMilliseconds: 60000
        maxLifetimeMilliseconds: 1800000
        maxPoolSize: 50
      ds1:
        url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=UTC&useSSL=false
        username: root
        password: 123456
        connectionTimeoutMilliseconds: 30000
        idleTimeoutMilliseconds: 60000
        maxLifetimeMilliseconds: 1800000
        maxPoolSize: 50
    
    shardingRule:
      tables:
        user:
          actualDataNodes: ds${0..1}.user_${0..1}
          tableStrategy:
            inline:
              shardingColumn: id
              algorithmExpression: user_${id % 2}
          keyGenerator:
            type: SNOWFLAKE
            column: id
        
      defaultDatabaseStrategy:
        inline:
          shardingColumn: id
          algorithmExpression: ds${id % 2}
      defaultTableStrategy:
        none:
    

    server.yaml

    #
    # Licensed to the Apache Software Foundation (ASF) under one or more
    # contributor license agreements.  See the NOTICE file distributed with
    # this work for additional information regarding copyright ownership.
    # The ASF licenses this file to You under the Apache License, Version 2.0
    # (the "License"); you may not use this file except in compliance with
    # the License.  You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    #
    
    ######################################################################################################
    # 
    # If you want to configure orchestration, authorization and proxy properties, please refer to this file.
    # 
    ######################################################################################################
    #
    #orchestration:
    #  orchestration_ds:
    #    orchestrationType: registry_center,config_center,distributed_lock_manager
    #    instanceType: zookeeper
    #    serverLists: localhost:2181
    #    namespace: orchestration
    #    props:
    #      overwrite: false
    #      retryIntervalMilliseconds: 500
    #      timeToLiveSeconds: 60
    #      maxRetries: 3
    #      operationTimeoutMilliseconds: 500
    
    #proxy服务的一些用户验证信息配置
    authentication:
      users:
        root:
          password: root
        sharding:
          password: sharding 
          authorizedSchemas: sharding_db
    
    props:
      max.connections.size.per.query: 1
      acceptor.size: 16  # The default value is available processors count * 2.
      executor.size: 16  # Infinite by default.
      proxy.frontend.flush.threshold: 128  # The default value is 128.
        # LOCAL: Proxy will run with LOCAL transaction.
        # XA: Proxy will run with XA transaction.
        # BASE: Proxy will run with B.A.S.E transaction.
      proxy.transaction.type: LOCAL
      proxy.opentracing.enabled: false
      proxy.hint.enabled: false
      query.with.cipher.column: true
      sql.show: true
      allow.range.query.with.inline.sharding: false
    
    

    权限验证

    用于执行登录Sharding Proxy的权限验证。配置用户名、密码、可访问的数据库后,必须使用正确的用户名、密码才可登录Proxy。

    authentication:
      users:
        root: # 自定义用户名
          password: root # 自定义用户名
        sharding: # 自定义用户名
          password: sharding # 自定义用户名
          authorizedSchemas: sharding_db, masterslave_db # 该用户授权可访问的数据库,多个用逗号分隔。缺省将拥有root权限,可访问全部数据库。
    

    启动服务

    在这里插入图片描述
    bin目录下的start.bat启动服务(windows)

    默认服务端口为3307,

    用navcat连接该服务

    在这里插入图片描述
    之后就可以查看分库分表后的数据了
    在这里插入图片描述

    参考文档地址:

    https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/

    展开全文
  • ShardingJDBC实战

    2021-10-30 21:49:39
    # 分片规则(默认取模) spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-...

    目录

    一、背景

    二、优化事项

    三、具体实战

    四、遇到的问题

    五、项目源码地址


    一、背景

    最近在公司手头上的项目单表达到了五千万的规模,而且日增长量每天就有10w左右,一个月就有大概300w的数据,这样一直下去过几个月以后表的数据很容易就上亿了,这样不利于管理以及在大表的情况下,对于表的DDL效率也会相对下降,和几个同事商量了下,于是乎开始做分表的技术优化。

    二、优化事项

    (1)首先先确定使用场景,当前表的使用场景更多的是根据一个具体的标识值去查询,范围查询的场景频率相对低下,在这这种情况下考虑想标识值作为分片键去进行分表。                              具体的算法为:通过标识值通过算法算出具体的时间季度,按季节进行拆分进行拆分,也就是一年

    record_delivery_log

    4个表record_order_log_202101,record_order_log_202102,record_order_log_202103,record_order_log_202104

    拆分前单表数据量为 5000w

    拆分后单表的数据量变成1200w,能够容忍将来4~ 5倍的增长量,符合预期范围。

    (2)调研了对应的分库分表中间件,目前Sharing-jdbc是最主流的中间件,而且社区和文档较完善,故采用Sharing-jdbc作为分表的中间件。

    三、具体实战

    在这里因为公司项目不好复用的原因,用一个模拟项目来模拟这次改造。

    (1)参照sharing-jdbc文档对项目进行改造

    引入sharing-jdbc对应的pom。

     <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
                <version>5.0.0-beta</version>
            </dependency>

    对应的配置文件

    #端口
    server.port=8080
    
    # 数据源ds0
    spring.shardingsphere.datasource.name=ds0
    # 数据源ds0的配置
    spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
    spring.shardingsphere.datasource.ds0.driverClassName=com.mysql.cj.jdbc.Driver
    spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/world1?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
    spring.shardingsphere.datasource.ds0.username=root
    spring.shardingsphere.datasource.ds0.password=123456
    
    # 分片规则,这里只分表,所以仅指定表的分片规则
    spring.shardingsphere.rules.sharding.tables.record_order_log.actual-data-nodes=ds0.record_order_log_$->{2021..2031}0$->{1..4}
    
    # 指定数据库的分片键,只有一个库所以还是用分表的分片键
    spring.shardingsphere.rules.sharding.tables.record_order_log.database-strategy.standard.sharding-column=order_delivery_id
    spring.shardingsphere.rules.sharding.tables.record_order_log.database-strategy.standard.sharding-algorithm-name=database-inline
    
    # 指定分表的分片键
    spring.shardingsphere.rules.sharding.tables.record_order_log.table-strategy.standard.sharding-column=order_delivery_id
    spring.shardingsphere.rules.sharding.tables.record_order_log.table-strategy.standard.sharding-algorithm-name=table-inline
    
    # Omit t_order_item table rule configuration ...
    # ...
    
    # 分片规则(默认取模)
    spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE
    spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds0
    spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.type=CLASS_BASED
    spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.props.strategy=STANDARD
    spring.shardingsphere.rules.sharding.sharding-algorithms.table-inline.props.algorithmClassName=com.cus.shd.sharingjdbc.config.OrderDeliveryIdShardingAlgorithm
    spring.shardingsphere.props.sql.show=true
    
    #mybatis-plus??
    mybatis-plus.mapper-locations=classpath:mappers/*.xml
    mybatis-plus.type-aliases-package=com.cus.shd.sharingjdbc.model
    mybatis-plus.configuration.map-underscore-to-camel-case=true
    # sql??
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    
    #本地数据库链接,忽略了springboot自动加载后失效
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/world1?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
    spring.datasource.username=root
    spring.datasource.password=123456

    注意好分表键设置时候的表名。

    (2)自定义分片键策略,根据order_delivery_id按季度进行存储

    package com.cus.shd.sharingjdbc.config;
    
    import org.apache.commons.lang.StringUtils;
    import org.apache.shardingsphere.sharding.api.sharding.ShardingAutoTableAlgorithm;
    import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
    import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
    import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
    
    import java.time.LocalDateTime;
    import java.time.ZoneOffset;
    import java.util.Collection;
    
    /**
     * @author ASUS
     * @Description 自定义分片策略
     * @Date 2021/11/6 22:20
     **/
    
    public class OrderDeliveryIdShardingAlgorithm implements StandardShardingAlgorithm<Long> {
    
        @Override
        public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
            String orderDeliveryId = shardingValue.getValue().toString();
            orderDeliveryId = orderDeliveryId.substring(0,orderDeliveryId.length() - 4);
            // 将时间戳转为当前时间
            LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(Long.valueOf(orderDeliveryId)/1000, 0, ZoneOffset.ofHours(8));
            String availableTargetName;
            int month = localDateTime.getMonthValue();
            LocalDateTime nowTime = LocalDateTime.now();
            int year = nowTime.getYear();
            if(month >= 1 && month < 3){
                availableTargetName = "01";
            }else if(month >= 3 && month < 6){
                availableTargetName = "02";
            }else if(month >= 6 && month < 9){
                availableTargetName = "03";
            }else {
                availableTargetName = "04";
            }
            if(StringUtils.isEmpty(availableTargetName)){
                return null;
            }
            return String.format("%s_%s%s",shardingValue.getLogicTableName(),year,availableTargetName);
        }
    
        @Override
        public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
            return availableTargetNames;
        }
    
        @Override
        public void init() {
    
        }
    
        @Override
        public String getType() {
            return "ORDER_DELIVERY_ID";
        }
    }
    

    (3)模拟提供两个接口,一个按id查询,一个插入接口。(修改的场景暂时没有,所以不考虑)

     新增的时候做了模拟插入,能够根据分片算法将数据存储到对应的表,达到效果。

    查询同理。

    (4)sharing-jdbc 不会自动的进行创建表,所以需在后台维护一个定时任务,到了一定的季度点就要进行建表操作。(需确保生产环境的应用程序对应的数据库账号是否有建表权限)

    <update id="createNewTable" parameterType="String">
            CREATE TABLE ${tableName} SELECT * FROM record_order_log WHERE 1=2
        </update>

    四、遇到的问题

    1、引入sharing-jdbc包的时候报错了。这里debug到源码发现是mybatisPlus的自动启动器(MybatisPlusAutoConfiguration)有指定单一数据源类(spring中数据源不能有多个实现类)的时候才会启动,因为sharing的引入造成了多数据源(多datasource),所以这个就不会启动了,导致了实例化mapper的时候报错了。解决方案是在SpringBoot的启动类的注解加上

    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,DruidDataSourceAutoConfigure.class})

    忽略掉SpringBoot数据源自动装配以及Druid数据源的自动装配,把所有的数据源实例化交给sharing-jdbc

    2、部分项目存在历史遗留的问题,如果是mybatis或者hibernate的情况下,不想彻底引入sharding-jdbc数据源的话,个人觉得可以使用多数据源的形式来进行改造,去扩展需要使用分表的一些数据库操作,切换对应的sharding数据源进行数据库操作。具体可以参考switchDataSource目录下的一些切换数据源的代码。

    3、给自己的疑问

    忽略了DataSourceAutoConfiguration.class后,sharing-jdbc是如何整合mybatis-plus的?

    答:其实也不难,相当于数据源这个对象原本由SpringBoot自带的数据源自动注入进行注入,现在换成了Sharding的自动装配(ShardingSphereAutoConfiguration)来进行注入,相当于换了整个数据源的一套东西,用的也是sharding整套的东西。

     所以在改造的时候需要检查一下是否对旧的项目存在影响。

    五、项目源码地址

     cus-sharding-jdbc: sharding-jdbc springboot实战

    展开全文
  • ShardingJDBC学习笔记

    2021-10-19 11:00:04
    ShardingJDBC ShardingJdbc准备-Mysql完成主从复制 概述 主从复制(也称AB复制) 允许将来自一个Mysql数据库服务器(主)的数据复制到一个或多个Mysql数据库服务器(从服务器) 复制是异步的从站不需要永久连接以接受...

    ShardingJDBC

    ShardingJdbc准备-Mysql完成主从复制

    概述

    主从复制(也称AB复制) 允许将来自一个Mysql数据库服务器(主)的数据复制到一个或多个Mysql数据库服务器(从服务器)

    复制是异步的从站不需要永久连接以接受来自主站的更新

    Mysql中复制的优点

    • 横向扩展解决方案-在多个从站之间分配负载以提高性能。在此环境中,所有写入和更新都必须在主服务器上进行。但是,读取可以在一个或多个从设备上进行。该模型可以提高写入性能,同时显著提高了越来越多的从设备的读取速度。
    • 数据安全性-因为数据被复制到从站可以暂停复制过程,所以可以在从站上运行备份服务而不会破坏相应的主数据。
    • 分析 - 可以在主服务器上创建实时数据,而信息分析可以在从服务器上进行,而不会影响主服务器的性能。
    • 远程数据分发 -您可以使用复制为远程站点创建数据的本地副本,而无需永久访问主服务器。

    Replication原理

    Replication原理

    主服务器角色的数据库服务器必须是开启二进制日志

    主服务器上面的任何修改都会通过直接的I/O tread(I/O线程)保存在二进制日志 Binary log里面。

    • 从服务器上面也启动一个I/O thread,通过配置好的用户名和密码,连接到主服务器上面请求读取二进制日志,然后把读取到二进制日志写到本地的Realy log里面。
    • 从服务器上面同时开启一个SQL thread定时检查Realy log,如果发现有更新立即把更新的内容在本机的数据库上执行一遍。每个从服务器都会收到主服务器二进制日志的全部内容的副本。
    • 从服务器设备负责决定应该执行二进制日志中的哪些语句。除非另外指定,否则主从二进制日志中的所有事件都在从站执行。如果需要,您可以将从服务器配置为仅处理一些特定数据库或表的事件。

    具体配置

    Master节点配置 /etc/my.cnf

    vim /etc/my.cnf
    [mysqld]
    ##同一局域网内注意要唯一
    server-id=100
    ##开启二进制日志功能
    log-bin=mysql-bin
    ##复制过滤:不需要备份的数据库,不输出(mysql库一般不同步)
    binlog-ignore-db=mysql
    ##为每一个session 分配内存,在事务过程中用二进制日志的缓存
    binlog_cache_size=1M
    ##主从复制的格式(mixed,statement,row,默认是statement)
    binlog_format=mixed
    #进入mysql查询通过命令查看是否配置成功
    mysql -u root -p
    show variables like '%server_id%';
    

    Slave节点配置 /etc/my.cnf

    vim /etc/my.cnf
    [mysqld]
    ##同一局域网内注意要唯一
    server-id=101
    ##开启二进制日志功能,以备Slave作为其他Slave的Master时使用
    log-bin=mysql-slave-bin
    ##realy_log 配置中继日志
    relay_log=edu-mysql-relay-bin
    ##复制过滤:不需要备份的数据库,不输出(mysql库一般不同步)
    binlog-ignore-db=mysql
    ##如果需要同步函数或者存储过程设置为true
    log_bin_trust_function_creators=true
    ##为每一个session 分配内存,在事务过程中用二进制日志的缓存
    binlog_cache_size=1M
    ##主从复制的格式(mixed,statement,row,默认是statement)
    binlog_format=mixed
    ##跳过主从复制中遇到的所有错误或者指定类型错误避免slave端复制终端
    ##如1062错误是指一些主键复制,1032错误是因为主从数据库数据不一致
    slave_skip_errors=1062
    #进入mysql查询通过命令查看是否配置成功
    show variables like '%server_id%';
    

    在master服务器授权slave服务器可以同步权限

    mysql -u -root -p master的密码
    #查看master的二进制文件名称和复制节点位置
    show master status;
    #授予slave服务器可以同步master服务
    grant replication slave,replication client on *.* to 'root'@'slave服务的ip' identified by 'slave服务器的密码'
    ##更新
    flush privileges;
    #查看mysql现在有哪些用户及对应的IP权限(可以不执行,只是一个查看)
    select user,host from mysql.user;
    
    
    ##配置从服务
    change master to master_host='192.168.0.177', master_user='root', master_password='123456',master_port=3307,master_log_file='mysql-bin.000006',master_log_pos=704;
    ##启动主从复制
    start slave;
    ##查看是否启动成功
    show slave status\G;
    

    使用springBoot操作

    配置pom文件

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!--mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!--集成druid连接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.21</version>
            </dependency>
            <!--MyBatis-Plus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.3.1.tmp</version>
            </dependency>
            <!--MyBatis-Plus自动生成器-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>3.3.1.tmp</version>
            </dependency>
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity-engine-core</artifactId>
                <version>2.2</version>
            </dependency>
            <!--sharding-->
            <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-core-common</artifactId>
                <version>4.0.0-RC1</version>
            </dependency>
        </dependencies>
    
     
    
    

    配置yml文件

    server:
      port: 8204
      servlet:
        context-path: /api
    spring:
      shardingsphere:
        #参数配置显示sql
        props:
          sql:
            show: true
        #配置数据源
        datasource:
          #给每个数据源取别名,下面ds1,ds2,ds3任意取
          names: ds1,ds2
          #给master-ds1每个数据源配置数据库连接信息
          ds1:
            #配置druid数据源
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3307/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
            username: root
            password: 123456
            maxPoolSize: 100
            minPoolSize: 5
          #给slave-ds2每个数据源配置数据库连接信息
          ds2:
            #配置druid数据源
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://127.0.0.1:3308/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
            username: root
            password: 123456
            maxPoolSize: 100
            minPoolSize: 5
        #配置默认数据源默认为ds1
        sharding:
          #默认数据源主要用于写操作,注意一定要配置读写分离,如果不配置就会吧所有节点当中slave节点,新增就会报错
          default-data-source-name: ds1
          #配置数据源的读写分离,但是数据库一定要做主从复制
        masterslave:
          name: ms
          #配置主库master,负责数据的写入
          master-data-source-name: ds1
          #配置从库slave节点
          slave-data-source-names: ds2
          #配置slave节点的负载均衡策略,采用轮询 默认是random_robin
          load-balance-algorithm-type: round_robin
        #配置主从名称,可以任意取名称
      servlet:
        multipart:
          max-file-size: 100MB
          max-request-size: 100MB
      jackson:
        date-format: yyyy-MM-dd HH:mm:ss
        time-zone: GMT+8
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    
    

    Mysql分库分表原理

    01、为什么要分库分表

    一般机器为(4核16G),单库的Mysql并发超过了2K,系统基本就完蛋了。最好是并发量控制在1k左右。这里就引出了一个问题为什么要分库分表?

    分库分表的目的:解决高并发,和数据量大的问题。

    1.高并发情况下,会造成IO读写频繁,自然就造成读写缓慢,甚至是宕机。一般单库不要超过2K并发,NB的机器除外。

    2.数据量大的问题。主要由于底层索引实现导致,Mysql的索引实现为B+TREE,数据量大会导致索引树十分庞大,造成查询缓慢。第二,innodb的最大存储限制是64TB。

    要解决上诉问题。最常见做法,就是分库分表。分库分表的目的是将一个表拆成N个表,就是让每个表的数据量控制在一定范围内,保证sql的性能。一个表数据建议不要超过500W。
    请添加图片描述

    02、分库分表

    又分为垂直拆分和水平拆分

    水平拆分: 统一个表的数据拆到不同的库不同的表中。可以根据时间,地区,或者某个业务建维度,也可以通过hash进行拆分,最后通过路由访问到具体的数据。拆分后的每个表结构保持一致。

    **垂直拆分:**就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都要包含部分字段。一般来说,可以根据业务维度进行拆分为订单,订单支持,订单地址,订单商品,订单扩展等表;也可以,根据数据冷热程度拆分,20%的热点字段拆到一个表,80%的冷字段拆到另外一个表。

    请添加图片描述

    03、不停机分库分表数据迁移

    一般数据库的拆分也是有个过程的,一开始是单表,后面慢慢拆分成多表。那么我们就看下如何平滑的从Mysql单表过度到Mysql的分库分表架构。

    1.利用mysql+canal做增量数据同步,利用分库分表中间件,将数据路由到对应的新表中。

    2.利用分库分表中间件,全量数据导入到对应的新表中。

    3.通过单表数据和分库分表数据两两比较,更新不匹配的数据到新表中。

    4.数据稳定后,将单表的配置切换到分库分表配置上。
    请添加图片描述

    04、总结

    垂直拆分:业务模块拆分,商品库,用户库,订单库

    水平拆分:对表进行水平拆分

    表进行垂直拆分:表的字段过多,字段使用频率不一。

    shardingJdbc的分库和分表

    01、逻辑表

    逻辑表是指:水平拆分的数据库或者数据库表的相同路基和数据库结构表的总称。比如用户数据根据用户id%2拆分成为2个表,分别是:user0和user1。他们的逻辑表名是:user。

    在shardingjdbc中的定义方式如下:

    spring:
    	shardingsphere:
    		sharding:
    			tables:
    				#逻辑表名称
    				user:
    

    02、分库分表数据节点-actual-data-nodes

    tables:
    	#逻辑表名称
    	user:
    		#数据节点:多数据源$->{0..N}.逻辑表名称$->{0..N} 相同表
    		actual-data-nodes: ds$->{0..2}.user$->{0..1}
    		#数据节点:多数据源$->{0..N}.逻辑表名称$->{0..N} 不同表
    		actual-data-nodes:ds0.user$->{0..1},ds1.user$->{2..4}
    		#指定单数据源的配置方式
    		actual-data-nodes:ds0.user$->{0..1}
    		#全部手动指定
    		actual-data-nodes:ds0.user0,ds0.user1,ds1.user0,ds1.user1,
    

    数据分片是最小的单元,由数据源名称和数据表组成,比如:ds0.ksd_user0。

    寻找规则:
    请添加图片描述

    03、分库分表5种分片策略

    请添加图片描述

    数据源分片分为两种:

    • 数据源分片
    • 表分片

    这两个是不同维度的分片规则,但是它们能用的分片策略和规则是一样的。他们由两部分构成:

    • 分片建
    • 分片算法

    第一种: none

    对应NoneShardingStragey,不分片策略,SQL会被发给所有节点去执行,这个规则没有子项目可以配置

    第二种:inline 行表达时分片策略(核心,必须掌握)

    对应lnlineShardingStragey。使用Groovy的表达式,提供对SQL语句中的=和in的分片操作支持,只支持单分片建。

    对于简单的分片算法,可以通过简单的配置使用,避免繁琐的java代码开放,如user$(分片键(数据表字段)userid%5)表示根据user表的某一个字段(userid)模5从而分为5张表,表名为:user0到user4。库也是如此

    tables:	#逻辑表名称	user:		#数据节点:多数据源$->{0..N}.逻辑表名称$->{0..N} 相同表		actual-data-nodes: ds$->{0..2}.user$->{0..1}		#拆分库策略,也就是什么样子的数据放入到哪个数据库中		database-strategy:			inline:				sharding-column: userid #分片字段				algorithm-expression: ds$->{userid%3} #分片算法表达式		#拆分表策略,也就是什么样子的数据放入哪个数据表中。		table-strategy:			inline:				sharding-column: age #分片字段				algorithm-expression: user$->{age%2} #分片算法表达式
    

    algorithm-expression行表达式:

    ${begin…end}表示区间范围

    ${[unit1,unit2,…,unitn]}表示枚举值

    行表达式如果出现连续多个 e x p r e s s s i o n 或 {expresssion}或 expresssion->{expression}表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合。
    请添加图片描述

    第三种:根据时间日期-按照标准规则分库分表

    标准分片-Standard

    • 对应StrandardShardingStrategy.提供对SQL语句中=,in和between and 的分片操作支持。
    • StrandardShardingStrategy只支持单片分表键。提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。
    • PreciseShardingAlgorithm是可选的,是用于吃力Between and分片,如果不配置和RangeShardingAlgorithm,SQL的Between And 将按照全库路由处理
     #配置默认数据源默认为ds1    sharding:      #默认数据源主要用于写操作,注意一定要配置读写分离,如果不配置就会吧所有节点当中slave节点,新增就会报错      default-data-source-name: ds0      #配置分表规则      tables:        # 逻辑表名称        db_user:          actual-data-nodes: ds$->{0..1}.db_user$->{0..1}          database-strategy:            standard:              sharding-column: birthday              preciseAlgorithmClassName: com.gf.shardingjdbc.config.BirthdayAlgorithm          table-strategy:            inline:              sharding-column: age              algorithm-expression: db_user$->{age%2}
    
    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;import java.time.LocalDate;import java.time.LocalDateTime;import java.util.*;public class BirthdayAlgorithm implements PreciseShardingAlgorithm<LocalDate> {    List<LocalDate> dateTimes = new ArrayList<>();    {        dateTimes.add(LocalDate.of(2020, 1, 1));        dateTimes.add(LocalDate.of(2021, 1, 1));    }    @Override    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<LocalDate> shardingValue) {        //获取数据库真实的值        LocalDate value = shardingValue.getValue();        //获取数据源的名称和信息列表        Iterator<String> iterator = availableTargetNames.iterator();        String target = null;        for (LocalDate dateTime : dateTimes) {            target = iterator.next();            if (value.isBefore(dateTime)) {                break;            }        }        //返回具体的数据源        return target;    }}
    

    分片日期规则配置

    第四种:ShardingSphere - 符合分片策略

    • 对应接口:HintShardingStrategy。通过Hint而非SQL解析的方式分片策略
    • 对于分片字段非SQL决定,而是由其他外置条件决定的场景,使用SQL hint 灵活的注入分片字段。列如:按照用户登录的时间,主键等进行分库,而数据库没有此字段。SQL hint支持通过javaAPI和SQL注解两种方式使用。让分库分表更加灵活。
      请添加图片描述

    第五种:ShardingSphere -hint分片策略(了解)

    • 对应ComplexShardingStrategy。符合分片策略提供对应SQL语句中-,in和between and的分片操作支持。
    • ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键组合以及分片操作符传至分片算法,完全由开发者自己实现提供最大的灵活度。
      请添加图片描述

    完整案例和配置如下

    准备两个数据库sharding-db。名字相同,两个数据源ds0和ds1。

    每个数据库下方test_user0和test_user1即可。

    数据库规则,性别为偶数的放入ds0库,奇数的放入ds1库。

    数据库规则,年龄为偶数的放入test_user0,奇数放入test_user1库中。

    server:  port: 8204  servlet:    context-path: /apispring:  shardingsphere:    #参数配置显示sql    props:      sql:        show: true    #配置数据源    datasource:      #给每个数据源取别名,下面ds1,ds2,ds3任意取      names: ds0,ds1      #给master-ds1每个数据源配置数据库连接信息      ds0:        #配置druid数据源        type: com.alibaba.druid.pool.DruidDataSource        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://127.0.0.1:3307/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true        username: root        password: 123456        maxPoolSize: 100        minPoolSize: 5      #给slave-ds2每个数据源配置数据库连接信息      ds1:        #配置druid数据源        type: com.alibaba.druid.pool.DruidDataSource        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://127.0.0.1:3308/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true        username: root        password: 123456        maxPoolSize: 100        minPoolSize: 5    #配置默认数据源默认为ds1    sharding:      #默认数据源主要用于写操作,注意一定要配置读写分离,如果不配置就会吧所有节点当中slave节点,新增就会报错      default-data-source-name: ds0      #配置分表规则      tables:        # 逻辑表名称        db_user:          actual-data-nodes: ds$->{0..1}.db_user$->{0..1}          database-strategy:            inline:              sharding-column: age              algorithm-expression: ds$->{age%2}          table-strategy:            inline:              sharding-column: age              algorithm-expression: db_user$->{age%2}#    #配置数据源的读写分离,但是数据库一定要做主从复制#    masterslave:#      name: ms#      #配置主库master,负责数据的写入#      master-data-source-name: ds1#      #配置从库slave节点#      slave-data-source-names: ds2#      #配置slave节点的负载均衡策略,采用轮询 默认是random_robin#      load-balance-algorithm-type: round_robin  servlet:    multipart:      max-file-size: 100MB      max-request-size: 100MB  jackson:    date-format: yyyy-MM-dd HH:mm:ss    time-zone: GMT+8mybatis-plus:  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    shardingJdbc分布式主键配置

    shardingJdbc提供灵活的配置分布式主键生成策略方式。在分片规则配置模块每个表的主键生成策略。默认使用学花算法。(snowflake)生成bit的长整形数据。支持两种配置。

    • SNOWFLAKE
    • UUID

    切记:主键列不能自增长数据类型是:bigint(20)

    spring:	shardingsphere:		sharding:			tables:				test_user:					key-generator:						column: id						type: SNOWFLAKE
    

    根据年月来进行分片

    inline 方式yml

    server:  port: 8204  servlet:    context-path: /apispring:  shardingsphere:    #参数配置显示sql    props:      sql:        show: true    #配置数据源    datasource:      #给每个数据源取别名,下面ds1,ds2,ds3任意取      names: ds0,ds1      #给master-ds1每个数据源配置数据库连接信息      ds0:        #配置druid数据源        type: com.alibaba.druid.pool.DruidDataSource        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://127.0.0.1:3307/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true        username: root        password: 123456        maxPoolSize: 100        minPoolSize: 5      #给slave-ds2每个数据源配置数据库连接信息      ds1:        #配置druid数据源        type: com.alibaba.druid.pool.DruidDataSource        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://127.0.0.1:3308/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true        username: root        password: 123456        maxPoolSize: 100        minPoolSize: 5    #配置默认数据源默认为ds1    sharding:      #默认数据源主要用于写操作,注意一定要配置读写分离,如果不配置就会吧所有节点当中slave节点,新增就会报错      default-data-source-name: ds0      #配置分表规则      tables:        # 逻辑表名称        db_user:          key-generator:            column: id            type: SNOWFLAKE          actual-data-nodes: ds$->{0..1}.db_user_$->{2021..2022}${(1..3).collect{t ->t.toString().padLeft(2,'0')}          database-strategy:            inline:              sharding-column: id              algorithm-expression: ds$->{id%2}          table-strategy:            #使用inline的方法            inline:              sharding-column: birthday              algorithm-expression: db_user_$->{birthday}#    #配置数据源的读写分离,但是数据库一定要做主从复制#    masterslave:#      name: ms#      #配置主库master,负责数据的写入#      master-data-source-name: ds1#      #配置从库slave节点#      slave-data-source-names: ds2#      #配置slave节点的负载均衡策略,采用轮询 默认是random_robin#      load-balance-algorithm-type: round_robin  servlet:    multipart:      max-file-size: 100MB      max-request-size: 100MB  jackson:    date-format: yyyy-MM-dd HH:mm:ss    time-zone: GMT+8mybatis-plus:  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    standard 方式yml

    server:  port: 8204  servlet:    context-path: /apispring:  shardingsphere:    #参数配置显示sql    props:      sql:        show: true    #配置数据源    datasource:      #给每个数据源取别名,下面ds1,ds2,ds3任意取      names: ds0,ds1      #给master-ds1每个数据源配置数据库连接信息      ds0:        #配置druid数据源        type: com.alibaba.druid.pool.DruidDataSource        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://127.0.0.1:3307/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true        username: root        password: 123456        maxPoolSize: 100        minPoolSize: 5      #给slave-ds2每个数据源配置数据库连接信息      ds1:        #配置druid数据源        type: com.alibaba.druid.pool.DruidDataSource        driver-class-name: com.mysql.cj.jdbc.Driver        url: jdbc:mysql://127.0.0.1:3308/test_user?serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true        username: root        password: 123456        maxPoolSize: 100        minPoolSize: 5    #配置默认数据源默认为ds1    sharding:      #默认数据源主要用于写操作,注意一定要配置读写分离,如果不配置就会吧所有节点当中slave节点,新增就会报错      default-data-source-name: ds0      #配置分表规则      tables:        # 逻辑表名称        db_user:          key-generator:            column: id            type: SNOWFLAKE          actual-data-nodes: ds$->{0..1}.db_user_$->{2021..2022}${(1..3).collect{t ->t.toString().padLeft(2,'0')}          database-strategy:            inline:              sharding-column: id              algorithm-expression: ds$->{id%2}          table-strategy:            #使用inline的方法#            inline:#              sharding-column: birthday#              algorithm-expression: db_user_$->{birthday}            #使用standard的方法               standard:                sharding-column: birthday                preciseAlgorithmClassName: com.gf.shardingjdbc.config.YearMonthShardingAlgorithm#    #配置数据源的读写分离,但是数据库一定要做主从复制#    masterslave:#      name: ms#      #配置主库master,负责数据的写入#      master-data-source-name: ds1#      #配置从库slave节点#      slave-data-source-names: ds2#      #配置slave节点的负载均衡策略,采用轮询 默认是random_robin#      load-balance-algorithm-type: round_robin  servlet:    multipart:      max-file-size: 100MB      max-request-size: 100MB  jackson:    date-format: yyyy-MM-dd HH:mm:ss    time-zone: GMT+8mybatis-plus:  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    策略类

    import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;import java.util.Collection;/** * <p> * * </p> * * @author gf * @date 2021/10/1415:34 */public class YearMonthShardingAlgorithm implements PreciseShardingAlgorithm<String> {    @Override    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {        return shardingValue.getLogicTableName() + "_" + shardingValue.getValue();    }}
    

    ShardingJdbc的事务管理

    数据库事务需要满足ACID(原子性,一致性,隔离性,持久性)四个特性。

    • 原子性(Atomicity)指事务作为整体来执行,要么全部执行要么全部不执行。
    • 一致性(Consistency)指事务应确保数据从一个一致的状态转变为另一个一致的状态。
    • 隔离性(Isolation) 指多个事务并发执行时,一个事务的执行不影响其他事务执行。
    • 持久性(Durability)指已提交的事务修改数据会被持久保存。

    在单一数据节点中,事务权限对于单一数据库资源的访问控制,称之为本地事务。几乎所有的成熟的关系型数据库提供了对本地事务的原生支持。但是在基于微服务的分布式应用环境下,越来越多的应用场景要求对于多个服务的访问及其相对应的多个数据库资源能纳入到同一个事务当中,分布式事务应运而生。

    关系型数据库虽然对本地事务提供了完整的ACID原生支持。但在分布式场景下,它却成为系统性能的桎梏。如何让数据库在分布式场景下满足ACID的特性或找寻相应的代替方案,是分布式事务的重点工作。

    本地事务

    在不开启任何分布式事务管理器的前提下,让每个数据节点各自管理自己的事务。它们之间没有协调以及通信的能力,也并不互相知晓其他数据节点事务的成功与否。本地事务在性能方面无任何损耗,但在强一致性方面则力不从心。

    • 完全支持非跨库事务,例如:仅分表,或分库但是路由的结果在单库中。
    • 完全支持因逻辑异常导致的跨库事务。例如:同一事务中,跨两个库更新。更新完毕后,抛出空指针,则两个库的内容都能回滚。
    • 不支持因网络、硬件异常导致的跨库事务。例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库宕机,则只有第二个库数据提交。

    两阶段提交

    XA协议最早的分布式事务模型是由X/Open Distributed Transaction Processing(DTP)模型,简称XA协议。

    基于XA协议实现的分布式事务对业务侵入很小,它最大的优点就是对使用方透明,用户可以像使用本地事务一样基于XA协议分布式事务,XA协议能够严格保障事务ACID特性。

    严格保障事务ACID特性是一种双刃剑,事务执行过程中需要资源全部锁定,他更加适用于执行时间确定的短事务。对于事务来说,整个事务进行期间对数据的独占,将导致热点数据依赖业务系统并发性能衰退明显。因此,在高并发的性能至上场景中,基于XA协议的分布式事务并不是最佳选择。

    • 支持数据分片后的跨库XA事务
    • 两阶段提交保证操作的原子性和数据的强一致性
    • 服务宕机重启后,提交/回滚中的事务可自动恢复
    • SPI机制整合主流的XA事务管理器,默认Atomikos,可以选择使用Narayana和Bitronix
    • 同时支持XA和非XA的连接池
    • 提供spring-boot和namespace的接入端

    柔性事务

    如果将实现ACID的事务要素的事务称之为刚性事务,那么基于BASE事务要素则称之为柔性事务,BASE是基本可用,柔性状态和最终一致性这三个要素的缩写。

    基本可用(Basically Availabel) 保证分布式事务参与方不一定同时在线。

    柔性状态(Solt state)则允许系统状态更新有一定的延时,这个延时对客户来说不一定能察觉。

    最终一致性(Eventually consistent) 通常是通过消息传递的方式保证系统的最终一致性。

    在ACID事务中对隔离性的要求很高,在事务执行过程中,必须所有的资源锁定。柔性事务的理念则是通过业务逻辑将互斥锁操作从资源层面上移至业务层面,通过放宽对强一致性要求,来换取系统吞吐量的提升。

    柔性事务-Saga

    • 完全支持跨库事务
    • 支持失败SQL重试及最大努力送达
    • 支持反向SQL、自动生成更新快照以及自动补偿
    • 默认使用关系型数据库进行快照及事务日志的持久化,支持使用SPI的方式加载其他类型的持久化

    柔性事务-SEATA

    • 完全支持跨库分布式事务
    • 支持RC隔离级别
    • 通过undo快照进行事务回滚
    • 支持服务宕机后的,自动恢复提交中的事务

    导入shardingjdbc包

     <!--sharding 事务-->        <dependency>            <groupId>io.shardingsphere</groupId>            <artifactId>sharding-transaction-spring-boot-starter</artifactId>            <version>3.1.0</version>        </dependency>
    

    在业务代码中加上注解

        @Transactional(rollbackFor = Exception.class)    @ShardingTransactionType(TransactionType.XA)
    
    展开全文
  • 人工创建两张表,t_order_1和t_order_2,这两张表是订单表拆分后的表,通过Sharding-Jdbc向订单表插入数据, 按照一定的分片规则,主键为偶数的进入t_order_1,另一部分数据进入t_order_2,通过Sharding-Jdbc 查询...
  • Sharding Proxy 实战

    2021-07-27 23:58:18
    文章目录单纯的 MySQL 实现建表 SQL下载并配置 sharding porxy添加 mysql 驱动修改 conf 下 server.yaml 如下修改 conf 下 sharding.yaml 如下启动 bin 下的 start.sh访问测试代理数据库附加Sharding Proxy + Spring...
  • sharding-jdbc在目前微服务和各种saas软件等数据量日益加大的情况下变得使用的人多了起来。 sharding-jdbc读写分离测试 数据库实体 @Data @Table(name = "city") @Entity public class City implements ...
  • JeecgBoot集成Sharding-JDBC

    2021-12-15 14:03:39
    1,环境描述 本次测试基于JeecgBoot 2.4.6,测试代码在Jeecg-boot-module-system中编写。 2,引入坐标 ...sharding-jdbc-spring-boot-starter</artifactId> <version>4.1.1</version
  • sharding-jdbc教程

    2021-01-20 08:56:01
    sharding-jdbc教程 ​ Sharding-JDBC是ShardingSphere的第一个产品,也是ShardingSphere的前身。 它定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外...
  • 1. ShardingJDBC 1.1 分库分表方式回顾 1) 分库分表的目的就是将我们的单库的数据控制在合理范围内,从而提高数据库的性能. 垂直拆分( 按照结构分 ) 垂直分表: 将一张宽表(字段很多的表), 按照字段的访问频次进行...
  • SSM项目引入sharding JDBC进行分表 注意点: 由于各个版本差别比较大,配置方式差别也特别大,请根据官方文档进行配置! 官方配置路径:...
  • 前言:上一篇博客我用AOP+AbstractRoutingDataSource实现了MySQL读写分离...使用Sharding-JDBC配置MySQL读写分离,优点在于数据源完全有Sharding-JDBC托管,写操作自动执行master库,读操作自动执行slave库。不需要程序
  • 一、前言 SpringBoot(48) 整合sharding-jdbc实现分库分表与读写分离 本文将通过自定义算法来实现定制化的分库分表来扩展相应业务 二、简介 1、分片键 用于数据库/表拆分的关键字段 ex: 用户表根据user_id取模拆分到...
  • ShardingJDBC ShardingJDBC可以理解为一个加强版的jdbc 可以使用它进行数据库的连接与执行命令 ShardingJDBC可以帮我们实现分库分表的功能 实现功能有几个概念 逻辑表:逻辑表就是ShardingJDBC操作的表具体数据会...
  • Sharding-JDBC入门

    2021-01-30 21:31:21
    Sharding-JDBC入门 ​ 最近在做一个java后台重构项目时碰到了一个问题,就是系统的数据量越来越大,单表的压力剧增,已经不能满足正常的数据操作了。这个时候就需要有分库分表的处理了,由于资源和业务限制,最终只...
  • ShardingJDBC实战配置

    2021-09-25 13:44:30
    shardingjdbc的核心功能是数据分片和读写分离,通过ShardingJDBC,应用可以 透明的使用JDBC访问已经分库分表、读写分离的多个数据源,而不用关心数据源的 数量以及数据如何分布 先了解几个概念 逻辑表:水平拆分的...
  • sharding-jdbc使用教程概念分库分表Sharding-JDBC逻辑表真实表数据节点环境要求Sharding-JDBC配置行表达式代码项目依赖配置数据库初始化源码片段代码结构代码清单测试类运行结果 概念 分库分表 分库:从单个数据库...
  • 一、shardingJDBC和sharding-Proxy的区别 关于shardingJDBC,我已经写过一篇文章可以参考,那么两者的区别到底在哪? Sharding-JDBC Sharding-JDBC 采用在 JDBC 层扩展分库分表,支持读写分离,是一个以 jar 形式...
  • order.table-strategy.standard.sharding-column=order_id spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=t-order-inline spring.shardingsphere.rules....
  • sharding-JDBC学习日记

    2021-05-24 19:53:12
    笔记由来:尚硅谷 推荐课程:尚硅谷...2.有三个产品分别为:Sharding-JDBC 、 Sharding-Proxy和ShardingSphere-Scaling(Alpha)。 3.定位为关系型数据库中间件,合理在分布式环境下使用关系型数据库操作。 1.2
  • sharding-jdbc官方文档:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/encrypt/
  • >{0..3} sharding.jdbc.config.sharding.tables.user.table-strategy.standard.sharding-column: id sharding.jdbc.config.sharding.tables.user.table-strategy.standard.precise-algorithm-class-name: ...
  • Sharding-JDBC实际使用方法一、下面简单讲解一下sharding-JDBC的实际用法?1.水平分表2.水平分库3.垂直分库总结 一、下面简单讲解一下sharding-JDBC的实际用法? 1.水平分表 水平分表是在同一个数据库内 ,把同一个...
  • SpringBoot 整合sharding jdbc实现分库分表
  • Sharding-JDBC动态水平分表实现背景:在项目中遇到了按照日期动态水平分表的需求,系统属于监控系统,每10分钟保存一次监控数据,并且每次要采集200个节点上的数据,即每次采集数据(间隔10分钟),向数据表添加200条...
  • SpringBoot 整合 Mybatis Plus Sharding-JDBC
  • Sharding-JDBC自定义复合分片算法一、背景二、需求1、对于客户端操作而言2、对于运营端操作而言三、分片算法1、客户id和订单id的生成规则2、 确定数据落在那个表中3、举例说明1、客户id确定数据表2、订单id确定数据...
  • 这中间存在主从服务之间的执行延时,如果主服务器的压力很大,则可能导致主从服务器延时较大 3、Sharding-Jdbc实现读写分离 1)、新建Springboot工程,引入相关依赖 org.springframework.boot spring-boot-starter-...
  • Sharding-JDBC 是 ShardingSphere 开源的分布式数据库中间件产品之一,提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。 Sharding-JDBC 在 Java ...
  • SpringBoot sharding 分库分表使用及配置 官方提供配置方式有哪些? JAVA配置 YAML配置 SpringBoot配置 Spring命名空间配置 无论哪种方式最后底层都包装成JAVA代码方式配置,今天主要以SpringBoot-sharding自动状态...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 57,627
精华内容 23,050
关键字:

sharding