SpringBoot整合Sharding水平分库(三)

2022-12-04 12:28:43 浏览数 (1)

上一篇文章阿粉已经实现了数据库进行分表的操作,而且也成功了,如果有想看的,可以看一下上一天的文章,使用SpringBoot整合 Sharding-JDBC 实现了单数据库分表保存数据和查询不同表中的数据。今天我们就来实现一下分库,并且分表,然后同样的执行保存数据和查询数据的操作。

水平分库分表

水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。阿粉之前趁着活动入手了2个最低配置的服务器,一个是阿里云的,一个是百度云的,只是做开发用的,虽然每次执行点东西都能让内存爆满,但是自己做开发测试啥的,也是没啥问题的,有兴趣的可以安排一下。在上面装好我们的数据库之后,我们就可以开始进行操作了。

第一步

创建数据库,我们分别在不同的两个数据库中创建相同表结构的两个表数据。

database1中,我们创建一个orderinfo的表

代码语言:javascript复制
DROP TABLE IF EXISTS orderinfo;
CREATE TABLE orderinfo (
order_id BIGINT(20) PRIMARY KEY AUTO_INCREMENT ,
user_id INT(11) ,
product_name VARCHAR(128),
COUNT INT(11)
);

database2中,我们创建同样的库,创建完成校验一下。

两个表的结构是一样的

接下来就去创建我们的SpringBoot项目,这个阿粉不说了,上一篇文章已经说过了,也是需要加入相同的依赖包。

第二步

更改配置:

代码语言:javascript复制
spring:
  application:
    name: sharding-jdbc-simple
  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true
  main:
    allow-bean-definition-overriding: true

  #定义数据源
  shardingsphere:
    datasource:
      names: db1,db2
      db1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/order?characterEncoding=UTF-8&useSSL=false
        username: root
        password: 123456
      db2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/ordersharding?characterEncoding=UTF-8&useSSL=false
        username: root
        password: 123456
        ## 分库策略,以user_id为分片键,分片策略为user_id % 2   1,user_id为偶数操作db1数据源,否则操作db2。
    sharding:
      tables:
        orderinfo:
          actual-data-nodes: db$->{1..2}.orderinfo
          key-generator:
            column: order_id
            type: SNOWFLAKE
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: db$->{user_id % 2   1}
    props:
      sql:
        show: true
server:
  servlet:
    context-path: /sharding-jdbc
mybatis:
  configuration:
    map-underscore-to-camel-case: true


我们的配置文件,在这里是通过配置对数据库的分片策略,来指定数据库进行操作。

分库策略,以user_id为分片键,分片策略为user_id % 2 1,user_id为偶数操作db1数据 源,否则操作db2。

这样的分库策略,直接通过 user_id 的奇偶性,来判断到底是用哪个数据源,用哪个数据库和表数据的。

接下来我们直接写Junit测试来测试一下。

代码语言:javascript复制
    @Autowired
    OrderDao orderDao;

    @Test
    public void TestInsertShardingDao(){
        for (int i = 0; i < 10; i  ) {
            orderDao.insertOrder(i,"大电视",1);
        }
    }

如果是单独看日志的话,看样子是成功了,那么我们得实际来验证一下这个内容。

这么看下来,我们保存的数据是没问题的,从水平切分来看,我们把数据分别保存了database1 和database2 库中的 orderinfo 里面,也就是说,我们的数据算是水平切分到了不同的数据库对应的表中。

接下来我们是不是就得去执行查询了?

分库分表后的查询

我们直接查询:

代码语言:javascript复制
    @Test
    public void TestQueryShardingDao(){
        List<Long> ids = new ArrayList<>();
        ids.add(743430896454991873L);
        ids.add(743430897486790656L);
        List<Map> mapList = orderDao.findOrderByIds(ids); System.out.println(mapList);
    }
    
    
      /**
     * 根据ID 查询订单
     * */
    @Select({"<script>" 
            "select * from orderinfo p where p.order_id in "  
                    "<foreach collection='orderIds' item='id' open='(' separator = ',' close=')'>#{id}</foreach>"
             "</script>"})
    List<Map> findOrderByIds(@Param("orderIds") List<Long> orderIds);
    

我们直接看返回结果

代码语言:javascript复制
[{user_id=0, COUNT=1, order_id=743430896454991873, product_name=大电视}, {user_id=3, COUNT=1, order_id=743430897486790656, product_name=大电视}]

这个样子看起来,我们水平分库分表拆分,是不是就完成了?

在这里,既然实战结束了,阿粉就得开始说说这个配置了。

在说配置之前,我们得先了解一下关于Sharding-JDBC的执行流程,不然我们也不知道这些配置都是干嘛用的。

当我们把SQL发送给 Sharding 之后,Sharding 会经过五个步骤,然后给我们返回接口,这五个步骤分别是:

  • SQL解析
  • SQL路由
  • SQL改写
  • SQL执行
  • 结果归并

SQL解析:编写SQL查询的是逻辑表, 执行时 ShardingJDBC 要解析SQL ,解析的目的是为了找到需要改写的位置。

SQL路由: SQL的路由是指 将对逻辑表的操作,映射到对应的数据节点的过程. ShardingJDBC会获取分片键判断是否正确,正确 就执行分片策略(算法) 来找到真实的表。

SQL改写: 程序员面向的是逻辑表编写SQL, 并不能直接在真实的数据库中执行,SQL改写用于将逻辑 SQL改为在真实的数据库中可以正确执行的SQL。

SQL执行: 通过配置规则 order_$->{order_id % 2 1} ,可以知道当 order_id 为偶数时 , 应该向 order_1表中插入数据, 为奇数时向 order_2表插入数据。

结果归并:将所有真正执行sql的结果进行汇总合并,然后返回。

我们都知道,要是用Sharding分库分表,那么自然就会有相对应的配置,而这些配置才是比较重要的地方,而其中比较经典的就是分片策略了。

分片策略

分片策略分为分表策略和分库策略,它们实现分片算法的方式基本相同,但是在阿粉看来,好像没有太大的区别,无非一个是针对库,一个是针对表。

而一般分片策略主要是分为如下的几种:

  • standard:标准分片策略
  • complex:复合分片策略
  • inline:行表达式分片策略,,使用Groovy的表达式.
  • hint:Hint分片策略,对应HintShardingStrategy。
  • none:不分片策略,对应NoneShardingStrategy。不分片的策略。

那么什么是标准的分片策略呢?

标准分片策略StandardShardingStrategy

使用场景:SQL 语句中有>,>=, <=,<,=,IN 和 BETWEEN AND 操作符,都可以应用此分片策略。

也就是说,你的 SQL 语句中频繁的出现这些符号的时候,而且这个时候你还想要进行分库分表的时候,就可以采用这个策略了。

但是这个时候要谨记一些内容,那就是标准分片策略(StandardShardingStrategy),它只支持对单个分片键(字段)为依据的分库分表,并提供了两种分片算法 PreciseShardingAlgorithm(精准分片)和 RangeShardingAlgorithm(范围分片)。

在使用标准分片策略时,精准分片算法是必须实现的算法,用于 SQL 含有 = 和 IN 的分片处理;范围分片算法是非必选的,用于处理含有 BETWEEN AND 的分片处理。

复合分片策略

使用场景:SQL 语句中有>,>=, <=,<,=,IN 和 BETWEEN AND 等操作符,不同的是复合分片策略支持对多个分片键操作。

这里要注意的就是多个分片键,也就是说,如果我们分片的话需要使用两个字段作为分片键,自定义复合分片策略。

行表达式分片策略

它的配置相当简洁,这种分片策略利用inline.algorithm-expression书写表达式。

阿粉就是使用的这个,来完成的分片,而且行表达式分片策略适用于做简单的分片算法,无需自定义分片算法,省去了繁琐的代码开发,是几种分片策略中最为简单的。

但是要注意,行表达式分片策略,它只支持单分片键。

Hint分片策略

Hint分片策略(HintShardingStrategy)和其他的分片策略都不一样了,这种分片策略无需配置分片键,分片键值也不再从 SQL中解析,而是由外部指定分片信息,让 SQL在指定的分库、分表中执行。

不分片策略

不分片策略这个没啥可说的,你不分片的话,用Sharing-JDBC的话,可能就没啥意思了。毕竟玩的就是分片。

在这里,阿粉想说,没有最好的分片策略,有些公司看重学习成本,有些公司看重实际应用,只能说选择对你们公司业务最优的才是最好的。下一篇文章阿粉再来说一下这个垂直分库分表实战。希望大家点个赞,支持一波阿粉,万分感谢!

文章参考

拉勾Sharding-JDBC讲解实战

0 人点赞