前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据库中间件分片算法之date

数据库中间件分片算法之date

原创
作者头像
BuddyYuan
修改2020-01-09 10:22:05
1.8K0
修改2020-01-09 10:22:05
举报
文章被收录于专栏:数据库中间件数据库中间件
前言

今天看了一个视频是关于梅西和C罗的,视频的主题是问他们两个是不是朋友?结果可想而知,两个人私底下很较劲,梅西投票从来没投给过C罗,C罗也从来没投过梅西,2个人在足球场上争斗了10年。今天你方唱罢,明天我方登场。如果你的一生中没有一个劲敌,自身前进的动力就会受影响。所以我们也要在自己的工作领域中找一个尊敬的对手,没事和他斗上一斗。

火钳刘明。今天我们来介绍date分区算法

1.hash分区算法

2.stringhash分区算法

3.enum分区算法

4.numberrange分区算法

5.patternrange分区算法

6.date分区算法

7.jumpstringhash算法

date分区算法的配置
代码语言:txt
复制
    <tableRule name="rule_date">
        <rule>
            <columns>create_date</columns>
            <algorithm>func_date</algorithm>
        </rule>
    </tableRule>

<function name="func_date" class="Date">
      <property name="dateFormat">yyyy-MM-dd</property>
      <property name="sBeginDate">2015-01-01</property>
      <property name="sEndDate">2015-01-31 </property> <!--if not set sEndDate,then in fact ,the sEndDate = sBeginDate+ (node size)*sPartionDay-1 -->
      <property name="sPartionDay">10</property>
      <property name="defaultNode">0</property><!--the default is -1-->
</function>

和之前的算法一样。需要在rule.xml中配置tableRule和function。

  • tableRule标签,name对应的是规则的名字,而rule标签中的columns则对应的分片字段,这个字段必须和表中的字段一致。algorithm则代表了执行分片函数的名字。
  • function标签,name代表分片算法的名字,算法的名字要和上面的tableRule中的<algorithm>标签相对应。class:指定分片算法实现类。此处需要填写为“date”或者“com.actiontech.dble.route.function.PartitionByDate"的分区规则,property指定了对应分片算法的参数。不同的算法参数不同。
  • dateFormat:指定日期的格式。
  • sBeginDate:指定日期的开始时间。
  • sEndDate:指定日期的结束时间。可以不配置或配置为空("")。
  • sPartionDay:指定分区的间隔,单位是天。
  • defaultNode:指定默认节点号。默认值为-1,不指定默认节点。
算法的工作模式

这个算法有两种工作模式:

1.模式1为带状模式。不需要定义sEndDate或者将sEndDate可以配置为""。这种模式只定义了开始时间,没有定义结束时间,它会以sPartionDay为间隔,进行区间划分,每个区间对应一个数据节点,然后可以一直循环无限增加下去。

真的是这样吗?,其实不是,sEndDate在不定义的情况下,和配置schema.xml有关。就是在schema.xml中我们会预先设置分片数。那么实际sEndDate是等于下面的结果的。

sEndDate=sBeginDate+(nodesize)∗sPartionDay−1

2.模式2为环形模式。定义sEndDate且不为""。这种模式定义了开始时间,也定义了结束时间,它以sPartionDay为间隔进行区间划分,划分为N个区间,每个区间对应一个数据节点。当执行一条SQL语句的时候,如果分区字段key的值小于结束时间值,则和模式1一样落在指定分区中。而一旦key的值大于结束时间。则要进行取模运算。通过取摸运算得出的分区号。

计算公式为:

index=((key−sBeginDate)/sPartionDay)%n
1.启动加载配置

在启动dble之后,就会读取rule.xml文件,加载sBeginDate来确定起始时间,如上面配置就会加载2015-01-01,确定为起始时间,然后读取sPartionDay来确定每个MySQL分片承载多少天内的数据。如上面配置10天的数据存放在一个分片上。读取dateFormat来确定日期格式,这里的日期格式为'yyyy-mm-dd'。

2.运行过程

如果有用户通过where查询create_date='2015-01-21'的时候,就会访问date分片算法,将where条件的值取出来尝试转换成Java内部的时间类型。然后求这个值与起始时间的差,在除以指定分区的间隔,确定所属的分片。

4.我们建表来测试一下
4.1 在rule.xml配置下列内容
代码语言:txt
复制
<tableRule name="rule_date">
    <rule>
        <columns>create_date</columns>
        <algorithm>func_date</algorithm>
    </rule>
</tableRule>

<function name="func_date" class="Date">
      <property name="dateFormat">yyyy-MM-dd</property>
      <property name="sBeginDate">2015-01-01</property>
      <property name="sEndDate">2015-01-31 </property> <!--if not set sEndDate,then in fact ,the sEndDate = sBeginDate+ (node size)*sPartionDay-1 -->
      <property name="sPartionDay">10</property>
      <property name="defaultNode">0</property><!--the default is -1-->
</function>
4.2 在schema.xml配置下列内容
代码语言:txt
复制
<table name="test_date" primaryKey="id" rule="rule_date" dataNode="dn1,dn2,dn3,dn4"/>
4.3 登录管理端口,reload配置
代码语言:txt
复制
[root@mysql5 ~]# mysql -uman1 -p -P9066 -h192.168.56.185 -p654321

mysql> reload @@config;
Query OK, 1 row affected (0.39 sec)
Reload config success
4.3 然后使用服务端口登录到dble上执行建表测试语句。

可以发现,我们插入了四条数据,分别是2015-01-07,2015-01-17,2015-01-27,2015-02-25。前面三条数据都小于我们的sEndDate。然后起始时间是2015-01-01,间隔是10,那么对应的分片情况就应该如下所示:

index 0: 2015-01-01,2015-01-10 ===>2015-01-07

index 1: 2015-01-11,2015-01-20 ===>2015-01-17

index 2: 2015-01-20,2015-01-30 ===>2015-01-27

index 3: 2015-01-31,2015-02-09

我们来看看源代码是如何实现的。

代码语言:txt
复制
public Integer calculate(String columnValue) {
    try {
        if (columnValue == null || "null".equalsIgnoreCase(columnValue)) {
            if (defaultNode >= 0) {
                return defaultNode;
            }
            return null;
        }
        long targetTime = formatter.get().parse(columnValue).getTime();
        if (targetTime < beginDate) {
            return (defaultNode >= 0) ? defaultNode : null;
        }
        int targetPartition = (int) ((targetTime - beginDate) / partitionTime);
        if (targetTime > endDate && nCount != 0) {
            targetPartition = targetPartition % nCount;
        }
        return targetPartition;
    } catch (ParseException e) {
        throw new IllegalArgumentException("columnValue:" + columnValue + " Please check if the format satisfied.", e);
    }
}

从代码中可以看出总共两种计算方式,输入时间大于beginDate小于等于endDate。就按照下面方式计算。

int targetPartition = (int) ((targetTime - beginDate) / partitionTime);

输入时间大于endDate就是上面计算出来的值在求模

targetPartition = targetPartition % nCount;

而这里需要注意一点的就是partitionTime被设置成了等于sPartionDay的值*86400000微秒,因为一天正好是86400000微秒。

private static final long ONE_DAY = 86400000;

partitionTime = Integer.parseInt(sPartionDay) * ONE_DAY;

那么当我们插入2015-02-25,就先将字符串转换成时间,时间在转换成long类型。然后相减在除以864000000(这里要乘以sPartionDay,也就是10天)。做下列运算得出来的值是5,然后5在对nCount(分区数)取模,最后得出结果是1,然后该数据就会落到分片2上(索引从0开始计算)。如果分区字段值小于sBeginDate,则会检查是否设置了defaultNode。设置了就会落到defaultNode上,没有设置就会报错。我们来写段java验证这个算法。

代码语言:txt
复制
[root@mysql5 ~]# more test1.java
import java.text.SimpleDateFormat;
import java.util.Date;
public class test1 {
public static void main(String[] args) throws Exception
{
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        long startDate=sdf.parse("2015-01-01").getTime();
        long endDate=sdf.parse("2015-01-31").getTime();
        long tarDate=sdf.parse("2015-02-25").getTime();
        
        int nCount = (int) ((endDate - startDate) / 864000000) + 1;
        int targetPartition = (int) ((tarDate - startDate) / 864000000);

        System.out.println(nCount);
        System.out.println(targetPartition);
}
}
[root@mysql5 ~]# java test1
4
5
注意事项:
  1. 分片字段必须是符合dateFormat的日期字符串。
  2. 区间划分不以日历时间为准,无法对应到日历时间。内部进行区间划分时,会将sPartionDay转化为以86400000毫秒为一天进行运算。
  3. 在模式2的情况下, 如果(sEndDate - sBeginDate)不是sPartionDay的整数倍,则索引号为0的数据节点承载更多的数据。
  4. 分片字段为NULL时,数据落在defaultNode节点上,若此时defaultNode没有配置,则会报错;当真实存在于mysql的字段值为not null的时候,报错 "Sharding column can't be null when the table in MySQL column is not null"
后记

今天就介绍到这里,在遇到一些小疑惑的时候通过看源码解决了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • date分区算法的配置
  • 算法的工作模式
  • 1.启动加载配置
  • 2.运行过程
  • 4.我们建表来测试一下
    • 4.1 在rule.xml配置下列内容
      • 4.2 在schema.xml配置下列内容
        • 4.3 登录管理端口,reload配置
          • 4.3 然后使用服务端口登录到dble上执行建表测试语句。
          • 注意事项:
          • 后记
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档