笔者带你剖析轻量级Sharding中间件——Kratos1.x

之因此编写Kratos其实存在一个小插曲,当笔者满山遍野寻找成熟、稳定、高性能的Sharding中间件时,确实是翻山越岭,只不过始终没有找到一款合适笔者项目场景的中间件产品。依稀记得当年第一款使用的Sharding中间件就是淘宝的TDDL3.0,只惋惜如今拿不到源码。而其它的中间件,大多都是基于Proxy的,相信作过度布式系统的人都知道,抛开网络消耗所带来的性能问题不谈,多一个外围系统依赖就意味着须要多增长和承担一份风险,所以与应用集成的方式,则成为笔者选择的首要条件。固然不是说基于Proxy方式的Sharding中间件很差,只能说我我的并不看重通用型需求,仅此而已。其实目前社区很是活跃的MyCat,笔者在这里要批评一下,既然开源,那么就请在GitHub上贴上使用手册,而不是配置手册,由于对于一个任何一个DVP而言,他要的是迅速上手的帮助文档,而不是要使用大家的开源产品还须要在淘宝上购买几十块一本的网络书,对于这一点,我很是鄙视和厌恶java

 

许多开发人员动不动就大谈分库分表来显示本身的成就感那么笔者须要作的事情就是将其拉下神坛,让你们切切实实感觉到一款平民化技术所带来的亲民性。做为一款数据路由工具,Kratos采用与应用集成的方式为开发人员带来超强的易用性。就目前而言,笔者测试环境上,物理环境选用为32库和1024表的库内分片模式,所以笔者就先没必要王婆卖瓜,自卖自诩其所谓的高性能和高稳定。至于你是否可以从Kratos中获益,取决于你是否想用最简单,最易上手的方式来解决分库分表场景下的数据路由工做。mysql

 

笔者此篇博文,并非宣导和普及分库分表理论,而是做为Kratos货真价实的Sharding权威指南手册呈现给你们。目前Kratos1.3.0版本已经在Github上开源,地址为:https://github.com/gaoxianglong/kratosgit

 

目录github

1、Kratos简介;正则表达式

2、互联网当下的数据拆分过程;算法

3、目前市面上常见的一些Sharding中间件产品对比;sql

4、Kratos的架构原型;数据库

5、动态数据源层的Master/Slave读写分离;express

6、Kratos的Sharding模型;网络

7、Sharding之库内分片;

8、Sharding之一库一片;

9、自动生成全局惟一的sequenceId;

10、自动生成kratos分库分表配置文件;

11、注意事项;

 

1、Kratos简介

由于找不到合适的Shading中间件,其次一些开源的Shading中间件动不动就几万行的代码真有必要吗?所以诞生了编写本身中间件的想发。Kratos这个名字来源于笔者以前在PS3平台玩的一款ACT游戏《战神3》中嗜血杀神的主角奎爷Kratos,尽管笔者的Kratos并无展示出秒杀其余Sharding中间件的霸气,但Kratos要作的事情很纯粹,仅仅就只是处理分库分表场景下的数据路由工做,它处于数据路由层,介于持久层与JDBC之间,所以没有其它看似有用实则花哨的鸡肋功能,而且与应用层集成的方式注定了Kratos必然拥有更好的易用性

 

对于开发人员而言,在使用Kratos的时候,就好像是在操做同一个数据源同样,也就是说,以前你的sql怎么写,换上Kratos以后,业务逻辑无需作出变更,你只须要关心你的逻辑便可,数据路由工做,你彻底能够放心的交由Kratos去处理。Kratos站在巨人的肩膀上,必然拥有更大的施展空间。首先Kratos依赖于Spring JDBC,那么若是你习惯使用JdbcTemplate,那就都不是问题,反之你能够先看一下笔者的博文笔者带你剖析Spring3.x JDBC。其次Kratos目前仅仅只支持Mysql数据库,对于其余RDBMS类型的数据库,基本上之后也不会支持,简单来讲,通用型需求并非Kratos的目标,作好一件事才是真正的实惠

 

 kratos的优势

一、动态数据源的无缝切换;
二、master/slave一主一从读写分离;
三、单线程读重试(取决于的数据库链接池是否支持);
四、单独且友好支持Mysql数据库,不支持其它RDBMS库;
五、非Proxy架构,与应用集成,应用直连数据库,下降外围系统依赖带来的down机风险;
六、使用简单,侵入型低,站在巨人的肩膀上,依赖于Spring JDBC;
七、不作真正意义上的Sql解析任务,规避Sql解析过程当中由词法解析、语法解析、语义解析等操做所带来的

      性能延迟,仅用正则表达式解析 片名和Route条件,解析过程仅耗时约<=1ms;
八、分库分表路由算法支持2类4种分片模式,库内分片/一库一片;
九、提供自动生成sequenceId的API支持;
十、提供自动生成配置文件的支持,下降配置出错率;
十一、目标和职责定位明确,仅专一于Sharding,不支持其它多余或鸡肋功能、无需兼容通用性,所以核心代

        码量少、易读易维护

 

2、互联网当下的数据拆分过程

对于一个刚上线的互联网项目来讲,因为前期活跃度并不大,并发量相对较小,所以企业通常都会选择将全部数据存放在一个物理DB中进行读写操做。但随着后续的市场推广力度不断增强,活跃度不断提高,这时若是仅靠一个DB来支撑全部读写压力,就会显得力不从心。因此通常到了这个阶段,大部分Mysql DBA就会将数据库设置为读写分离状态(一主一从/一主多从),Master负责写操做,而Slave负责读操做。按照二八定律,80%的操做更可能是读操做,那么剩下的20%则为写操做,通过读写分离后,大大提高了单库没法支撑的负载压力。不过光靠读写分离并不会一劳永逸,若是活跃度再次提高,相信又会再次碰见数据库的读写瓶颈。所以到了这个阶段,就须要实现垂直分库。

 

所谓垂直分库,简单来讲就是根据业务的不一样将本来冗余在单库中的业务表拆散,分布到不一样的业务库中,实现分而治之的读写访问操做。固然咱们都知道,传统RDBMS数据库中的数据表随着数据量的暴增,从维护性和高响应的角度去看,不管任何CRUD操做,对于数据库而言,都是一件极其伤脑筋的事情。即使设置了索引,检索效率依然低下,由于随着数据量的暴增,RDBMS数据库的性能瓶颈就会逐渐显露出来。这一点,Nosql数据库却是作得很好,固然架构不一样,因此不予比较。那么既然已经到了这个节骨眼上了,惟一的杀手锏就是在垂直分库的基础之上进行水平分区,也就是咱们常说的Sharding。

 

简单来讲,水平分区要作的事情,就是将本来冗余在单库中的业务表分散为N个子表(好比tab_000一、tab_000二、tab_000三、tab_0004...)分别存放在不一样的子库中。理论上来说,子表之间经过某种契约关联在一块儿,每一张子表均按段位进行数据存储,好比tab_0000存储1-10000的数据,而tab_0001存储10001-20000的数据。通过水平分区后,必然可以将本来由一张业务表维护的海量数据分配给N个子表进行读写操做和维护,大大提高了数据库的读写性能,下降了性能瓶颈。基于分库分表的设计,目前在国内一些大型网站中应用的很是广泛。

 

固然一旦实现分库分表后,将会牵扯到5个很是关键的问题,以下所示:

一、单机ACID被打破,分布式事务一致性难以保证;

二、数据查询须要进行跨库跨表的数据路由;

三、多表之间的关联查询会有影响;

四、单库中依赖于主键序列自增时生成的惟一ID会有影响;

五、强外键(外键约束)难以支持,仅能考虑弱外键(约定);

 

3、目前市面上常见的一些Sharding中间件产品对比

其实目前市面上的分库分表产品不在少数,可是这类产品,更多的是基于Proxy架构的方式,在对于不看重通用性的前提下,基于应用集成架构的中间件则只剩下淘宝的TDDL和Mysql官方的Fabric。其实笔者仍是很是喜欢TDDL的,Kratos中所支持的库内分片就是效仿的TDDL,相信你们也看得出来笔者对TDDL的感情。可是TDDL并不是是绝对完美的,其弊端一样明显,好比:社区推动力度缓慢、文档资料匮乏、过多的功能、外围系统依赖,再加上致命伤非开源,所以注定了TDDL没法为欣赏它的非淘宝系用户服务。而Fabric,笔者接触的太少了,而且正式版发行时间仍是过短了,所以就不想当小白鼠去试用,避免出现hold不住的状况。目前常见的一些Shardig中间件产品对比,如图A-1所示:

图A-1 常见的Shading中间件对比

 

在基于Proxy架构的Sharding中间件中,大部分的产品基本上都是衍生子Cobar,而且这类产品对分片算法的支持都仅限于一库一片的分片方式。对于库内分片和一库一片等各自的优缺点,笔者稍后会进行讲解。具体使用什么样的中间件产品,还须要根据具体的应用场景而定,固然若是是你正在愁找不到完善的使用手册、配置手册之类的文档资料,Kratos你能够优先考虑。

 

4、Kratos的架构原型

简单来讲,分库分表中间件无非就是根据Sharding算法对持有的多数据源进行动态切换,这是任何Sharding中间件最核心的部分。一旦在程序中使用Kratos后,应用层将会持有N个数据源,Kratos经过路由条件进行运算,而后经过Route技术对数据库和数据表进行读写操做。在此你们须要注意,Kratos内部并无实现本身的ConnectionPool,这也就意味着,给了开发人员极大的自由,使之能够随意切换任意的ConnectionPool产品,好比你以为C3P0没有BonePC性能高,那么你能够切换为BonePC。

 

对于开发人员而言,你并不须要关心底层的数据库和表的划分规则,程序中任何的CRUD操做,都像是在操做同一个数据库同样,而且读写效率还不可以比以前低太多(好比几毫秒或者实际毫秒以内完成),而Kratos就承担着这样的一个任务。Kratos所处的领域模型定位,如图A-2所示:

图A-2 Kratos的领域模型定位

 

 如图A-2所示,Kratos的领域模型定位处于持久层和JDBC之间。以前笔者曾经说起过,Kratos是站在巨人的肩膀上,这个巨人正是Spring。简单来讲,Kratos重写了JdbcTemplate,并使用了Spring提供的AbstractRoutingDataSource做为动态数据源层。所以从另一个侧面反应了Kratos的源码注定是简单、轻量、易阅读、易维护的,由于Kratos更多的关注点只会停留在Master/Slave读写分离层和分库分表层。咱们知道通常的Shading中间件,动不动就几万行代码,其中得“猫腻”有不少,不只数据库链接池要本身写、动态数据源要本身写,再加上一些杂七杂八的功能,好比:通用性支持、多种类型的RDBMS或者Nosql支持,那么代码天然冗余,可读性极差。在Kratos中,这些都问题彻底会“滚犊子”,由于Kratos只会考虑如何经过Sharding规则实现数据路由。Kratos的3层架构,如图A-3所示:

图A-3 Kratos的3层架构

 

既然Kratos只考虑最核心的功能,同时也就意味着它的性能恒定指标还须要结合其余第三方产品,好比Kratos的动态数据源层所使用的ConnectionPool为C3P0,尽管很是稳定的,可是性能远远比不上使用BonePC,所以你们彻底能够将Kratos看作一个高效的黏合剂,它的核心任务就是数据路由,你别期望Kratos还能为你处理边边角角的零碎杂事,想要什么效果,自行组合配置,这就是Kratos,一个简单、轻量级的Sharding中间件。Kratos的应用整体架构,如图A-4所示:

图A-4 Kratos的应用整体架构

 

 5、动态数据源层的Master/Slave读写分离

当你们对Kratos有了一个基本的了解后,那么接下来咱们就来看看如何在程序中使用Kratos。com.gxl.kratos.jdbc.core.KratosJdbcTemplate是Kratos提供的一个Jdbc模板,它继承自Spring的JdbcTemplate。简单来讲,KratosJdbcTemplate几乎支持JdbcTemplate的全部方法(除批量操做外)。对于开发人员而言,只须要将JdbcTemplate替换为KratosJdbcTemplate便可,除此以外,程序中没有其余任何须要进行修改的地方,这种低侵入性相信你们都应该可以接受。

 

通常来讲,数据库的主从配置,既能够一主一从,也能够一主多从,但目前Kratos仅支持一主一从。接下来咱们再来看看如何在配置文件中配置一主一从的读写分离操做,以下所示:

Xml代码   收藏代码
  1. <import resource="datasource1-context.xml" />  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  
  3. <context:component-scan base-package="com">  
  4.     <context:include-filter type="annotation"  
  5.         expression="org.aspectj.lang.annotation.Aspect" />  
  6. </context:component-scan>  
  7. <bean id="kJdbcTemplate" class="com.gxl.kratos.jdbc.core.KratosJdbcTemplate">  
  8.     <constructor-arg name="isShard" value="false"/>  
  9.     <property name="dataSource" ref="kDataSourceGroup"/>  
  10.     <property name="wr_weight" value="r1w0"/>  
  11. </bean>  
  12. <bean id="kDataSourceGroup" class="com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup">  
  13.     <property name="targetDataSources">  
  14.         <map key-type="java.lang.Integer">  
  15.             <entry key="0" value-ref="dataSource1"/>  
  16.             <entry key="1" value-ref="dataSource2"/>  
  17.         </map>  
  18.     </property>  
  19. </bean>  

   

上述程序实例中,com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup就是一个用于管理多数据源的Group,它继承自Spring提供的AbstractRoutingDataSource,并充当了动态数据源层的角色,由此基础之上实现DBRoute。咱们能够看见,在<entry/>标签中,key属性指定了数据源索引,而value-ref属性指定了数据源,经过这种简单的键值对关系就能够明确的切换到具体的目标数据源上。

 

在com.gxl.kratos.jdbc.core.KratosJdbcTemplate中,isShard属性实际上就是一个Sharding开关,缺省为false,也就意味着缺省是没有开启分库分表的,那么在不Sharding的状况下,咱们依然可使用Kratos来完成读写分离操做。在wr_weight属性中定义了读写分离的权重索引,也就是说,咱们有多少个主库,就必定须要有多少个从库,好比主库有1个,从库也应该是1个,所以KratosDatasourceGroup中持有的数据源个数就应该一共是2个,索引从0-1,若是主库的索引为0,那么从库的索引就应该为1,也就是“r1w0”。当配置完成后,一旦Kratos监测到执行的操做为写操做时,就会自动切换为主库的数据源,而当操做为读操做的时候,就会自动切换为从库的数据源,从而实现一主一从的读写分离操做。

 

6、Kratos的Sharding模型

目前市面上的Sharding中间的分库分表算法有2种最多见的类型,分别是库内分片和一库一片。笔者先从库内分片开始谈起,而且会在后续进行比较这2种分片算法的优劣势,让你们更好的进行选择使用。

 

库内分片是TDDL采用的一种分片算法,这种分片算法相对来讲较为复杂,由于不只须要根据路由条件计算出数据应该落盘到哪个库,还须要计算须要落盘到哪个子表中。好比咱们生产上有32个库,数据库表有1024张,那么平均每一个库中存放的子表数量就应该是32个。库内分片算法示例,如图A-5所示:

图A-5 库内分片

 

一库一片目前是很是常见的一种分片算法,它同时具有了简单性和易维护性等特色。简单来讲,一旦经过路由算法计算出数据须要落盘到哪个库后,就等于间接的计算出了须要落盘到哪个子表下。假设咱们有1024个子表,那么对应的数据库也应该是1024个,当计算出数据须要落盘到第105个库的时候,天然子表也就是第105个。一库一片算法示例,如图A-6所示:

图A-6 一库一片

 

那么咱们究竟在生产中应该选择库内分片仍是一库一片呢?尽管Kratos这2种分片算法都同时支持,但生产上所使用的分片算法最好是统一的,千万别一个子系统的分片算法使用的库内分片,而另一个子系统使用的是一库一片,由于这样对于DBA来讲将会极其痛苦,不只维护极其困难,数据迁移也是一个头痛的问题。笔者建议优先考虑一库一片这种分片方式,由于这种分片方式维护更简单,而且在后期数据库扩容时,数据迁移工做更加容易和简单,毕竟算出来库就等于算出来表,迁移越简单就意味着生产上停应用的时间更短。固然究竟应该选择哪种分片算法,还须要你自行考虑抉择。

 

7、Sharding之库内分片

以前笔者已经说起过,Kratos的低侵入性设计只须要将本来的JdbcTemplate替换为KratosJdbcTemplate便可,除此以外,程序要不须要修改任何地方,由于读写分离操做、分库分表操做都仅仅只是在配置文件中进行配置的,无需耦合在业务逻辑中。库内分片的配置,以下所示:

Xml代码   收藏代码
  1. <import resource="datasource1-context.xml" />  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  
  3. <!-- 自动扫描 -->  
  4. <context:component-scan base-package="com">  
  5.     <context:include-filter type="annotation"  
  6.         expression="org.aspectj.lang.annotation.Aspect" />  
  7. </context:component-scan>  
  8. <bean id="kJdbcTemplate" class="com.gxl.kratos.jdbc.core.KratosJdbcTemplate">  
  9.     <!-- Sharding开关 -->  
  10.     <constructor-arg name="isShard" value="true"/>  
  11.         <property name="dataSource" ref="kDataSourceGroup"/>  
  12.     <!--读写权重 -->  
  13.         <property name="wr_weight" value="r32w0"/>  
  14.     <!-- 分片算法模式,false为库内分片,true为一库一片 -->  
  15.         <property name="shardMode" value="false"/>  
  16.     <!-- 分库规则 -->  
  17.         <property name="dbRuleArray" value="#userinfo_id|email_hash# % 1024 / 32"/>  
  18.     <!-- 分表规则 -->  
  19.         <property name="tbRuleArray" value="#userinfo_id|email_hash# % 1024 % 32"/>  
  20. </bean>  
  21. <bean id="kDataSourceGroup" class="com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup">  
  22.     <property name="targetDataSources">  
  23.         <map key-type="java.lang.Integer">  
  24.             <entry key="0" value-ref="dataSource1"/>  
  25.             <!-- 省略一部分数据源... -->  
  26.             <entry key="63" value-ref="dataSource2"/>  
  27.         </map>  
  28.     </property>  
  29. </bean>  

 

上述程序示例中,笔者使用的是一主一从读写分离+库内分片模式。主库一共是32个(1024个子表,每一个库包含子表数为32个),那么天然从库也就是32个,在KratosDatasourceGroup中一共会持有64个数据源,数据源索引为0-63。那么在KratosJdbcTemplate中首先要作的事情是将分库分片开关打开,而后读写权重索引wr_weight属性的比例是“r32w0”,这也就意味着0-31都是主库,而32-63都是从库,Kratos会根据这个权重索引来自动根据所执行的操做切换到对应的主从数据源上。属性shardMode其实就是指明了须要Kratos使用哪种分片算法,true为一库一片,而false则为库内分片。

 

属性dbRuleArray指明了分库规则,“#userinfo_id|email_hash# % 1024 / 32”指明了路由条件可能包括两个,分别为userinfo_id和email_hash。而后根据路由条件先%tbSize,最后在/dbSize,便可计算出具体的数据源。在此你们须要注意,库的倍数必定要是表的数量,不然数据将没法均匀分布到全部的子表上。或许你们有个疑问,为何路由条件会有多个呢?这是由于在实际的开发过程当中,咱们全部的查询条件都须要根据路由条件来,而且实际状况不可能只有一个理由条件,甚至有可能更多(好比反向索引表)。所以经过符号“|”分隔开多个路由条件。当一条sql执行时,Kratos会匹配sql条件中的第一个数据库参数字段是不是分库分表条件,若是不是则会抛出异常com.gxl.kratos.jdbc.exception.ShardException。分表规则“#userinfo_id|email_hash# % 1024 % 32”其实大体和分库规则同样,只不过最后并不是是/dbSize,而是%dbSize。通过分库分表算法后,一条sql就会被解析并落盘到指定的库和指定的表中。

 

8、Sharding之一库一片

一库一片相似于库内分片的配置,但又细微的不一样,以前笔者曾经说起过,使用一库一片算法后,根据路由条件计算出库后,就等于间接计算出表,那么配置文件中就只需配置分库规则便可。一库一片的配置,以下所示:

Xml代码   收藏代码
  1. <import resource="datasource1-context.xml" />  
  2. <aop:aspectj-autoproxy proxy-target-class="true" />  
  3. <!-- 自动扫描 -->  
  4. <context:component-scan base-package="com">  
  5.     <context:include-filter type="annotation"  
  6.         expression="org.aspectj.lang.annotation.Aspect" />  
  7. </context:component-scan>  
  8. <bean id="kJdbcTemplate" class="com.gxl.kratos.jdbc.core.KratosJdbcTemplate">  
  9.     <!-- Sharding开关 -->  
  10.     <constructor-arg name="isShard" value="true"/>  
  11.         <property name="dataSource" ref="kDataSourceGroup"/>  
  12.     <!--读写权重 -->  
  13.         <property name="wr_weight" value="r32w0"/>  
  14.     <!-- 分片算法模式,false为库内分片,true为一库一片 -->  
  15.         <property name="shardMode" value="true"/>  
  16.     <!-- 分库规则 -->  
  17.         <property name="dbRuleArray" value="#userinfo_id|email_hash# % 32"/>  
  18. </bean>  
  19. <bean id="kDataSourceGroup" class="com.gxl.kratos.jdbc.datasource.config.KratosDatasourceGroup">  
  20.     <property name="targetDataSources">  
  21.         <map key-type="java.lang.Integer">  
  22.             <entry key="0" value-ref="dataSource1"/>  
  23.             <!-- 省略一部分数据源... -->  
  24.             <entry key="63" value-ref="dataSource2"/>  
  25.         </map>  
  26.     </property>  
  27. </bean>  

 

上述程序示例中,笔者使用的是一主一从读写分离+一库一片模式。主库一共是32个(32个子表,每一个库包含子表数为1个),那么天然从库也就是32个,在KratosDatasourceGroup中一共会持有64个数据源,数据源索引为0-63。权重索引wr_weight属性的比例是“r32w0”,这也就意味着0-31都是主库,而32-63都是从库,Kratos会根据这个权重索引来自动根据所执行的操做切换到对应的主从数据源上。属性shardMode其实就是指明了须要Kratos使用哪种分片算法,true为一库一片,而false则为库内分片。

 

属性dbRuleArray指明了分库规则,“#userinfo_id|email_hash# % 32”指明了路由条件可能包括两个,分别为userinfo_id和email_hash。而后根据路由条件%dbSize便可计算出数据究竟应该落盘到哪个库的哪个子表下。

 

9、自动生成全局惟一的sequenceId

一旦咱们分库分表后,本来单库中使用的序列自增Id将没法再继续使用,那么这应该怎么办呢?其实解决这个问题并不困难,目前有2种方案,第一种是全部的应用统一调用一个集中式的Id生成器,另一种则是每一个应用集成一个Id生成器。不管选择哪种方案,Id生成器所持有的DB都只有一个,也就是说,经过一个通用DB去管理和生成全局觉得的sequenceId。

 

目前市面上几乎全部的Sharding中间件都没有提供sequenceId的支持,而Kratos的工具包中却为你们提供了支持。Kratos选择的是每一个应用都集成一个Id生成器,而没有采用集中式Id生成器,由于在分布式场景下,多一个外围系统依赖就意味着多一分风险,相信这个道理你们都应该懂。那么究竟应该如何使用Kratos提供的Id生成器呢?首先咱们须要单独准备一个全局DB出来,而后使用Kratos的建表语句,以下所示:

Sql代码   收藏代码
  1. CREATE TABLE kratos_sequenceid(  
  2.     k_id INT NOT NULL AUTO_INCREMENT COMMENT '主键',  
  3.     k_type INT NOT NULL COMMENT '类型',  
  4.     k_useData BIGINT NOT NULL COMMENT '申请占位数量',  
  5.     PRIMARY KEY (k_id)  
  6. )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_bin;  

 

当成功创建好生成sequenceId所须要的数据库表后,接下来要作的事情就是进行调用。生成sequenceId,以下所示:

Java代码   收藏代码
  1. /** 
  2.  * 获取SequenceId 
  3.  *  
  4.  * @author gaoxianglong 
  5.  */  
  6. public @Test void getSequenceId() {  
  7.     /* 初始化数据源信息 */  
  8.     DbConnectionManager.init("account", "pwd", "url", "driver");  
  9.     System.out.println(SequenceIDManger.getSequenceId(1, 1, 5000));  
  10. }  

 

上述程序示例中,首先须要调用com.gxl.kratos.utils.sequence.DbConnectionManager类的init()方法对数据源进行初始化,而后调用com.gxl.kratos.utils.sequence.DbConnectionManager类的getSequenceId()方法便可成功获取全局惟一的sequenceId。在此你们须要注意,Kratos生成的sequenceId是一个17-19位之间的整型,在getSequenceId()方法中,第一个参数为IDC机房编码,第二个参数为类型操做码,而最后一个参数很是关键,就是须要向DB中申请的内存占位数量(自增码)。

 

简单来讲,相信你们都知道,既然业务库都分库分表了,就是为了缓解数据库读写瓶颈,当并发较高时,一个生成sequenceId的通用数据库能扛得住吗?笔者告诉你,扛得住!就是由于内存占位。其实原理很简单,当第一次应用从Id生成器中去拿sequenceId时,Id生成器会锁表并将数据库字段k_useData更新为5000,那么第二次应用从Id生成器中去拿sequenceId时,将不会与DB建议物理会话连接,而是直接在内存中去消耗着5000内存占位数,直至消耗殆尽时,才会从新去数据库中申请下一次的内存占位。

 

那么问题又来了,若是并发访问会不会有问题呢?其实保证全局惟一性有3点,第一是程序中会加锁,其次数据库会for update,最后每个操做码都是惟一的,都会管理本身旗下的内存占位数(经过Max()函数比较,累加下一个5000)。或许你会在想,如何提高Id生成器的性能,尽量的避免与数据库创建物理会话,没错,这么想是对的,每次假设从数据库申请的占位数量是50000,那么性能确定比只申请5000好,可是这也有弊端,一旦程序出现down机,内存中的内存数量就会丢失,只能从新申请,这会形成资源浪费

 

10、自动生成kratos分库分表配置文件

Kratos的工具包中除了提供有自动生成sequenceId的功能外,还提供有自动生成分库分表配置文件等功能。笔者本身是很是喜欢这个功能。由于这很明显能够减小配置出错率。好比咱们采用库内分片模式,32个库1024个表,数据源配置文件中,须要配置的数据源信息会有32个,固然这经过手工的方式也何尝不可,无非就是一个kratos分库分表配置文件+一个dataSource文件(若是主从的话,还须要一个从库的dataSource文件),dataSource文件中须要编写32个数据源信息的<bean/>标签。可是若是咱们使用的是一库一片这种分片方式,使用的库的数量是1024个的时候呢?dataSource文件中须要定义的数据源将会是1024个?写不死你吗?你能保证配置不会出问题?

 

既然手动编写配置文件可能会出现错误,那么究竟应该如何使用Kratos提供的自动生成配置文件功能来下降出错率呢?Kratos自动生成配置文件,以下所示:

Java代码   收藏代码
  1. /** 
  2.  * 生成核心配置文件 
  3.  *  
  4.  * @author gaoxianglong 
  5.  */  
  6. public @Test void testCreateCoreXml() {  
  7.     CreateXml c_xml = new CreateXml();  
  8.     /* 是否控制台输出生成的配置文件 */  
  9.     c_xml.setIsShow(true);  
  10.     /* 配置分库分片信息 */  
  11.     c_xml.setDbSize("1024");  
  12.     c_xml.setShard("true");  
  13.     c_xml.setWr_weight("r0w0");  
  14.     c_xml.setShardMode("false");  
  15.     c_xml.setDbRuleArray("#userinfo_id|email_hash# % 1024");  
  16.     //c_xml.setTbRuleArray("#userinfo_id|email_hash# % 1024 % 32");  
  17.     /* 执行配置文件输出 */  
  18.     System.out.println(c_xml.createCoreXml(new File("e:/kratos-context.xml")));  
  19. }  
  20.   
  21. /** 
  22.  * 生成数据源配置文件 
  23.  *  
  24.  * @author gaoxianglong 
  25.  */  
  26. public @Test void testCreateDadasourceXml() {  
  27.     CreateXml c_xml = new CreateXml();  
  28.     /* 是否控制台输出生成的配置文件 */  
  29.     c_xml.setIsShow(true);  
  30.     /* 数据源索引发始 */  
  31.     c_xml.setDataSourceIndex(1);  
  32.     /* 配置分库分片信息 */  
  33.     c_xml.setDbSize("1024");  
  34.     /* 配置数据源信息 */  
  35.     c_xml.setJdbcUrl("jdbc:mysql://ip:3306/um");  
  36.     c_xml.setUser("${name}");  
  37.     c_xml.setPassword("${password}");  
  38.     c_xml.setDriverClass("${driverClass}");  
  39.     c_xml.setInitialPoolSize("${initialPoolSize}");  
  40.     c_xml.setMinPoolSize("${minPoolSize}");  
  41.     c_xml.setMaxPoolSize("${maxPoolSize}");  
  42.     c_xml.setMaxStatements("${maxStatements}");  
  43.     c_xml.setMaxIdleTime("${maxIdleTime}");  
  44.     /* 执行配置文件输出 */  
  45.     System.out.println(c_xml.createDatasourceXml(new File("e:/dataSource-context.xml")));  
  46. }  
  47.   
  48. /** 
  49.  * 生成master/slave数据源配置文件 
  50.  *  
  51.  * @author gaoxianglong 
  52.  */  
  53. public @Test void testCreateMSXml() {  
  54.     CreateXml c_xml = new CreateXml();  
  55.     c_xml.setIsShow(true);  
  56.     /* 生成master数据源信息 */  
  57.     c_xml.setDataSourceIndex(1);  
  58.     c_xml.setDbSize("32");  
  59.     c_xml.setJdbcUrl("jdbc:mysql://ip1:3306/um");  
  60.     c_xml.setUser("${name}");  
  61.     c_xml.setPassword("${password}");  
  62.     c_xml.setDriverClass("${driverClass}");  
  63.     c_xml.setInitialPoolSize("${initialPoolSize}");  
  64.     c_xml.setMinPoolSize("${minPoolSize}");  
  65.     c_xml.setMaxPoolSize("${maxPoolSize}");  
  66.     c_xml.setMaxStatements("${maxStatements}");  
  67.     c_xml.setMaxIdleTime("${maxIdleTime}");  
  68.     System.out.println(c_xml.createDatasourceXml(new File("e:/masterDataSource-context.xml")));  
  69.     /* 生成slave数据源信息 */  
  70.     c_xml.setDataSourceIndex(33);  
  71.     c_xml.setDbSize("32");  
  72.     c_xml.setJdbcUrl("jdbc:mysql://ip2:3306/um");  
  73.     c_xml.setUser("${name}");  
  74.     c_xml.setPassword("${password}");  
  75.     c_xml.setDriverClass("${driverClass}");  
  76.     c_xml.setInitialPoolSize("${initialPoolSize}");  
  77.     c_xml.setMinPoolSize("${minPoolSize}");  
  78.     c_xml.setMaxPoolSize("${maxPoolSize}");  
  79.     c_xml.setMaxStatements("${maxStatements}");  
  80.     c_xml.setMaxIdleTime("${maxIdleTime}");  
  81.     System.out.println(c_xml.createDatasourceXml(new File("e:/slaveDataSource-context.xml")));  
  82. }  

 

11、注意事项

一旦在程序中使用Kratos进行Sharding后,sql的编写必定要注意,不然将没法进行路由。sql规则以下所示:

一、暂时不支持分布式事物,所以没法保证事务一致性;

二、不支持多表查询,全部多表查询sql,务必所有打散为单条sql分开查询;

三、不建议使用一些数据库统计函数、Order by语句等;

四、sql的参数第一个必须是路由条件;

五、不支持数据库别名;

六、路由条件必须是整型;

七、采用连续分片时,子表后缀为符号"_"+4位整型,好比“tb_0001”——"tb_1024";

 

注意:

目前Kratos还处于1.2阶段,将来还有很长的路要走,如今笔者测试环境上已经在大规模的使用,后续生产环境上将会开始投放使用,所以若是各位在使用过程当中有任何疑问,均可以经过企鹅群:150445731进行交流,或者获取Kratos的构件。

相关文章
相关标签/搜索