架构设计系统存储MySQL横向拆分与业务透明化_第1页
架构设计系统存储MySQL横向拆分与业务透明化_第2页
架构设计系统存储MySQL横向拆分与业务透明化_第3页
架构设计系统存储MySQL横向拆分与业务透明化_第4页
架构设计系统存储MySQL横向拆分与业务透明化_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、架构设计:系统存储MySQL横向拆分与业务透明化使用MyCat配置横向拆分之前文章中我们介绍了如何使用MyCat进行读写分离,类似的关系型数据库的读写分离存储方案可以在保持上层业务系统透明度的基础上满足70%业务系统的数据承载规模要求和性能要求。比起单纯使用LVS + Replicaion的读写分离方案而言最大的优势在于更能增加对上层业务系统的透明性。当然如果 您觉得单个MyCat节点在高可用范畴或者性能范畴上还需要增强,还可以使用Keepalived、LVS等组件在多个MyCat节点上组成高可用集群或者负载集群。但是这个方案也有一个明显的问题,那就是它没有解决数据存储规模的瓶颈。如果单个节点

2、上某个单表的数据规模超过了千万级,那么这个节点的读操作也会产生性能瓶颈。所以我们还需要进一步使用MyCat的分片技术对业务数据表进行横向拆分。要说清楚MyCat对横向拆分的支持,就首先要说清楚关系型数据库横向拆分所面临的主要问题,以及MyCat为了解决这些问题所作的努力。总的来说横向拆分的所面临的问题主要分为两大类,一类是数据读的问题一类是数据写的问题。本节我们首先分析讨论一下数据读的问题,后文中介绍MyCat对分布式事务的支持时我们再来讨论横向拆分时的数据写问题。4-3-1、数据分片中的数据读操作问题select TableA.*,TableB.xname,TableC.xcode from

3、 TableAleft join TableB on TableB.id = TableA.b_codeidleft join TableC on TableC.a_id = TableA.idwhere TableA.groupname = 'XXXX'以上查询语句是我们在业务系统数据查询的过程中经常使用的一种查询类型,是一种多个数据表进行左外连接的查询语句。其中TableA业务表拥有大量的数据且变化频率非常高,是需要进行拆分的主要数据表;TableB业务表可能是一张字典表,虽然它有比较大的数据,但远远没有达到千万级别并且变化频率很低(每天最多有10000次数据写操作);Ta

4、bleC业务表中的数据量也很大,从技术角度上说该业务表可以做拆分也可以不做拆分,其中的TableC.a_id字段和TableA.id字段也是一种弱关联,也就是说TableC中的业务数据就算没有关联TableA中的业务数据也可以相对独立的工作。当然以上说的是一种可能的业务数据状态,实际情况还可能更复杂。如果这些业务表同在一个数据库中,甚至是存在于同一个MySQL实例的不同数据库中,那么执行以上查询语句基本上没有什么难度,技术人员使用MySQL的执行计划也可以很清晰的看到查询语句的执行过程:但是如果在分库状态下,那么查询过程就没有这么简单了。首先来说,主要需要进行数据拆分处理的TableA中的数据

5、分布在不同的数据库中,这些数据库工作在不同的MySQL实例上。另外业务表TableC中的数据也进行了拆分,但是拆分时并没有参考和其可能有关联的TableA中的业务数据存储分片情况,也就是说原来已有的关联在拆分存储后可能就消失了,而且即使拆分后的数据关联还存在,但拆分前和拆分后执行数据排序操作的结构也可能是不同的。至于字典表TableB,由于可预见的时间内数据总规模不大,所以可以不进行拆分所有拆分后的数据库中TableB数据表的数据内容完全一样。下图展示了一种数据表中数据进行随机拆分后可能的存储结构和产生的问题:这样来看,数据表横向拆分过程中至少需要考虑以下读操作问题:横向拆分后数据表之间的逻辑

6、关联问题:数据表间存在各种关联,有的关联甚至还存在外键约束。数据拆分后的关联关系应该和拆分前的关联关系保持一致,至少应该保证通过数据库中间件查询得到的数据关联结果和拆分前的关联结果保持一致。横向拆分后数据的排序和分页问题:由于数据拆分后,排序动作会分别在各个拆分后的数据库中单独执行,这可能就会导致拆分后的排序和分页结果和拆分前的结果不一致。那么数据库中间件需要保证能够将这些结果集合进行整合并还原成拆分前的排序和分页结果。横向拆分后数据的分组操作问题:分组和统计操作同样存在和以上描述类似的问题,各个拆分后的数据库将单独执行分组和统计,这就可能导致用一个分区条件在各个拆分数据库中都有分组和统计结果

7、。数据库中间件还是需要保证能够合并这些分组统计结果,并保证它们和拆分前的数据库操作结果一致。4-3-2、全局表在数据库的横向拆分过程中,各种数据字典表基本上不需要进行拆分。这是因为这些数据表的数据规模都不会太大且变化频率较低,另外一个原因是减少横向拆分后表关联操作的难度。类似省市县信息、手机区号信息、功能菜单信息等数据都应算作字典数据。根据实际工作经验,并不会出现所有业务表的数据规模都达到或超过千万级规模,只有部分关键业务表的数据规模会出现这样的情况,基于这样的情况只有这些业务表和与它们直接关联的部分数据表需要进行数据拆分设计。MyCat数据库中间件中为除了以上情况外,各个不需要进行数据拆分的

8、数据表提供了一种冗余复制方案:全局表。如果一张业务表在schema.xml配置文件配设置成了全局表,那么MyCat将在涉及这张业务表的所有分片节点上保持这张数居表中数据完全一致。Mycat在Join操作中,业务表与全局表进行Join聚合会优先选择相同分片内的全局表join,避免跨库 Join;在进行数据插入操作时,MyCat将把数据分发到全局表对应的所有分片执行,在进行数据读取时候将会随机获取一个节点读取数据。schema.xml配置文件中的全局表配置类似如下:.<schema .> # 请注意这里的type属性,属性值为“global”,代表全局表 # 这样,在dn1和dn2两个

9、分片节点中的t_area业务表中,其数据将保持完全一致。 <table name="t_area" primaryKey="id" type="global" dataNode="dn1,dn2" /></schema>.分片表为了在表关联查询性能和表关联处理难易程度之间取得平衡,MyCat提供了两种分片表类型和多种分片规则。对于业务关联较为独立的需要进行数据分片的业务表可以采用普通分片。然而有一类情况是,需要进行数据分片的业务表有一些非常重要的关联数据也同时需要进行分片,例如订单(orde

10、r)数据表和订单明细(order_detail)数据表。很明显订单数据和订单明细数据是经常需要进行关联查询的,并且既然订单数据达到了一定的规模需要进行数据分片,那么只会比它数据量更大的订单明细表也同时需要进行分片。在这样的关联分片情况下,MyCat需要保证订单明细A1、A2、A3、A4数据能够正确的写入到他们关联的订单信息A所在的分片上。这样才能保证订单A在join查询订单明细时,向请求者返回正确的查询结果。MyCat提供的这种分片模式称为ER分片/智能分片。在后续4-4、4-5和4-6节中,本文将和读者一起来讨论MyCat中支持数据分片的两种关键分片表类型,普通分片和智能分片。我们还会一起讨

11、论MyCat中主要支持的数据分片规则,包括mod-long、partbymonth、rang-mod、rang-long、hash-int等分片规则。MyCat还支持开发人员自定义分片规则,这个自定义方式也会进行介绍。4-3-4、Share join和catlet(人工智能)MyCat还向技术人员提供了两种不同分片的查询汇总功能,其中Share join是一个简单的跨分片Join方式,目前支持 2 个表的 join,原理就是解析 SQL 语句,拆分成单表的 SQL 语句执行,然后把各个节点的数据汇 集;另外一种catlet人工智能分片查询功能,是将Join查询语句分析后,从指定分片提取查询结果

12、的前半部分,然后将查询结果送入其它分片以便可以结合到这个结果所关联的其他数据。Share join查询的做法和人工智能分片查询的做法往往无法实现高性能处理,所以这两种不同分片的数据关联查询方式只适合开发人员使用,不建议在生产环境中使用。4-4、普通分片场景示例数据表普通分片是比较好理解的概念,即是说一个拥有相对独立业务的数据表,按照一定的拆分规则将数据分别存储在若干个独立的数据库中的操作。能够进行这种分片操作的数据表的特点是,业务耦合度一般较低或者属于基础性功能模块;这种数据表也可能存在和其它数据的关联,但是关联的是一个或者多个字典数据表;即使这种数据表存在直接关联的其它业务数据,那么后者的数

13、据规模和变化频率也不会在可预见的时间内进行数据分片操作。这种场景在实际业务中是比较常见的,典型的就是用户基础信息:在产品第N次迭代时,考虑了后续几个月内注册用户量将突破1000万大关,且半年内将继续成几何级增长。这时架构师就必须考虑对“用户中心子系统”中用户基本信息进行分库处理。用户基本信息快速迁移/割接的问题很好解决,由于目前用户基本信息只有百万左右,所以可以考虑在每个分片库先做整体冗余,然后再后续运维工作中再进行数据清扫。也可以在最初阶段就考虑合适的分片规则,保证这几百万数据在后续存储方案升级中将可以作为整个MySQL分库分表集群的第一个分片节点组(后文在讲解分片规则时会详细讲到)“用户中

14、心子系统”中我们为可能的5000万用户数据规模规划了5个分片,每个分片中做两组读写分离,每组读写分离包含一个写节点和二至三个读节点。并且这两个组的写节点互为主从。以下是schema.xml主配置文件中重要的设置内容:<mycat:schema xmlns:mycat="http:/io.mycat/"> <!- 在这个测试示例中一共有三张逻辑表 -> <schema name="usercenterSchema" checkSQLschema="false" sqlMaxLimit="200&q

15、uot;> <!- 以下是若干张不需要进行数据分片的字典性质的数据表 它们都以全局表的形式在每个分片节点上拥有完全一致的数据 -> <table name="dictionaryA" primaryKey="Id" type="global" dataNode="dn1,dn2,dn3,dn4,dn5" /> <table name="dictionaryB" primaryKey="Id" type="global"

16、dataNode="dn1,dn2,dn3,dn4,dn5" /> <table name="dictionaryC" primaryKey="Id" type="global" dataNode="dn1,dn2,dn3,dn4,dn5" /> <table name="dictionaryD" primaryKey="Id" type="global" dataNode="dn1,dn2,dn3,d

17、n4,dn5" /> <!- 这是在本示例中我们需要进行分片的用户基本信息数据表 -> <table name="usertable" primaryKey="Id" dataNode="dn1,dn2,dn3,dn4,dn5" rule="mod-long"/> </schema> <!- 设置五个逻辑节点/分片节点 这里注意一个问题,如果为了节约成本可以将某两个或者某几个逻辑节点 运行在相同的MySQL物理机群上,那么建议database属性取不同的名字

18、 例如stacks01、stacks02、stacks03. -> <dataNode name="dn1" dataHost="dataHost1" database="stacks" /> <dataNode name="dn2" dataHost="dataHost2" database="stacks" /> <dataNode name="dn3" dataHost="dataHost3"

19、database="stacks" /> <dataNode name="dn4" dataHost="dataHost4" database="stacks" /> <dataNode name="dn5" dataHost="dataHost5" database="stacks" /> <!- 第一个分片节点中定义了两个写操作节点和其对应的读操作节点, writeType设置为0,表示一般情况下所有写操作都发送到配

20、置的第一个写节点上, 另一个写节点和读节点充当standby的角色。 -> <dataHost name="dataHost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="2"> <heartbeat>select user()</heartbeat&

21、gt; <writeHost host="dataHost1_hostM1" url="40:3306" user="root" password="123456"> <readHost host="dataHost1_hostS11" url="41:3306" user="root" password="123456"/> <readHost host=&

22、quot;dataHost1_hostS12" url="42:3306" user="root" password="123456"/> </writeHost> <writeHost host="dataHost1_hostM2" url="50:3306" user="root" password="123456"> <readHost host="

23、;dataHost1_hostS21" url="51:3306" user="root" password="123456"/> <readHost host="dataHost1_hostS22" url="52:3306" user="root" password="123456"/> </writeHost> </dataHost> .</my

24、cat:schema>以上配置示例中关于分片规则的部分(table标签的rule属性),我们将在后文中专门进行介绍。读者在这里只需要知道“mod-long”是一种长整形取余的分片方式就可以了,另外全局表和分片表唯一的设置差别就是全局表需要明确指定type属性,而分片表不需要。很明显至少从现在的情况看,用户基本信息虽然有直接关联的数据信息,但是关联的都是字典性质的数据表,这些数据表都被设置为全局表,所以在任何分片中用户基本信息都可以找到与它正确关联的信息。最后需要再次注意的MyCat并不负责数据同步过程,所以所有节点的数据同步还需要技术人员根据顶层设计自行配置。4-5、ER分片及使用限制4

25、-5-1、ER分片基本使用经过上一节示例的技术迭代过程,为不久的将来线上业务系统达到5000万级别用户基本信息的数据存储规模的准备工作就完成了,但是新的要求又来了:我们需要记录最近一年时间内用户对基本信息的修改情况。这部分修改情况可能来源于另一套日志采集系统(例如一套基于Apache Flume + Apache Kafka + Apache Storm的日志数据实时采集分析平台)也可能直接来源于业务系统对数据变化的判断,这里我们并不讨论数据的来源问题,而只讨论这部分用户基本信息变化数据的存储情况假设技术团队已经决定使用关系型数据库存储这些变化数据。很显然用户基本信息的修改明细和用户基本信息存

26、在很强的关联关系,且用户基本信息的修改明细也需要进行分片。当用户基本信息A进入分片数据库X时,需要和这个用户基本信息进行关联的修改明细信息也必须正确进入数据库X,这样才能保持数据关联的正确性。这是因为:如果数据表存在外键约束设定,那么用户信息修改明细错误写入分片时就会导致写操作直接报错分片数据库无法找到关联的用户信息。而使用外键约束又是明确被建议的数据库设计方式。即使数据表不存在外键约束设定,虽然用户信息修改明细可以写入和用户基本信息不一致的分片,但是在基于用户基本信息进行关联查询时就无法查询到正确的关联信息。看来要在保持性能的前提下解决这个问题,就必须保证父级表和子级表在同时需要分片时,相关

27、联数据能够正确写入相同的分片中,MyCat称这样的分片表为ER分片表。MyCat的主配置文件中使用table标签的子标签childTable对ER分片表的关系进行标识。如下示例:.<table name="usertable" primaryKey="Id" dataNode="dn1,dn2" rule="mod-long"> <childTable name="booktable" primaryKey="Id" joinKey="author

28、id" parentKey="Id"/></table>.关于table标签已经在上文中介绍过了,这里的使用方式相似。需要注意的是childTable标签的几个关键属性:primaryKey属性:该属性和table标签中的primaryKey属性意义相同,表示该逻辑表对应真实表的主键。joinKey属性和parentKey属性:在进行childTable表数据插入时,MyCat会首先依据joinKey属性设置的字段拿到本次数据插入时该字段的值,然后再根据parentKey属性指定的父级Table的列信息生成查询语句,以便确定将要插入的这条数据,其

29、父级数据在哪个分片上。有的读者可能就要提问了:为什么不采用已设置的分片规则重新计算出数据存放的分片呢?这是因为分片规则可能会产生变化,即使分片规则没有产生变化,很多规则下作为计算基准的“可用分片数量”也可能产生了变化。了解了ER分片表的基本工作方式,我们就可以对上一节用户中心使用的普通分片场景进行调整,在其为用户基本信息修改明细配置ER分片关系,调整后的配置文件如下所示(只列出了关键的变化位置,其他全局表的设置没有变化):.<!- 原来的全局表设置还是没有变 -><table name="dictionaryD" primaryKey="Id&q

30、uot; type="global" dataNode="dn1,dn2,dn3,dn4,dn5" /><!- 这是在本示例中我们需要进行分片的用户基本信息数据表-><table name="usertable" primaryKey="Id" dataNode="dn1,dn2,dn3,dn4,dn5" rule="mod-long"> <!- 用户基本信息修改明细 -> <childTable name="user

31、modifyDetails" primaryKey="Id" joinKey="userid" parentKey="Id"/></table>.请注意,我们并没有为childTable设置分片规则和可以使用的分片节点,这是因为childTable每一条数据存储的位置是由它父级Table表中每一条数据的实际存储位置决定。通过ER分片我们可以保证类似如下的join关联语句能够在每个分片中正确执行,并被汇总到MyCat服务上。这是MyCat服务对这些分片结果进行正确的二次整合的前提条件。# 无论是这两张数据表做

32、怎样的join关联,都可以保证没个分片中的查询结果是正确的。# 如以下这种查询方法select usertable.*,usermodifyDetails.fieldname,usermodifyDetails.fieldnewValue from usertableleft join usermodifyDetails on usertable.Id = usermodifyDetails.userid# 或者这种查询方法,又或者其它的只涉及这两个数据表的一对多、多对一关联查询select usermodifyDetails.*,usertable.Id,usertable.username

33、 from usermodifyDetailsleft join usertable on usertable.Id = usermodifyDetails.userid主要分片规则上文提到MyCat的逻辑表支持多种分片规则,表现于schema配置文件中中table标签的rule属性。本节将以MyCat Version 1.6版为基础,介绍几种经常使用的分片规则,这些分片规则都通过rule.xml文件进行定义和配置。4-6-1、分片枚举sharding-by-intfile.<tableRule name="sharding-by-intfile"> <r

34、ule> <!- columns表示分片计算时的取值列,记得设置成您的数据表列名 -> <columns>sharding_id</columns> <algorithm>hash-int</algorithm> </rule></tableRule>.<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"> <property name="

35、mapFile">partition-hash-int.txt</property></function>.实现类,这个分片规则是直接按照partition-hash-int.txt文件(默认)中定义的固定分片规则进行分片。其中可以设置的属性包括:type:这个分片规则也不支持按照字符串为分片依据,当type属性的值为0时表示Integer,非零表示String,默认的属性值为0。defaultNode:从MyCat Version 1.6版本开始,rule.xml配置文件中就不再设置默认分片节点了。但是这个属性还是存在的,如果碰到不识别的枚举值,就让它

36、路由到默认节点;如果不配置默认节点(defaultNode值小于0表示不配置默认节点),碰到不识别的枚举值就会报错:like this:cant find datanode for sharding column:column_name val:ffffffff。mapFile:固定枚举分片所设置的枚举规则存储的文件,默认的文件名就是partition-hash-int.txt,并且和rule.xml文件在一个工作目录下。partition-hash-int.txt文件的定义类似如下:# 如果取值列的值为10000则将数据送入0号分片10000=010010=1我们来跟踪一下Partition

37、ByFileMap类的源码,会发现这个分片规则工作很简单简单有它的好处,不会在分片过程中浪费太多的计算时间计算分片目标:.public class PartitionByFileMap extends AbstractPartionAlgorithm implements RuleAlgorithm . / 默认节点在map中的key private static final String DEFAULT_NODE = "DEFAULT_NODE" / 在配置文件的配置规则会被初始化到app2Partition对象中 / 这个Map对象的value表示分片节点编号从0开始编

38、号 private Map<Object, Integer> app2Partition; . public Integer calculate(String columnValue) / columnValue既是当前将要进行的数据分片所取的列值 Object value = columnValue; / 如果配置文件设置了type为0,则需要将值转为数字 if(type = 0) value = Integer.valueOf(columnValue); Integer rst = null; / 取得关联的分片,从app2Partition对象中 / 注意,这个地方的变量命名

39、可能会使阅读者产生歧义 / 实际上这value对象代表了app2Partition中的key信息。 Integer pid = app2Partition.get(value); if (pid != null) rst = pid; / 如果没有取到对应的分片,则试图取默认分片 else rst =app2Partition.get(DEFAULT_NODE); return rst; .4-6-2、取模的分片方式:mod-long.<tableRule name="mod-long"> <rule> <!- columns表示分片计算时的取

40、值列,记得设置成您的数据表列名 -> <columns>id</columns> <algorithm>mod-long</algorithm> </rule></tableRule>.<function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!- how many data nodes -> <property name="count"

41、>2</property></function>.实现类:,这个分片规则只适合数字形式的列,并且规则的实现更简单:既是按照将要插入数据的指定列进行取模运算。该规则需要设置一个count属性,这个数据就是现存的分片节点的个数用于进行取模运算的基数:.public class PartionByMod extends AbstractPartionAlgorithm implements RuleAlgorithm . # 该属性设置当前的分片节点数量 private int count; . public Integer calculate(String colum

42、nValue) # 取绝对值 BigInteger bigNum = new BigInteger(columnValue).abs(); # 返回取模运算的值 # 如果您不清楚BigInteger类型的特性,请自行查阅资料 return (bigNum.mod(BigInteger.valueOf(count).intValue(); .4-6-3、约定数字范围auto-sharding-long.<tableRule name="auto-sharding-long"> <rule> <!- columns表示分片计算时的取值列,记得设置成

43、您的数据表列名 -> <columns>id</columns> <algorithm>rang-long</algorithm> </rule></tableRule>.<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong"> <property name="mapFile">autopartition-long.txt</p

44、roperty></function>.实现类:,这个规则下MyCat会使用autopartition-long.txt文件(默认的文件路径)中已经设置好的数字范围决定将数据存储到哪个分片中,autopartition-long.txt文件中的配置类似如下:# 这个配置文件的含义是# 指定分片列的数值为05000000的数据都在0号分片中0-500M=0# 指定分片列的数值为500000010000000的数据都在1号分片中500M-1000M=11000M-1500M=2如果您的业务系统后续考虑方便的增加分片节点,可以考虑优先使用这种分片规则,因为这种固定设置的分片规则既简

45、单又实用。以下是AutoPartitionByLong类的重要判断代码,通过查看代码中主要的分片过程,有助于我们理解这种分片规则:.public class AutoPartitionByLong extends AbstractPartionAlgorithm implements RuleAlgorithm private String mapFile; / defaultNode设置为-1,表示没有默认的分片节点。 private int defaultNode = -1; . / longRongs在初始化时被设置 / 在autopartition-long.txt文件中有几行设置范围

46、,就有几个LongRange对象。(参见该类中没有列出的initialize方法) private LongRange longRongs; . public Integer calculate(String columnValue) long value = Long.valueOf(columnValue); Integer rst = null; for (LongRange longRang : this.longRongs) / 如果条件成立,就说明找到了可以承载columnValue值的那个数据库分片编号 if (value <= longRang.valueEnd &

47、;& value >= longRang.valueStart) return longRang.nodeIndx; /数据超过范围,暂时使用配置的默认节点(如果有的话) if(rst =null && defaultNode >= 0) return defaultNode; / 返回null,则说明没有找到可承载columnValue的分片编号 / 也没有进行默认分片的设置(这是MyCat会报错) return rst; . / LongRange对象有三个属性: static class LongRange / 该属性为当前范围所存储的分片编号 pub

48、lic final int nodeIndx; / 该属性为范围的开始值 public final long valueStart; / 该属性为范围的结束值 public final long valueEnd; . .注意,在以上代码片段的注释中有一句“暂时使用配置的默认节点”。这句话不是笔者的注释,而是该类的编写者“wuzhi”加的注释,也就是说这里判断成立后的处理代码可能会在后续版本中进行修改。4-6-4、自然月分片:sharding-by-month.<tableRule name="sharding-by-month"> <rule> &

49、lt;!- columns属性同样表示分片计算时的取值列,记得设置成您的数据表列名 -> <columns>create_date</columns> <algorithm>partbymonth</algorithm> </rule></tableRule>.<function name="partbymonth" class="io.mycat.route.function.PartitionByMonth"> <property name="d

50、ateFormat">yyyy-MM-dd</property> <property name="sBeginDate">2015-01-01</property></function>.实现类,该分片规则可按照每个自然月为一个分片数据库,也就是说技术团队可以将数据放置到分片数据库中,并且每个月增加一个分片数据库,而不对之前的分片数据库产生影响。该规则有三个属性可以设置:dateFormat:日期数据列的数据格式。该属性必须要设置,否则分区规则将无法进行正常工作。默认的配置文件中,这个数据都存在默认值,就是以上

51、示例中看到的yyyy-MM-dd。sBeginDate:这个属性是指存储的数据中,涉及的分区日期列的开始时间。sEndDate:这个属性是指存储的数据中,涉及的分区日期列的结束时间,该值可以不进行设置。实际上设置sEndDate和不设置sEndDate,PartitionByMonth将按照两种不同的方式工作,详见下文代码分析。.public class PartitionByMonth extends AbstractPartitionAlgorithm implements RuleAlgorithm . / 以下三个属性就是技术人员在配置文件中设置的属性 private String s

52、BeginDate; private String dateFormat; private String sEndDate; / 日期计算对象,专门用于根据设置开始事件和结束时间参与分片计算 private Calendar beginDate; private Calendar endDate; / 这个参数很重要,分区总数,后面的代码分析中会进行讲述 private int nPartition; . public void init() . / 关键点在这里,当技术人员设置了sEndDate的值后 / 该规则将计算一个nPartition的参数值 if(sEndDate!=null&am

53、p;&!sEndDate.equals("") endDate = Calendar.getInstance(); endDate.setTime(new SimpleDateFormat(dateFormat).parse(sEndDate); / nPartition 为结束时间和开始时间的年差 * 12 / 加上结束时间和开始时间的月差 最后再 + 1 / 如果结束时间为2016-05,开始时间为2015-08则: / 12 + 7 - 4 + 1 = 16个月 nPartition = (endDate.get(Calendar.YEAR) - beginD

54、ate.get(Calendar.YEAR) * 12 + endDate.get(Calendar.MONTH) - beginDate.get(Calendar.MONTH) + 1; / nPartition出现小于0的情况,则说明开始时间和结束时间的设置错误。 if (nPartition <= 0) throw new java.lang.IllegalArgumentException("Incorrect time range for month partitioning!"); / 如果没有设置结束时间,则nPartition为-1。 else nPa

55、rtition = -1; . / 正式的分片计算 public Integer calculate(String columnValue) try int targetPartition; Calendar curTime = Calendar.getInstance(); curTime.setTime(formatter.get().parse(columnValue); / 目标分片的计算方式为: / 当前时间和开始时间的年差 * 12 / 加上当前时间和开始时间的月差 / 如果开始时间为2015-08,当前时间为2015-12则: / 0 + 11 - 7 = 4,也就是第五个分片库

56、 targetPartition = (curTime.get(Calendar.YEAR) - beginDate.get(Calendar.YEAR) * 12 + curTime.get(Calendar.MONTH) - beginDate.get(Calendar.MONTH); / 如果设置了结束时间,说明分片数量是有限制的,需要在reCalculatePartition方法中,基于targetPartition进行取余运算 if (nPartition > 0) targetPartition = reCalculatePartition(targetPartition);

57、 return targetPartition; catch (ParseException e) throw new IllegalArgumentException(new StringBuilder().append("columnValue:").append(columnValue).append(" Please check if the format satisfied.").toString(),e); . / 该私有方法负责基于nPartition进行取余 private int reCalculatePartition(int targetPartition) if (targetPartition < 0) targetPartition = nPartition -

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论