#Mycat全局Sequence详解 在分布数数据库设计环节,将一个大表切分成各个子表,并且存储到各个数据节点上,如何保持一条数据记录的全局唯一性是一个关键性问题。mycat提供了一种全局sequence的机制,并且提供了多种实现方案。该文将对Mycat这一块进行讨论,以理清mycat这一块的设计思路。
mycat当前提供了master分支提供了三种全局sequence实现方式,还有一种zookeeper好像还处于开发阶段,没有看到完整的实现代码。下面将对这三种实现方式进行讨论。
##从哪里配置采用那种全局Sequence实现 在SystemConfig中有如下几行代码:
<!--lang:java-->
public static final int SEQUENCEHANDLER_LOCALFILE = 0;
public static final int SEQUENCEHANDLER_MYSQLDB = 1;
public static final int SEQUENCEHANDLER_LOCAL_TIME = 2;
private int sequnceHandlerType = SEQUENCEHANDLER_LOCALFILE;
这里可以看到提供了三种实现方式。看到上面,如果熟悉mycat的话就应该知道在哪里配置sequnceHandlerType
参数了。如果不熟悉也没关系,下面我告诉大家,在mycat的conf/server.xml的<property name="sequnceHandlerType">1/0/2</property>
,在<system>
标签内的所有<property>
标签都会通过name
属性找到SystemConfig
中的属性并且自动将配置的值赋予到对应的属性中去(这部分实现可以看看XMLServerLoader
的loadSystem
实现)。
mycat对于sequence生成策略抽象出了一个接口-SequenceHandler
,这个接口提供的实现类有IncrSequenceMySQLHandler
,IncrSequencePropHandler
,IncrSequenceTimeHandler
和IncrSequenceZKHandler
,其中IncrSequenceZKHandler
还没有具体的实现。通过类名应该知道了具体哪个类实现了哪个sequence策略类型。比如SEQUENCEHANDLER_LOCALFILE
策略是IncrSequencePropHandler
实现类,SEQUENCEHANDLER_MYSQLDB
策略是IncrSequenceMySQLHandler
实现类,SEQUENCEHANDLER_LOCAL_TIME
是IncrSequenceTimeHandler
实现类。说到这里感觉mycat对于插件化这一块做的不够好,比如这里的sequence策略实现,完全可以插件化,但是从mycat加载各个具体实现的地方,都是通过硬编码去获取具体实现,而没有提供一种外部去接入的地方,所以如果外部需要实现自己的sequence策略,只能修改源码,这方面感觉还不够开放,例如下面硬编码获取具体实现:
<!--lang:java-->
public DruidSequenceHandler(int seqHandlerType) {
switch(seqHandlerType){
case SystemConfig.SEQUENCEHANDLER_MYSQLDB:
sequenceHandler = IncrSequenceMySQLHandler.getInstance();
break;
case SystemConfig.SEQUENCEHANDLER_LOCALFILE:
sequenceHandler = IncrSequencePropHandler.getInstance();
break;
case SystemConfig.SEQUENCEHANDLER_LOCAL_TIME:
sequenceHandler = IncrSequenceTimeHandler.getInstance();
break;
default:
throw new java.lang.IllegalArgumentException("Invalid sequnce handler type "+seqHandlerType);
}
}
上面配置完值,如何在插入的时候从sequence获取值呢?这方面mycat提供了一种规范,通过“next value for MYCATSEQ_SEQUENCENAME”方式获取下一个sequence值。 比如insert into t_users (id,province_code) values(next value for MYCATSEQ_USERS,"110000");
,t_user的id属性mycat会自动从全局USERS sequence中获取一个值。
上面列出了mycat在sequence策略方面的三个具体实现(其中zk还没有实现完,所以是三个),那么下面将对这三个实现进行介绍。
##SEQUENCEHANDLER_LOCALFILE(0) 这种方式的实现是通过IncrSequencePropHandler
类去实现,也是mycat提供默认的实现方式。通过静态配置文件来设置全局sequence的取值范围。各个全局sequence的配置放在sequence_conf.properties文件中,格式如下:
<!--lang:java-->
#default global sequence
GLOBAL.HISIDS= //GLOBAL sequence历史的分片区间
GLOBAL.MINID=10001 //GLOBAL sequence 当前获取的最小值
GLOBAL.MAXID=20000 //GLOBAL sequence 当前能够获取的最大id
GLOBAL.CURID=10000 //GLOBAL sequence 当前的值
其中GLOBAL
就是sequence的名称,比如你创建一个USER的sequence,就如下进行创建:
<!--lang:java-->
#default user sequence
USER.HISIDS= //USER sequence历史的分片区间
USER.MINID=10001 //USER sequence 当前获取的最小值
USER.MAXID=20000 //USER sequence 当前能够获取的最大id
USER.CURID=10000 //USER sequence 当前的值
mycat每次从sequence_conf.properties获取一个sequence之后将会把CURID跟新到sequence_conf.properties文件中,并且如果当前的CURID大于MAXID,则会自动扩容,并且扩容的区间范围是和设置的范围一样。
##SEQUENCEHANDLER_MYSQLDB(1) 这种方式是将sequence信息存储在数据库中,并且在对应数据库中执行响应的存储过程,然后mycat会调用对应的存储过程获取对赢的sequence,存储过程可以从这里获取。这部分在Mycat里面的具体实现是在IncrSequenceMySQLHandler
中。在使用这个进行存储和获取sequence的时候,需要配置sequence的表是在哪个数据节点上,这些信息是配置在conf/sequence_db_conf.properties文件中。格式如下:
<!--lang:java-->
#sequence stored in datanode
GLOBAL=sequence //sequence名称=数据节点名称(配置在schema.xml里面)
USERS=sequence
ORDERS=sequence
##SEQUENCEHANDLER_LOCAL_TIME(2) 这种方式有点类似采用UUID的方式来生成一个全局的唯一标识,这种规则的实现实在唉IncrSequenceTimeHandler
中。需要配置conf/sequence_time_conf.properties文件。格式如下: #sequence depend on TIME WORKID=01 DATAACENTERID=01