1.1 rule.xml
1.1.1 相关文件
这部分与rule.dtd和rule.xml相关。
在2.18.12.0之后的版本中rule.xml在文件头中添加了version属性,以供运维人员区分配置创建或修改的版本(xml配置version对照表)
verison字段不匹配时,启动和dryrun会给出NOTICE的提示,但不会影响功能
rule.dtd定义解析规则,仅与开发相关。如有疑问,请参看xml_dtd_intro。
rule.xml定义实际用到的分区算法的配置,它包括如下两类信息:
A.分区规则定义
分区规则定义有如下形式:
- tableRule
配置名称 | 配置内容 | 可设多值 | 说明 |
---|---|---|---|
name | 规则名称 | 否 | tableRule属性,该规则名将被schema.xml中表配置引用,必须在整个文件中唯一 |
rule | 规则详情 | 否 | rule子结构 |
- rule
配置名称 | 配置内容 | 可设多值 | 可选项/默认值 |
---|---|---|---|
columns | 拆分列名 | 否 | |
algorithm | 执行的function名字 | 否 | 只能是function声明过的元素 |
举例
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
B.分区算法定义
分区算法定义有如下形式:
name: 定义分区算法名, 在分区规则定义中被引用。 class:指定分区算法实现类。 每一种分区算法要求的参数个数, 类型各不相同,property name部分用于指定相应分区算法的参数。这部分请参考各分区算法描述。
- function
配置名称 | 配置内容 | 可设多值 | 说明 |
---|---|---|---|
name | 函数的名称 | 否 | 在分区规则定义中被引用 |
class | 拆分算法 | 否 | 只能是Enum,NumberRange,Hash,StringHash,Date,PatternRange,jumpStringHash之一 |
property | 根据具体的function代码示例的属性进行配置参数 | 是 |
举例:
<function name="rang-long"" class="com.actiontech.dble.route.function.AutoPartitionByLong">
<property name="mapFile">auto-sharding-long.txt</property>
...
</function>
1.1.2 支持的分区算法
目前,已支持的分区算法有: hash, stringhash, enum, numberrange, patternrange, date,jumpstringhash.
1.hash分区算法
function的 class属性设置为“hash”或者“com.actiontech.dble.route.function.PartitionByLong"的分区规则应用该算法。具体配置如下:
<function name="hashLong" class="hash">
<property name="partitionCount">C1[,C2, ...Cn]</property>
<property name="partitionLength">L1[,L2, ...Ln]</property>
</function>
partitionCount:指定分区的区间数, 具体为 C1 [+C2 + ... + Cn].
partitionLength:指定各区间长度, 具体区间划分为 [0, L1), [L1, 2L1), ..., [(C1-1)L1, C1L1), [C1L1, C1L1+L2), [C1L1+L2, C1L1+2L2), ... 其中,每一个区间对应一个数据节点。
例如,配置F1:
<property name="partitionCount">2,3</property>
<property name="partitionLength">100,50</property>
将划分如下的分区:
[0 , 100)
[100, 200)
[200, 250)
[250, 300)
[300, 350)
再如,配置F2:
<property name="partitionCount">2</property>
<property name="partitionLength">1000</property>
将划分如下的分区:
[0 , 1000)
[1000, 2000)
根据具体配置, 模的基数M有如下计算公式:C1L1 + ... + Cn Ln.
上面的例子中F1 的M值为350,F2的M值为2000。
在进行分片查找时, 将分区字段key和M值进行求模运算:
value = key mod M
得到的value值再从区间分布中找到自己数据节点的序号。
例如,当配置为F1,key =805 时,value = 105,那么从5个区间内发现对应的数据节点的序号为1(从0开始)。
结点的个数N 记为 C1 [+C2 + ... + Cn].
上面的例子中F1 的N值为5,F2的N值为2。
注意事项:
- M不能大于2880。2880的原因是这样的:2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 30, 32, 36, 40, 45, 48, 60, 64, 72, 80, 90, 96, 120, 144, 160, 180, 192, 240, 288, 320, 360, 480, 576, 720, 960, 1440是2880的约数,这样预分片扩容方便。
- N必须要等于
schema.xml
中使用该分区算法的逻辑表的dataNode属性指定的DataNode数量之和,如dataNode="dn1,dn2,dn3,dn4"中,N必须等于4。 - Cn和Ln的个数必须相等。
- 分区字段必须为整型字段,如果是其他类型,要求值可转化为数字。
- 当partitionLength为1时,hash分区算法退化为求模算法,M及N均为partitionCount的值。
- NULL作为分片列的值的时候数据的结果恒落在0号节点(第一个节点上)
2.stringhash分区算法
class属性设置为“stringhash”或者“com.actiontech.dble.route.function.PartitionByString"的分区规则应用该算法。具体配置如下:
<function name="hashSting" class="stringhash">
<property name="partitionCount">C1[,C2, ...Cn]</property>
<property name="partitionLength">L1[,L2, ...Ln]</property>
<property name="hashSlice">l:r</property>
</function>
partitionCount,partitionLength的具体意义参看hash分区算法。
hashSlice:指定参与hash值计算的key的子串。字符串从0开始索引计数。
子串截取有如下计算步骤和格式:
步骤1. 子串索引区间确定
格式 | 条件 | 区间 |
---|---|---|
n | n>=0 | (0,n) |
n | n<0 | (n,0) |
:r | (0,r) | |
l: | (l,0) | |
: | (0:0) | |
l:r | (l, r) |
步骤2. 子串索引区间修订
a.左边界l修订
第一次修订 | 第二次修订 | |||
---|---|---|---|---|
修订前 | 条件 | 修订后 | 条件 | 修订后 |
l | l>=0 | l | l | |
l | l<0 | l=l+length | l<0 | l=0 |
b.右边界r修订
第一次修订 | 第二次修订 | |||
---|---|---|---|---|
修订前 | 条件 | 修订后 | 条件 | 修订后 |
r | r>0 | r | r>length | r=length |
r | r<=0 | r=r+length | r |
注:length是分区字段实际串的长度.
步骤3. 结果确定
条件 | 结果子串 | hash值 |
---|---|---|
l<r | 索引在[l, r)范围的子串 | 通过子串计算的hash值 |
l>=r | 空字串 | 0 |
这个子串截取算法只是看起来比较复杂。用简洁但不太准确的语言可描述为:区间边界为负值的,从分区字段串尾部开始计数;区间边界为正值的,从分区字段串首部开始计数;然后做一些纠错处理。
该算法与hash分区算法类似,但针对的分区字段为字符串类型。在进行分片查找时, 要先对分区字段求hash值,然后将得到的hash值做hash分区算法的分区查找运算。
注意事项:
- 该分区算法和hash分区算法有同样的限制(注意事项3除外)
- 分区字段为字符串类型。
3.enum分区算法
class属性设置为“enum”或者“com.actiontech.dble.route.function.PartitionByFileMap"的分区规则应用该算法。具体配置如下:
<function name="enum" class="enum">
<property name="mapFile">partition.txt</property>
<property name="defaultNode">0</property>
<property name="type">0</property>
</function>
mapFile:指定配置文件名。其格式将在下面做详细说明。
defaultNode:指定默认节点号。默认值为-1, 不指定默认节点。
type:指定配置文件中key的类型。0:整型; 其它:字符串。
配置文件格式如下: a. type值为0时,
#comment
//comment
this line will be skiped
int1=node0
int2=node1
...
b. type值为非0时,
#comment
//comment
this line will be skiped
string1=node0
string2=node1
...
在进行分片查找时,分片字段的枚举值对应的数据节点既是目的节点。如果不能在配置映射中找到枚举值对应的数据节点:如果配置了defaultNode,则defaultNode既是目的数据节点, 否则出错。
注意事项:
- 不包含“=”的行将被跳过.
- nodex为数据节点索引号。
- 重复的枚举值的分区数据节点以最后一个配置为准。
- 分片字段为该枚举类型。
- 分片字段为NULL时,数据落在defaultNode节点上,若此时defaultNode没有配置,则会报错;当真实存在于mysql的字段值为not null的时候,报错 "Sharding column can't be null when the table in MySQL column is not null"
4.numberrange分区算法
class属性设置为“numberrange”或者“com.actiontech.dble.route.function.AutoPartitionByLong"的分区规则应用该算法。具体配置如下:
<function name="rangeLong" class="numberrange">
<property name="mapFile">partition.txt</property>
<property name="defaultNode">0</property>
</function>
mapFile:指定配置文件名。其格式将在下面做详细说明。
defaultNode:指定默认节点号。默认值为-1, 不指定默认节点。
配置文件格式如下:
#comment
//comment
this line will be skiped
start1-end1=node1
start2-end2=node2
...
该分区算法相当于定义了区间序列 [start1, end1], [start2, end2], ... 每一个区间对应一个数据节点。
在进行分片查找时,分片字段的值落在的区间对应的数据节点即是目的节点。
如果不能在配置映射中找到分片字段的值所落的区间时,分两种情况:1.配置了defaultNode,则defaultNode是目的数据节点;2.没配defaultNode,出错。
注意事项:
- 不包含“=”的行将被跳过。
- nodex为数据节点索引号。
- 如果区间存在重合, 在对重合部分的分片字段值进行分片查找时在配置文件中最先定义的区间对应的数据节点为目的节点。
- 分片字段为整型。
- 分片字段为NULL时,数据落在defaultNode节点上,若此时defaultNode没有配置,则会报错;当真实存在于mysql的字段值为not null的时候,报错 "Sharding column can't be null when the table in MySQL column is not null"
5.patternrange分区算法
class属性设置为“patternrange”或者“com.actiontech.dble.route.function.PartitionByPattern"的分区规则应用该算法。具体配置如下:
<function name="pattern" class="patternrange">
<property name="mapFile">partition.txt</property>
<property name="patternValue">1024</property>
<property name="defaultNode">0</property>
</function>
mapFile:指定配置文件名。其格式将在下面做详细说明。
patternValue:指定模值, 默认为1024。
defaultNode:指定默认节点号。默认值为-1, 不指定默认节点。
配置文件格式如下:
#comment
//comment
this line will be skiped
start1-end1=node1
start1-end2=node2
...
该分区算法类似于numberrange分区算法。但在进行分片查找时先对分片字段值进行模patternValue运算,然后将得到的模数进行等同于numberrange分区算法的分区查找。
注意事项:
- 不包含“=”的行将被跳过。
- nodex为数据节点索引号。
- 如果区间存在重合, 在对重合部分的分片字段值进行分片查找时在配置文件中最先定义的区间对应的数据节点为目的节点。
- 分片字段的内容必须可以转化为整数。如果不能转化为整数:如果配置了defaultNode, 目的数据节点为defaultNode; 否则, 出错。
- 分片字段为NULL时,数据落在defaultNode节点上,若此时defaultNode没有配置,则会报错;当真实存在于mysql的字段值为not null的时候,报错 "Sharding column can't be null when the table in MySQL column is not null"
6.date分区算法
class属性设置为“date”或者“com.actiontech.dble.route.function.PartitionByDate"的分区规则应用该算法。具体配置如下:
<function name="partbydate" class="date">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2015-01-01</property>
[<property name="sEndDate">2015-01-31</property>]
<property name="sPartionDay">10</property>
<property name="defaultNode">0</property>
</function>
dateFormat:指定日期的格式。
sBeginDate:指定日期的开始时间。
sEndDate:指定日期的结束时间。该性质可以不配置或配置为空("")。
sPartionDay:指定分区的间隔,单位是天。
defaultNode:指定默认节点号。默认值为-1, 不指定默认节点。
该算法有两种工作模式:
模式1:不配置sEndDate或者将sEndDate配置为""
在这种模式下, 该算法将时间以sBeginDate为开始, 以sPartionDay为间隔,进行区间划分, 每个区间对应一个数据节点。
在进行区间查找时,分区字段值落在的区间对应的数据节点即是目的数据节点。如果分区字段值小于sBeginDate:如果配置了defaultNode,则数据会被路由至defaultNode;如果没配置defaultNode,则会报错。
模式2:配置sEndDate且不为""
在这种模式下, 该算法将时间以sBeginDate为开始, 以sPartionDay为间隔,以sEndDate为终点, 进行区间划分,划分为N个区间, 每个区间对应一个数据节点。
在进行区间查找时: 如果分区字段值小于等于sEndDate,则计算过程和结果等同于模式1。如果分区字段值大sEndDate,则分片查找公式为:index=((key - sBeginDate)/sPartionDay)%N, 其中key为分片字段值, index为映射到的数据节点索引。这等同于一个环形映射。
如果分区字段值小于sBeginDate,则会检查是否设置了defaultNode。如果设置了,数据会路由至defaultNode;否则报错。
注意事项:
- 分片字段必须是符合dateFormat的日期字符串。
- 区间划分不以日历时间为准,无法对应到日历时间。内部进行区间划分时,会将sPartionDay转化为以86400000毫秒为一天进行运算。
- 在模式2的情况下, 如果(sEndDate - sBeginDate)不是sPartionDay的整数倍,则索引号为0的数据节点承载更多的数据。
- 分片字段为NULL时,数据落在defaultNode节点上,若此时defaultNode没有配置,则会报错;当真实存在于mysql的字段值为not null的时候,报错 "Sharding column can't be null when the table in MySQL column is not null"
7.跳增字符串算法
class属性设置为"jumpstringhash"或者"com.actiontech.dble.route.function.PartitionByJumpConsistentHash"的分区规则应用该算法,具体配置如下
<function name="jumphash"
class="jumpStringHash">
<property name="partitionCount">2</property>
<property name="hashSlice">0:2</property>
</function>
partitionCount:分片数量
hashSlice:分片截取长度
该算法来自于Google的一篇文章A Fast, Minimal Memory, Consistent Hash Algorithm其核心思想是通过概率分布的方法将一个hash值在每个节点分布的概率变成1/n,并且可以通过更简便的方法可以计算得出,而且分布也更加均匀
注意事项:
- 分片字段值为NULL时,数据恒落在0号节点之上;当真实存在于mysql的字段值为not null的时候,报错 "Sharding column can't be null when the table in MySQL column is not null"
1.1.3 完整配置举例
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dble:rule SYSTEM "rule.dtd">
<dble:rule xmlns:dble="http://dble.cloud/" version="9.9.9.9">
<tableRule name="sharding-by-enum">
<rule>
<columns>id</columns>
<algorithm>enum</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-range">
<rule>
<columns>id</columns>
<algorithm>rangeLong</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-hash">
<rule>
<columns>id</columns>
<algorithm>hashLong</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-hash2">
<rule>
<columns>id</columns>
<algorithm>hashLong2</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-hash3">
<rule>
<columns>id</columns>
<algorithm>hashLong3</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-mod">
<rule>
<columns>id</columns>
<algorithm>hashmod</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-hash-str">
<rule>
<columns>id</columns>
<algorithm>hashString</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-date">
<rule>
<columns>calldate</columns>
<algorithm>partbydate</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-pattern">
<rule>
<columns>id</columns>
<algorithm>pattern</algorithm>
</rule>
</tableRule>
<!-- enum partition -->
<function name="enum"
class="Enum">
<property name="mapFile">partition-hash-int.txt</property>
<property name="defaultNode">0</property><!--the default is -1,means unexpected value will report error-->
<property name="type">0</property><!--0 means key is a number, 1 means key is a string-->
</function>
<!-- number range partition -->
<function name="rangeLong" class="NumberRange">
<property name="mapFile">autopartition-long.txt</property>
<property name="defaultNode">0</property><!--he default is -1,means unexpected value will report error-->
</function>
<!-- Hash partition,when partitionLength=1, it is a mod partition-->
<!--MAX(sum(count*length[i]) must not more then 2880-->
<function name="hashLong" class="Hash">
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
<!-- <property name="partitionCount">2,3</property>
<property name="partitionLength">4,5</property>-->
</function>
<!-- Hash partition,when partitionLength=1, it is a mod partition-->
<!--MAX(sum(count*length[i]) must not more then 2880-->
<function name="hashLong2" class="Hash">
<property name="partitionCount">2</property>
<property name="partitionLength">512</property>
<!-- <property name="partitionCount">2,3</property>
<property name="partitionLength">4,5</property>-->
</function>
<!-- Hash partition,when partitionLength=1, it is a mod partition-->
<!--MAX(sum(count*length[i]) must not more then 2880-->
<function name="hashLong3" class="Hash">
<property name="partitionCount">2,1</property>
<property name="partitionLength">256,512</property>
<!-- <property name="partitionCount">2,3</property>
<property name="partitionLength">4,5</property>-->
</function>
<!-- eg: mod 4 -->
<function name="hashmod" class="Hash">
<property name="partitionCount">4</property>
<property name="partitionLength">1</property>
</function>
<!-- Hash partition for string-->
<function name="hashString" class="StringHash">
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
<property name="hashSlice">0:2</property>
<!--<property name="hashSlice">-4:0</property> -->
</function>
<!-- date partition -->
<!-- 4 case:
1.set sEndDate and defaultNode: input <sBeginDate ,router to defaultNode; input>sEndDate ,mod the period
2.set sEndDate, but no defaultNode:input <sBeginDate report error; input>sEndDate ,mod the period
3.set defaultNode without sEndDate: input <sBeginDate router to defaultNode;input>sBeginDate + (node size)*sPartionDay-1 will report error(expected is defaultNode,but can't control now)
4.sEndDate and defaultNode are all not set: input <sBeginDate report error;input>sBeginDate + (node size)*sPartionDay-1 will report error
-->
<function name="partbydate" 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>
<!-- pattern partition -->
<!--mapFile must contains all value of 0~patternValue-1,key and value must be Continuous increase-->
<function name="pattern"
class="PatternRange">
<property name="mapFile">partition-pattern.txt</property>
<property name="patternValue">1024</property>
<property name="defaultNode">0</property><!--contains string which is not number,router to default node-->
</function>
</dble:rule>