MyCat分片算法学习(纯转)

2018/03/30 20:40
阅读数 1.1K

转载自Mycat分片算法学习

1 分片枚举

1.1 官方文档

通过在配置文件中配置可能的枚举id,自己配置分片。
这种规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国的省份区县固定的,这类业务使用这一规则。配置如下

<tableRule name="sharding-by-intfile">
    <rule>
        <columns>user_id</coulumns>
        <algorithm>hash-int</algorithm>
    </rule>
</tableRule>
<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">
    <property name="mapFile">partition-hash-int.txt</property>
    <property name="type">0</property>
    <property name="defaultNode">0</property>
</function>

partition-hash-int.txt配置:

10000=0
10010=1
DEFAULT_NODE=1

 

上面columns标识将要分片的表字段,algorithm分片函数。
其中分片函数配置中,mapFile标识配置文件名称,type默认值为0,0标识Integer,非零标识String,所有的节点配置都是从0开始。

defaultNode默认节点:小于0表示不设置默认节点,大于等于0表示设置默认节点
默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点
如果不配置默认节点,碰到不识别的枚举值就会报错

2 固定分片hash算法

2.1 官方文档

本条规则类似于十进制的求模运算,区别在于是二进制的操作,是取id的二进制低10位(why?)
此算法的有点在于,如果按照10进制取模运算,在连续插入1-10时他们会把分到1-10个分片,增大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少事务控制难度

<tableRule name="rule1">
    <rule>
       <columns>user_id</columns>
       <algorithm>func1</algorithm>
    </rule>
</tableRule>

<function name="fuc1" class="org.opencloudb.route.function.PartitionByLong">
    <property name="partitionCount">2,1</property>
    <property name="partitionLength">256,512</property>
</function>

配置说明:
partitionCount分片个数列表,partitionLength分片范围列表
分区长度:默认为最大1024,即最大支持1024分区
1024 = sum((count[i]*length[i])). count 和 length 两个向量的点积恒等于 1024

2.2 个人笔记

即,按照上文的例子来说,将一个表分为1024块,前两份分别拿走256块,第三份拿走512块。
意义不明,使用string字段分片时,根据配置的分片规则,只和最后一个字母和倒数第二个字母的部分有关。
只能作用在纯数字的列上。好处可以理解,似乎只能适用在id之类的字段上。如文档中所说,适合在连续插入的时候使用。
这种分片方法适用在什么地方依旧有待研究

3 范围约定

3.1 官方文档

此分片适用于提前规划好分片字段某个范围属于哪个分片
start <= range <= end.
range start-end, data node index
K=1000, M=10000.

<tableRule name="auto-sharding-long">
    <rule>
        <columns>user_id</columns>
        <algorithm>rang-long</algorithm>
    </rule>
</tableRule>
<function name="rang-long" class="org.opencloudb.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
    <property name="defaultNode">0</property>
</function>
    0-500M=0
    500M-1000M=1
    1000M-1500M=2

3.2 个人笔记

将数值类型临近的数据分在一起。如果能获得某条数据具体在哪个分片表中,就可以简化全局排序(需要去查查看
**分片依据的数值改变超过分片界限后是否会重新分布?(需验证)**

分片依据的那一列数据无法改变,用处瞬间变小。

4 取模

4.1 官方文档

此规则为对分片字段求模运算。

<tableRule name="mod-long">
    <rule>
        <columns>user_id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>
<funciton name="mod-long" class="org.opencloudb.route.function.PartitionByMod">
    <property name="count">3</property>
</funciton>

此种配置非常明确地根据id进行十进制取模运算,相比固定分片hash,这种方法在批量插入时可能存在批量插入单事物插入多数据分片,增大失误一致性难度。

4.2 个人笔记

缺点有了,优点在哪儿??

5 按日期(天)分片

5.1 官方文档

<tableRule name="sharding-by-date">
    <rule>
        <columns>create_time</columns>
        <algorithm>sharding-by-date</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-date" class="org.opencloudb.route.function.PartitionByDate">
    <property name="dateFormat">yyyy-MM-dd</property>121
    <property name="sBeginDate">2014-01-01</property>
    <property name="sEndDate">2014-01-02</property>
    <property name="sPartionDay">10</property>
</function>

如果配置了 sEndDate 则代表数据达到了这个日期的分片后后循环从开始分片插入。

5.2 个人笔记

啥啥啥???文档说的不明不白的,需要具体试一下是怎么分片的。

6 取模范围约束

6.1 官方文档

此种规则是取模运算与范围约束的结合,主要为了后续数据迁移做准备,即可以自主决定取模后数据的节点
分布。
“`html


user_id
sharding-by-pattern



256
2
partition-pattern.txt


```text
partition-pattern.txt




<div class="se-preview-section-delimiter"></div>

# id partition range start-end ,data node index




<div class="se-preview-section-delimiter"></div>

###### first host configuration
1-32=0
33-64=1122
65-96=2
97-128=3




<div class="se-preview-section-delimiter"></div>

######## second host configuration
129-160=4
161-192=5
193-224=6
225-256=7
0-0=7




<div class="se-preview-section-delimiter"></div>

上面 columns 标识将要分片的表字段,algorithm 分片函数,patternValue 即求模基数,defaoultNode
默认节点,如果配置了默认,则不会按照求模运算

配置文件中,1-32 即代表 id%256 后分布的范围,如果在 1-32 则在分区 1,其他类推,如果 id 非数据,则
会分配在 defaoultNode 默认节点

6.2 个人笔记

取模后再范围约束可以解决需要根据分片字段的值来设计分片的问题,如果分片用的字段是类似销售额之类的内容,如果销售额增加超过了之前设置的上限,那么范围约束算法会报错或者将数据全都放到默认分片中。这个算法则不会。
所谓的“为了后续数据迁移做准备”是什么意思?为什么这样的分片可以做到这一点?

7 截取数字做 hash 求模范围约束

7.1 官方文档

此种规则类似于取模范围约束,此规则支持数据符号字母取模。

<tableRule name="sharding-by-prefixpattern">
    <rule>
        <columns>user_id</columns>
        <algorithm>sharding-by-prefixpattern</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-pattern" class="org.opencloudb.route.function.PartitionByPrefixPattern">
    <property name="patternValue">256</property>
    <property name="prefixLength">5</property>
    <property name="mapFile">partition-pattern.txt</property>
</function>




<div class="se-preview-section-delimiter"></div>
partition-pattern.txt
# range start-end ,data node index
# ASCII
# 8-57=0-9 阿拉伯数字123
# 64、 65-90=@、 A-Z
# 97-122=a-z
###### first host configuration
1-4=0
5-8=1
9-12=2
13-16=3
###### second host configuration
17-20=4
21-24=5
25-28=6
29-32=7
0-0=7

 

上面 columns 标识将要分片的表字段,algorithm 分片函数,patternValue 即求模基数,prefixLength
ASCII 截取的位数

此种方式类似方式 6 只不过采取的是将列种获取前 prefixLength 位列所有 ASCII 码的和进行求模
sum%patternValue ,获取的值,在范围内的分片数。

8 应用指定

8.1 官方文档

此规则是在运行阶段有应用自主决定路由到那个分片。

<tableRule name="sharding-by-substring">
    <rule>
      <columns>user_id</columns>
        <algorithm>sharding-by-substring</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-substring" class="org.opencloudb.route.function.PartitionDirectBySubString">
    <property name="startIndex">0</property><!-- zero-based -->124
    <property name="size">2</property>
    <property name="partitionCount">8</property>
    <property name="defaultPartition">0</property>
</function>

上面 columns 标识将要分片的表字段,algorithm 分片函数,此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。
例如 id=05-100000002,在此配置中代表根据 id 中从 startIndex=0,开始,截取 siz=2 位数字即 05,05 就是获取的分区,如果没传默认分配到 defaultPartition

9 截取数字hash解析

9.1 官方文档

此规则是截取字符串中的 int 数值 hash 分片。

<tableRule name="sharding-by-stringhash">
    <rule>
        <columns>user_id</columns>
        <algorithm>sharding-by-stringhash</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-stringhash" class="org.opencloudb.route.function.PartitionByString">
    <property name="partitionLength">512</property><!-- zero-based -->
    <property name="partitionCount">2</property>
    <property name="hashSlice">0:2</property>
</function>
/**
* “2” -> (0,2)
* “1:2” -> (1,2)
* “1:” -> (1,0)
* “-1:” -> (-1,0)
* “:-1” -> (0,-1)125
* “:” -> (0,0)
*/

10 一致性hash

10.1 官方文档

一致性 hash 预算有效解决了分布式数据的扩容问题。

<tableRule name="sharding-by-murmur">
    <rule>
    <columns>user_id</columns>
    <algorithm>murmur</algorithm>
</rule>
</tableRule>
<function name="murmur" class="org.opencloudb.route.function.PartitionByMurmurHash">
    <property name="seed">0</property><!-- 默认是 0-->
    <property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片-->
    <property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是 160 倍,也就是虚拟节点数是物理节点数的 160 倍-->
<!--
<property name="weightMapFile">weightMapFile</property>
节点的权重,没有指定权重的节点默认是 1。以 properties 文件的格式填写,以从 0 开始到 count-1 的整数值也就是节点索引为 key,以节点权重值为值。所有权重值必须是正整数,否则以 1 代替
-->
<!--
<property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的 murmur hash 值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西
-->
</function>

假设有10个分片节点,1000万的数据量,主键从100万开始自增。
执行PartitionByMurmurHash的main方法,按上述假设会得到以下结果

index bucket ratio
 0 1001836 0.1001836
 1 1038892 0.1038892
 2 927886 0.0927886
 3 972728 0.0972728
 4 1086100 0.10861
 5 908616 0.0908616
 6 1024269 0.1024269
 7 1018029 0.1018029
 8 995581 0.0995581
 9 1026063 0.1026063

第一列是分片节点的编号,第二列是hash到每个节点的数据量,第三列是每个hash到每个节点的数据量与总数据量的比值。第三列的和是1.0,第十五列的和是10000000。 如果数据量相当少,会发现一致性哈希的分布不够均匀,而只要数据量在10000以上一致性哈希的分布比率就能保持在0.1左右,数据越多分布越均匀,每个节点的数据量越接近。
现在假设增加二个新节点,对0号节点执行rehash,会出现以下结果

 index bucket ratio
 0 853804 0.8522392886660092
 1 0 0.0
 2 0 0.0
 3 0 0.0
 4 0 0.0
 5 0 0.0
 6 0 0.0
 7 0 0.0
 8 0 0.0
 9 0 0.0
 10 70075 0.06994657808264028
 11 77957 0.07781413325135052

第一第二列的意义与上一组列表一样,第三列是hash到当前节点上的数据量与原0号节点总数据量的比值。 从以上列表可以看到,原0号节点有1001836数据,rehash之后大部分数据仍然hash到0号上面,少量数据hash到了10、11号两个新节点,其它旧节点没有得到原来0号上的数据。其实不管增加多少节点,数据的rehash结果都会呈现这个规律:已有节点的数据发生rehash时只有两个可能的去处,要么是rehash之前的节点,要么是新增加的节点,这也是一致性哈希的意义所在。
采用这种该片方式,可以保证数据在rehash时尽可能的少迁移数据。

10.2 个人笔记

如何rehash??

11 按月单小时拆分

11.1 官方笔记

此规则是单月内按照小时拆分,最小粒度是小时,可以一天最多 24 个分片,最少 1 个分片,一个月完后下月
从头开始循环。
每个月月尾,需要手工清理数据。

<tableRule name="sharding-by-hour">
    <rule>
        <columns>create_time</columns>
        <algorithm>sharding-by-hour</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-hour" class="org.opencloudb.route.function.LatestMonthPartion">
    <property name="splitOneDay">24</property>
</function>

splitOneDay : 一天切分的分片数

LatestMonthPartion partion = new LatestMonthPartion();
partion.setSplitOneDay(24);
Integer val = partion.calculate("2015020100");
assertTrue(val == 0);
val = partion.calculate("2015020216");
assertTrue(val == 40);
val = partion.calculate("2015022823");
assertTrue(val == 27 * 24 + 23);
Integer[] span = partion.calculateRange("2015020100", "2015022823");
assertTrue(span.length == 27 * 24 + 23 + 1);
assertTrue(span[0] == 0 && span[span.length - 1] == 27 * 24 + 23);
span = partion.calculateRange("2015020100", "2015020123");127
assertTrue(span.length == 24);
assertTrue(span[0] == 0 && span[span.length - 1] == 23);

12 范围求模分片

12.1 官方文档

先进行范围分片计算出分片组,组内再求模
优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题
综合了范围分片和求模分片的优点,分片组内使用求模可以保证组内数据比较均匀,分片组之间是范围分片
可以兼顾范围查询。
最好事先规划好分片的数量,数据扩容时按分片组扩容,则原有分片组的数据不需要迁移。由于分片组内数据比较均匀,所以分片组内可以避免热点数据问题。

<tableRule name="auto-sharding-rang-mod">
    <rule>
        <columns>id</columns>
        <algorithm>rang-mod</algorithm>
    </rule>
</tableRule>
<function name="rang-mod" class="org.opencloudb.route.function.PartitionByRangeMod">
    <property name="mapFile">partition-range-mod.txt</property>
    <property name="defaultNode">21</property>
</function>

上面 columns 标识将要分片的表字段,algorithm 分片函数,
rang-mod 函数中 mapFile 代表配置文件路径
defaultNode 超过范围后的默认节点顺序号,节点从 0 开始。
partition-range-mod.txt
range start-end ,data node group size
以下配置一个范围代表一个分片组,=号后面的数字代表该分片组所拥有的分片的数量。
0-200M=5 //代表有 5 个分片节点
200M1-400M=1
400M1-600M=4
600M1-800M=4
800M1-1000M=6

13 日期范围hash分片

13.1 官方文档

思想与范围求模一致,当由于日期在取模会有数据集中问题,所以改成 hash 方法。
先根据日期分组,再根据时间 hash 使得短期内数据分布的更均匀
优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题128
要求日期格式尽量精确些,不然达不到局部均匀的目的

<tableRule name="rangeDateHash">
    <rule>
        <columns>col_date</columns>
        <algorithm>range-date-hash</algorithm>
    </rule>
</tableRule>
<function name="range-date-hash" class="org.opencloudb.route.function.PartitionByRangeDateHash">
    <property name="sBeginDate">2014-01-01 00:00:00</property>
    <property name="sPartionDay">3</property>
    <property name="dateFormat">yyyy-MM-dd HH:mm:ss</property>
    <property name="groupPartionSize">6</property>
</function>

 

14 冷热数据分片

14.1 官方文档

根据日期查询日志数据 冷热数据分布 ,最近 n 个月的到实时交易库查询,超过 n 个月的按照 m 天分片。

<tableRule name="sharding-by-date">
    <rule>
        <columns>create_time</columns>
        <algorithm>sharding-by-hotdate</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-hotdate" class="org.opencloudb.route.function.PartitionByHotDate">
    <property name="dateFormat">yyyy-MM-dd</property>
    <property name="sLastDay">10</property>
    <property name="sPartionDay">30</property>
</function>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

15 自然月分片

15.1 官方文档

按月份列分区 ,每个自然月一个分片,格式 between 操作解析的范例。

<tableRule name="sharding-by-month">
    <rule>
        <columns>create_time</columns>
        <algorithm>sharding-by-month</algorithm>
    </rule>
</tableRule>
<function name="sharding-by-month" class="org.opencloudb.route.function.PartitionByMonth">
    <property name="dateFormat">yyyy-MM-dd</property>
    <property name="sBeginDate">2014-01-01</property>
</function>
PartitionByMonth partition = new PartitionByMonth();
partition.setDateFormat("yyyy-MM-dd");
partition.setsBeginDate("2014-01-01");
partition.init();
Assert.assertEquals(true, 0 == partition.calculate("2014-01-01"));
Assert.assertEquals(true, 0 == partition.calculate("2014-01-10"));
Assert.assertEquals(true, 0 == partition.calculate("2014-01-31"));
Assert.assertEquals(true, 1 == partition.calculate("2014-02-01"));
Assert.assertEquals(true, 1 == partition.calculate("2014-02-28"));
Assert.assertEquals(true, 2 == partition.calculate("2014-03-1"));
Assert.assertEquals(true, 11 == partition.calculate("2014-12-31"));
Assert.assertEquals(true, 12 == partition.calculate("2015-01-31"));
Assert.assertEquals(true, 23 == partition.calculate("2015-12-31"));

二次开发

开发新的分片规则
建一个新的类,继承AbstractPartitionAlgorithm实现RuleAlgorithm。重写public void init()和public Integer calculate(String columnValue)。
init根据rule.xml指定的分片初始化参数初始化分片规则,calculate(String)接收分片字段的字符串形式计算记录应该保存的节点。

总笔记

Todo:
- 试验下固定分片Hash
- 看下范围约定的源码,主要是怎么转换二进制的。测试下具体分片效果
- 去看看日期分片的源码
- 试一下日期分片那个sEndDate的用处
- 为何一致性hash预算能解决扩容问题?
- 单月按小时分片里面的需要手工清理数据何解?清理到哪儿去?

Note:
- 变更规则后需要重启mycat才能生效,或者在mysql中使用”
- 使用Insert的时候需要指定ColumnList,不能隐藏
- 固定分片Hash算法所对应的列只支持数字
- 无法对分片算法使用的行进行Update
- 文档中写的算法class路径与实际路径不一致,新的路径是“io.mycat.”开头的
- 按照日期分片不需要显式地定义分片数,具体分片数量由sBeginDate,sEndDate和sPartionDay三者决定,前两者决定了总天数,第三者决定了每x天换一个分片。
sEndDate到达后会从头开始分配

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部