社区投稿 | DBLE 自定义拆分算法
文章来源:爱可生开源社区
作者:钟悦
作者简介
钟悦,就职于某大型国有银行,多年从事MySQL和分布式中间件的方案设计与实施工作;资深MySQL数据库专家,架构师;DBLE开源项目积极贡献者。
文章概要
DBLE默认支持数十种数据拆分算法,基本能满足大部分的社区用户的使用需求;为了满足更广的业务场景,DBLE还支持更加灵活的自定义拆分算法;本文对面向有类似需求的DBLE开发者,提供了一个如何开发和部署自定义的拆分规则的一个指引。
目录
1. 工作原理
1.1 函数的加载
1.2 路由计算
1.3 参数查询
2. 开发和部署
2.1 开发
2.2 部署
3. 接口规范
3.1 配置项setters
3.2 selfCheck()
3.3 init()
3.4 calculate()和calculateRange()
3.5 getAllProperties()
4. 内置路由函数的缩写与类名对照表
1. 工作原理
1.1 函数的加载
路由函数的加载发生在DBLE启动或重载时。
- DBLE读取rule.xml时,根据用户配置的标签的class属性
DBLE通过Java的反射机制,从$DBLE_HOME/lib的jar包中,找到对应的jar(里的class文件),加载同名的类并创建对象
DBLE会逐个扫描中的标签,并根据name属性来调用路由函数的对应setter,以此完成赋值过程——例如,如果用户配置了2,那么DBLE就会尝试找路由函数中叫做setPartitionCount()的方法,并将字符串“2”传给它
DBLE调用路由函数的selfCheck()方法,执行函数编写者制定的检查动作,例如检查赋值得到的变量值是否有问题
DBLE调用路由函数的init()方法,执行函数编写者制定的准备动作,例如创建后面要用到的一些中间变量
1.2 路由计算
路由函数接受用户SQL中的分片字段的值,计算出这个值对应的数据记录应该在哪个编号的数据分片(逻辑分片)上,DBLE从而知道把这个SQL准确发到这些分片上。
1.3 参数查询
用户通过管理端口(默认9066),通过SHOW @@ALGORITHM WHERE SCHEMA=? AND TABLE=?来查询表上的路由算法时,DBLE调用路由算法的getAllProperties()方法,直接从内存中获取路由信息的配置。
mysql> show @@algorithm where schema=testdb and table=seqtest;
+-----------------+----------------------------------------------------+
| KEY | VALUE |
+-----------------+----------------------------------------------------+
| TYPE | SHARDING TABLE |
| COLUMN | ID |
| CLASS | com.actiontech.dble.route.function.PartitionByLong |
| partitionCount | 2 |
| partitionLength | 1 |
+-----------------+----------------------------------------------------+
5 rows in set (0.05 sec)
2. 开发和部署
2.1 开发
开发时,理论上只需要引入AbstractPartitionAlgorithm抽象类和RuleAlgorithm接口及它们的依赖类就可以了。但实际上AbstractPartitionAlgorithm抽象类依赖了TableConfig类,由此开启了环游世界的依赖之旅。因此,现实的操作还是引用整个DBLE项目的源代码会比较直接方便。
开发一个新的路由函数时,必须给这个路由函数的开发新建项目,然后再引用DBLE项目(项目引用项目的方式)。而不应该直接打开DBLE的项目,然后在DBLE的项目里面直接新建源代码来直接开发(内嵌开发方式)。通过遵循这个做法,会有以下好处:
- 路由函数可以独立打包,直接去看路由函数的jar包版本就能够确认函数版本;而把路由函数嵌到DBLE里的话,就很容易出现DBLE版本一样,但不清楚里面的函数是什么版本的窘况
路由函数的递进可以更加自由,如果DBLE的AbstractPartitionAlgorithm抽象类和RuleAlgorithm接口没有变动,同一版本的路由函数可以延续使用好几个版本的DBLE,而不需要每次DBLE释放新版就得去重编译
可以让路由函数中的受保护代码免受DBLE自身的开源协议影响
2.2 部署
完成开发之后,成品打包成jar包进行发布,而不要直接发布class和依赖的library(其他项目的jar包或class文件)。
让DBLE使用上新的路由函数的过程:
- 将成品jar包放入$DBLE_HOME/lib目录中
调整jar包的所有者权限(chown)和文件权限(chmod),使之与其他$DBLE_HOME/lib目录里的jar包一样
| 请注意:2.18.12.0及以后版本建议放在algorithm目录下,用于区分原生lib和自定义lib,便于管理
按照原来的思路配置rule.xml,但需要注意标签的class属性必须要填写新的路由函数类的完全限定名(Fully Qualified Name),例如net.john.DBLE.route.functions.NewFunction
配置逻辑表之类的必要信息,重启DBLE后,自动生效。
3. 接口规范
每个路由函数本质上就是一个继承了AbstractPartitionAlgorithm抽象类,并且实现了RuleAlgorithm接口的一个类。下面以内置的com.actiontech.DBLE.route.function.PartitionByLong为例,介绍实现一个路由函数类所需要做的最小工作(必要工作)。
3.1 配置项setters
在rule.xml中,我们需要配置partitionCount和partitionLength两个配置项。
<function name="hashmod" class="com"><property name="partitionCount">4</property><property name="partitionLength">1</property>
</function>
为了让DBLE在函数加载过程中,能够认出这里的partitionCount(值为4)和partitionLength(值为1),因此PartitionByLong类中,就必须有属性设置方法(setter)setPartitionCount()和setPartitionLength()。而因为rule.xml是个文本型的XML文件,所以这些函数的传入参数就只能是一个String,数据类型转换和预处理的动作就由这些setter来处理了。
public void setPartitionCount(String partitionCount) {this.count = toIntArray(partitionCount);/* 参考本文的getAllProperties()的说明 */propertiesMap.put("partitionCount", partitionCount);
}public void setPartitionLength(String partitionLength) {this.length = toIntArray(partitionLength);/* 参考本文的getAllProperties()的说明 */propertiesMap.put("partitionLength", partitionLength);
}
3.2 selfCheck()
在函数加载过程中,完成了配置项赋值之后,DBLE会调用这个路由函数对象的selfCheck()方法,让这个对象自我检查刚才读进来的配置项的值,放在一起是不是有问题。如果有问题的话,路由函数编写者在这时候,可以通过抛出RuntimeException来进行报错,并终止DBLE使用这个函数,当然,由于RuntimeException的霸道,DBLE自己也会因此而报错退出。
由于selfCheck()是RuleAlgorithm接口的要求,而且AbstractPartitionAlgorithm抽象类没又实现它,对于想偷懒或者没有必要进行这个检查的人来说,还是需要自行定义一个空的同名方法来实现它。
@Override
public void selfCheck() {
}
3.3 init()
在函数加载过程的最后,DBLE调用这个路由函数对象的init()方法,让这个对象完成一些内部的初始化工作。
在我们的例子PartitionByLong里,通过init()方法准备了PartitionUtil对象,其中有一个哈希值的范围与逻辑分片号对应的数组,这样在后面的路由计算时就能通过查数组来加速得到结果。
3.4 calculate()和calculateRange()
DBLE执行用户SQL时,根据用户SQL的不同,调用calculate()或calculateRange()来确定用户的SQL应该发到哪个数据分片上去。
从IPO(Input-Process-Output)来分析,calculate()和calculateRange()的工作原理是一样的:
- Input:用户SQL中的分片字段值
- Output:用户SQL应该要发往的数据分片的编号
- Process:Input与Output转换的计算过程,由函数开发者编写
calculate()和calculateRange()的使用场景不同,导致它们存在着一些微小的差异。
函数名 | 调用场景 | Input | Output |
---|---|---|---|
calculate() | 用户SQL里分片字段的值是单值的情况,例如 … WHERE sharding_key = 1 | 1个String | 1个Integer |
calculateRange() | 用户SQL里分片字段的值是连续范围,例如 … WHERE sharding_key BETWEEN 1 AND 5 | 2个String | Integer数组 |
@Override
public Integer calculate(String columnValue) {try {if (columnValue == null || columnValue.equalsIgnoreCase("NULL")) {return 0;}long key = Long.parseLong(columnValue);return calculate(key);} catch (NumberFormatException e) {throw new IllegalArgumentException("columnValue:" + columnValue + " Please eliminate any quote and non number within it.", e);}
}@Override
public Integer[] calculateRange(String beginValue, String endValue) {long begin = 0;long end = 0;try {begin = Long.parseLong(beginValue);end = Long.parseLong(endValue);} catch (NumberFormatException e) {return new Integer[0];}int partitionLength = partitionUtil.getPartitionLength();if (end - begin >= partitionLength || begin > end) { //TODO: optimize begin > endreturn new Integer[0];}Integer beginNode = calculate(begin);Integer endNode = calculate(end);if (endNode > beginNode || (endNode.equals(beginNode) && partitionUtil.isSingleNode(begin, end))) {int len = endNode - beginNode + 1;Integer[] re = new Integer[len];for (int i = 0; i < len; i++) {re[i] = beginNode + i;}return re;} else {int split = partitionUtil.getSegmentLength() - beginNode;int len = split + endNode + 1;if (endNode.equals(beginNode)) {//remove duplicatelen--;}Integer[] re = new Integer[len];for (int i = 0; i < split; i++) {re[i] = beginNode + i;}for (int i = split; i < len; i++) {re[i] = i - split;}return re;}
}
3.5 getAllProperties()
当用户找DBLE要路由函数的配置信息时,DBLE通过访问路由函数的getAllProperties()来获得一个<配置项, 配置值>的哈希表,然后将里面的内容逐项返回给用户。
getAllProperties()是RuleAlgorithm接口所规定要实现的,但为了简化编写新的路由函数的工作,在AbstractPartitionAlgorithm抽象类里,定义了propertiesMap这个私有变量,并且把“将propertiesMap交出去”作为了实现了getAllProperties()方法的默认实现。
一般来说,这个默认的实现能满足需求,而新路由函数编写者只需要在配置项setters处理用户配置时,将<配置项, 配置值>给put()进propertiesMap里就好了。
@Override
public Map<String, String> getAllProperties() {return propertiesMap;
}
4. 内置路由函数的缩写与类名对照表
DBLE内置的路由函数都位于com.actiontech.DBLE.route.function命名空间。但实际配置rule.xml的时候,却不用写那么长的完全限定名,这其实都是XMLRuleLoader类做了转换,因此实现了简写。下面就是7个内置函数的类名和它们的简写。
简写名 |
完整类名 |
---|---|
date | PartitionByDate |
enum | PartitionByFileMap |
hash | PartitionByLong |
jumpstringhash | PartitionByJumpConsistentHash |
numberrange | AutoPartitionByLong |
patternrange | PartitionByPattern |
stringhash | PartitionByString |
社区投稿 | DBLE 自定义拆分算法相关推荐
- TF之LSTM:基于tensorflow框架自定义LSTM算法实现股票历史(1990~2015数据集,6112预测后100+单变量最高)行情回归预测
TF之LSTM:基于tensorflow框架自定义LSTM算法实现股票历史(1990~2015数据集,6112预测后100+单变量最高)行情回归预测 目录 输出结果 LSTM代码 输出结果 数据集 L ...
- DL之DNN优化技术:自定义MultiLayerNetExtend算法(BN层使用/不使用+权重初始值不同)对Mnist数据集训练评估学习过程
DL之DNN优化技术:自定义MultiLayerNetExtend算法(BN层使用/不使用+权重初始值不同)对Mnist数据集训练评估学习过程 目录 输出结果 设计思路 核心代码 更多输出 相关文章: ...
- php 红包算法,PHP语言:实现微信红包拆分算法
本文主要向大家介绍了PHP语言:实现微信红包拆分算法,通过具体的内容向大家展示,希望对大家学习php语言有所帮助. · 修复最后一个红包输出未保留2位数 · 修复领取的红包金额低于最小红包限制 * 红 ...
- 社区发现(一)--算法综述
基础概念简介:https://baike.baidu.com/item/%E7%A4%BE%E5%8C%BA%E5%8F%91%E7%8E%B0%E7%AE%97%E6%B3%95/19460396 ...
- php分割金额_PHP实现红包金额拆分算法案例详解
这次给大家带来PHP实现红包金额拆分算法案例详解,PHP实现红包金额拆分算法的注意事项有哪些,下面就是实战案例,一起来看一下.<?php // 新年红包金额拆分试玩 class CBonus { ...
- 计算机网络二分法划分网络,三种经典复杂网络社区结构划分算法研究_GN算法
论文导读::复杂网络是复杂系统的高度抽象.即社区结构特性[3].算法是一种试探优化法[4].算法. 关键词:复杂网络,社区结构,Laplace图谱,Kernighan-Lin算法,GN算法 1引言 现 ...
- 第五章:Sharding-JDBC 自定义分片算法
Sharding-JDBC 自定义分片算法 自定义分片算法 Sharding提供了以下4种算法接口 PreciseShardingAlgorithm RangeShardingAlgorithm Hi ...
- CK3M自定义伺服算法(C语言)开发的简单流程
本文的章节安排如下 1 注意事项 2 自定义伺服算法(C语言)开发 2.1 Global Includes 2.2 C Language 2.3 编译并下载程序 2.4 控制器调试 3 运动程序 在导 ...
- 社区发现之标签传播算法(LPA)
在Graph领域,社区发现(Community detection)是一个非常热门且广泛的话题,后面会写一个系列,该问题实际上是从子图分割的问题演变而来,在真实的社交网络中,有些用户之间连接非常紧密, ...
最新文章
- 求自定类型元素序列的中位数
- Python学习第四天----Linux之用户与用户组权限
- cannot be cast to org.springframework.web.accept.ContentNegotiationManager
- tsf php,TSF:腾讯推出的 PHP 协程方案
- python commands模块_python commands模块在python3.x被subprocess取代
- jQuery的顶级对象 $
- Py之face_alignment:face_alignment库的简介、安装、使用方法之详细攻略
- MyEclipse之安装SVN1.10.7
- SAP顾问,市场的双重需求
- 计算机网络(一)-概述(补充)
- LeetCode 135 分发糖果
- Emulator 29.2.12 稳定版发布,启用 Google Maps UI
- Trouble Shooting记录:服务 Microsoft Exchange Information Store 意外停止
- 分享一个自己写的基于TP的关系模型(2)
- struts2之自定义拦截器及拦截器生命周期分析
- 大数据分析师的报考条件是什么?
- 如何调整plt.plot()线的粗细,linewidth
- C# 五步完成Bmp文件流到AVI的转换
- Frame skipped from debugging during step-in. VSCode调试无法定位其它库中代码的解决办法
- 第三节 数据通信基础
热门文章
- 产业区块链发展周报(10.17—10.23)| 陀螺研究院
- android adobe pdf阅读器,Adobe发布Android手机专用PDF阅读程序
- 乔布简历:谁说我只是个简历模板库?
- html注册登录接口,API注册
- Ubuntu 7z安装
- 数据分析师的Windows装机必备软件
- 任天堂红白机 ( NES ) 文档
- zigbee无线传感网技术与应用开发v2.0_物联网通讯协议——Zigbee
- P3166 [CQOI2014]数三角形
- 认识无线网络之Wi-Fi