去这里下载 http://dl.mycat.io (GitHub的mycat: https://github.com/MyCATApache/Mycat-download)
可以看到最新的稳定版是1.6.7.1
之后的都是测试和开发版,所以我们下载稳定版的1.6.7.1
根据不同这里下载不同的版本(这里没有显示全,补过可以通过把鼠标移上去看左下角显示的链接信息)
因为我要下linux版本的。所以我下载第一个Mycat-server-1.6.7.1-release-20190213150257-lin的
下载后上传到Linux上,我的是centos6.5
tar -xvf Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz
可以看到解压出来的内容都放到mycat文件夹中了。
删除安装包,进入mycat文件夹
bin:命令文件
catlet:空的,扩展
conf:配置文件(server.xml,schema.xml,rule.xml等)
lib:依赖的jar包
进入conf文件夹配置文件
在mycat根目录创建logs文件夹,会把日志自动记录到这里。
如果启动报错可以到此目录查看日志
我在安装的时候发现一个错误
本地主机名未知,我的linux主机名称为test1,所以报错,需要在hosts文件中加上我的主机名字
编辑hosts文件:vim /etc/hosts
通过命令:ps -ef | grep mycat 查看服务是否已经确认启动成功
在后面的测试环节有对此配置的说明内容,很详细。
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mydatabase" checkSQLschema="true" sqlMaxLimit="100">
<table name="t_user" dataNode="dn1,dn2,dn3" primaryKey="id" rule="auto-sharding-long">
<childTable name="t_loginlog" primaryKey="id" joinKey="user_id" parentKey="id">childTable>
table>
<table name="t_student" dataNode="dn1,dn3" primaryKey="id" rule="student_id" />
<table name="t_dictionaries" dataNode="dn1,dn2,dn3" type="global" />
<table name="t_teacher" dataNode="dn1" />
schema>
<dataNode name="dn1" dataHost="dtHost1" database="db1" />
<dataNode name="dn2" dataHost="dtHost1" database="db2" />
<dataNode name="dn3" dataHost="dtHost2" database="db3" />
<dataHost name="dtHost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="127.0.0.1" url="192.168.199.11:3306" user="root" password="123456">
<readHost host="127.0.0.1" url="192.168.199.12:3306" user="root" password="123456" />
writeHost>
dataHost>
<dataHost name="dtHost2" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="127.0.0.1" url="192.168.199.13:3306" user="root" password="123456">
<readHost host="127.0.0.1" url="192.168.199.13:3306" user="root" password="123456" />
writeHost>
dataHost>
mycat:schema>
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="serverPort">8066property>
<property name="nonePasswordLogin">0property>
<property name="useHandshakeV10">1property>
<property name="useSqlStat">0property>
<property name="useGlobleTableCheck">0property>
<property name="sequnceHandlerType">2property>
<property name="subqueryRelationshipCheck">falseproperty>
<property name="processorBufferPoolType">0property>
<property name="handleDistributedTransactions">0property>
<property name="useOffHeapForMerge">1property>
<property name="memoryPageSize">64kproperty>
<property name="spillsFileBufferSize">1kproperty>
<property name="useStreamOutput">0property>
<property name="systemReserveMemorySize">384mproperty>
<property name="useZKSwitch">falseproperty>
<property name="strictTxIsolation">falseproperty>
<property name="useZKSwitch">trueproperty>
system>
<firewall>
<whitehost>
<host host="192.*" user="root"/>
<host host="127.*" user="root"/>
whitehost>
<blacklist check="false">
blacklist>
firewall>
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">mydatabaseproperty>
user>
mycat:server>
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="student_id">
<rule>
<columns>idcolumns>
<algorithm>student_textalgorithm>
rule>
tableRule>
<tableRule name="rule1">
<rule>
<columns>idcolumns>
<algorithm>func1algorithm>
rule>
tableRule>
<tableRule name="rule2">
<rule>
<columns>user_idcolumns>
<algorithm>func1algorithm>
rule>
tableRule>
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_idcolumns>
<algorithm>hash-intalgorithm>
rule>
tableRule>
<tableRule name="auto-sharding-long">
<rule>
<columns>idcolumns>
<algorithm>rang-longalgorithm>
rule>
tableRule>
<tableRule name="mod-long">
<rule>
<columns>idcolumns>
<algorithm>mod-longalgorithm>
rule>
tableRule>
<tableRule name="sharding-by-murmur">
<rule>
<columns>idcolumns>
<algorithm>murmuralgorithm>
rule>
tableRule>
<tableRule name="crc32slot">
<rule>
<columns>idcolumns>
<algorithm>crc32slotalgorithm>
rule>
tableRule>
<tableRule name="sharding-by-month">
<rule>
<columns>create_timecolumns>
<algorithm>partbymonthalgorithm>
rule>
tableRule>
<tableRule name="latest-month-calldate">
<rule>
<columns>calldatecolumns>
<algorithm>latestMonthalgorithm>
rule>
tableRule>
<tableRule name="auto-sharding-rang-mod">
<rule>
<columns>idcolumns>
<algorithm>rang-modalgorithm>
rule>
tableRule>
<tableRule name="jch">
<rule>
<columns>idcolumns>
<algorithm>jump-consistent-hashalgorithm>
rule>
tableRule>
<function name="student_text"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">student_text.txtproperty>
function>
<function name="murmur"
class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0property>
<property name="count">2property>
<property name="virtualBucketTimes">160property>
function>
<function name="crc32slot"
class="io.mycat.route.function.PartitionByCRC32PreSlot">
function>
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txtproperty>
function>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txtproperty>
function>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3property>
function>
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">8property>
<property name="partitionLength">128property>
function>
<function name="latestMonth"
class="io.mycat.route.function.LatestMonthPartion">
<property name="splitOneDay">24property>
function>
<function name="partbymonth"
class="io.mycat.route.function.PartitionByMonth">
<property name="dateFormat">yyyy-MM-ddproperty>
<property name="sBeginDate">2015-01-01property>
function>
<function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
<property name="mapFile">partition-range-mod.txtproperty>
function>
<function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">
<property name="totalBuckets">3property>
function>
mycat:rule>
vim student_text.txt
内容如下,需要注意的是
0-1M ,为0<=id<=10000,
1M-2M,为10000
0-1M=0
1M-2M=1
<table name="t_student" dataNode="dn1,dn3" primaryKey="id" rule="student_id" />
在bin文件夹下执行./mycat start
然后通过navicat进行连接,注意端口为之前配置的8066,用户名和密码也是之前配置的root/123456,连接地址为mycat安装主机地址
192.168.199.11和192.168.199.12为双机热备的物理库,
192.168.199.13为单机库,
mycat为逻辑库,通过逻辑库做增删改查、建表、删除表等表操作都会同步在其他物理库上执行。
然后在mycat中执行sql创建表,并自动同步到物理库中,注意是mycat执行建表sql
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `t_student`;
CREATE TABLE `t_student` (
`id` int(11) NOT NULL,
`s_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `t_teacher`;
CREATE TABLE `t_teacher` (
`id` int(11) NOT NULL,
`t_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `t_loginlog`;
CREATE TABLE `t_loginlog` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`create_time` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `t_dictionaries`;
CREATE TABLE `t_dictionaries` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `t_student` VALUES ('0', '00');
INSERT INTO `t_student` VALUES ('1', '111');
INSERT INTO `t_student` VALUES ('10000', '10000');
INSERT INTO `t_student` VALUES ('10001', '10001');
INSERT INTO `t_student` VALUES ('20000', '20000');
INSERT INTO `t_user` VALUES ('1', '111');
INSERT INTO `t_user` VALUES ('10000001', '10000001');
INSERT INTO `t_user` VALUES ('5000001', '5000001');
INSERT INTO `t_loginlog` VALUES ('1', '1', '2019-03-08');
INSERT INTO `t_loginlog` VALUES ('3', '10000001', '2019-03-06');
INSERT INTO `t_loginlog` VALUES ('2', '5000001', '2019-03-07');
INSERT INTO `t_dictionaries` VALUES ('1', '1');
打开mycat查看刚刚插入的数据,
首先确认t_student的数据,分片节点为dn1和dn3对应db1和db3的物理库,并分别存储在dtHost1和dtHost2两台mysql上
分片规则为
0-1M=0
1M-2M=1
确认物理库确实根据ID分开进行存储
确认t_user确实按照ID进行了分片,t_loginlog确实根据t_user的外键关联进行了分片,因为我的SQL并没有写主外键的约束,所以我手动往t_loginlog也是可以插入数据的
配置了3个数据节点,所以3个数据节点中的数据是一致的
此处就不截图了。
常用的分片规则:总共十个(基本够用)
一、枚举法
<tableRule name="sharding-by-intfile">
<rule>
<columns>user_idcolumns>
<algorithm>hash-intalgorithm>
rule>
tableRule>
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txtproperty>
<property name="type">0property>
<property name="defaultNode">0property>
function>
partition-hash-int.txt 配置:
10000=0
10010=1
上面columns 标识将要分片的表字段,algorithm 分片函数,
其中分片函数配置中,mapFile标识配置文件名称,type默认值为0,0表示Integer,非零表示String,
所有的节点配置都是从0开始,及0代表节点1
/**
* defaultNode 默认节点:小于0表示不设置默认节点,大于等于0表示设置默认节点,结点为指定的值
*
默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点
* 如果不配置默认节点(defaultNode值小于0表示不配置默认节点),碰到
* 不识别的枚举值就会报错,
* like this:can't find datanode for sharding column:column_name val:ffffffff
*/
二、固定分片hash算法
<tableRule name="rule1">
<rule>
<columns>user_idcolumns>
<algorithm>func1algorithm>
rule>
tableRule>
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">2,1property>
<property name="partitionLength">256,512property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
partitionCount 分片个数列表,partitionLength 分片范围列表
分区长度:默认为最大2^n=1024 ,即最大支持1024分区
约束 :
count,length两个数组的长度必须是一致的。
1024 = sum((count[i]*length[i])). count和length两个向量的点积恒等于1024
用法例子:
@Test
public void testPartition() {
// 本例的分区策略:希望将数据水平分成3份,前两份各占25%,第三份占50%。(故本例非均匀分区)
// |<---------------------1024------------------------>|
// |<----256--->|<----256--->|<----------512---------->|
// | partition0 | partition1 | partition2 |
// | 共2份,故count[0]=2 | 共1份,故count[1]=1 |
int[] count = new int[] { 2, 1 };
int[] length = new int[] { 256, 512 };
PartitionUtil pu = new PartitionUtil(count, length);
// 下面代码演示分别以offerId字段或memberId字段根据上述分区策略拆分的分配结果
int DEFAULT_STR_HEAD_LEN = 8; // cobar默认会配置为此值
long offerId = 12345;
String memberId = "qiushuo";
// 若根据offerId分配,partNo1将等于0,即按照上述分区策略,offerId为12345时将会被分配到partition0中
int partNo1 = pu.partition(offerId);
// 若根据memberId分配,partNo2将等于2,即按照上述分区策略,memberId为qiushuo时将会被分到partition2中
int partNo2 = pu.partition(memberId, 0, DEFAULT_STR_HEAD_LEN);
Assert.assertEquals(0, partNo1);
Assert.assertEquals(2, partNo2);
}
如果需要平均分配设置:平均分为4分片,partitionCount*partitionLength=1024
<function name="func1" class="org.opencloudb.route.function.PartitionByLong">
<property name="partitionCount">4property>
<property name="partitionLength">256property>
function>
三、范围约定
<tableRule name="auto-sharding-long">
<rule>
<columns>user_idcolumns>
<algorithm>rang-longalgorithm>
rule>
tableRule>
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txtproperty>
function>
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
或
0-10000000=0
10000001-20000000=1
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
rang-long 函数中mapFile代表配置文件路径
所有的节点配置都是从0开始,及0代表节点1,此配置非常简单,即预先制定可能的id范围到某个分片
四、求模法
<tableRule name="mod-long">
<rule>
<columns>user_idcolumns>
<algorithm>mod-longalgorithm>
rule>
tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
此种配置非常明确即根据id与count(你的结点数)进行求模预算,相比方式1,此种在批量插入时需要切换数据源,id不连续
五、日期列分区法
<tableRule name="sharding-by-date">
<rule>
<columns>create_timecolumns>
<algorithm>sharding-by-datealgorithm>
rule>
tableRule>
<function name="sharding-by-date" class="io.mycat.route.function..PartitionByDate">
<property name="dateFormat">yyyy-MM-ddproperty>
<property name="sBeginDate">2014-01-01property>
<property name="sPartionDay">10property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
配置中配置了开始日期,分区天数,即默认从开始日期算起,分隔10天一个分区
还有一切特性请看源码
Assert.assertEquals(true, 0 == partition.calculate("2014-01-01"));
Assert.assertEquals(true, 0 == partition.calculate("2014-01-10"));
Assert.assertEquals(true, 1 == partition.calculate("2014-01-11"));
Assert.assertEquals(true, 12 == partition.calculate("2014-05-01"));
六、通配取模
<tableRule name="sharding-by-pattern">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-patternalgorithm>
rule>
tableRule>
<function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPattern">
<property name="patternValue">256property>
<property name="defaultNode">2property>
<property name="mapFile">partition-pattern.txtproperty>
function>
partition-pattern.txt
# id partition range start-end ,data node index
###### first host configuration
1-32=0
33-64=1
65-96=2
97-128=3
######## second host configuration
129-160=4
161-192=5
193-224=6
225-256=7
0-0=7
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,patternValue 即求模基数,defaoultNode 默认节点,如果不配置了默认,则默认是0即第一个结点
mapFile 配置文件路径
配置文件中,1-32 即代表id%256后分布的范围,如果在1-32则在分区1,其他类推,如果id非数字数据,则会分配在defaoultNode 默认节点
String idVal = "0";
Assert.assertEquals(true, 7 == autoPartition.calculate(idVal));
idVal = "45a";
Assert.assertEquals(true, 2 == autoPartition.calculate(idVal));
七、ASCII码求模通配
<tableRule name="sharding-by-prefixpattern">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-prefixpatternalgorithm>
rule>
tableRule>
<function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPrefixPattern">
<property name="patternValue">256property>
<property name="prefixLength">5property>
<property name="mapFile">partition-pattern.txtproperty>
function>
partition-pattern.txt
# range start-end ,data node index
# ASCII
# 48-57=0-9
# 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 截取的位数
mapFile 配置文件路径
配置文件中,1-32 即代表id%256后分布的范围,如果在1-32则在分区1,其他类推
此种方式类似方式6只不过采取的是将列种获取前prefixLength位列所有ASCII码的和进行求模sum%patternValue ,获取的值,在通配范围内的
即 分片数,
/**
* ASCII编码:
* 48-57=0-9阿拉伯数字
* 64、65-90=@、A-Z
* 97-122=a-z
*
*/
如
String idVal="gf89f9a";
Assert.assertEquals(true, 0==autoPartition.calculate(idVal));
idVal="8df99a";
Assert.assertEquals(true, 4==autoPartition.calculate(idVal));
idVal="8dhdf99a";
Assert.assertEquals(true, 3==autoPartition.calculate(idVal));
八、编程指定
<tableRule name="sharding-by-substring">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-substringalgorithm>
rule>
tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionDirectBySubString">
<property name="startIndex">0property>
<property name="size">2property>
<property name="partitionCount">8property>
<property name="defaultPartition">0property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数
此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。
例如id=05-100000002
在此配置中代表根据id中从startIndex=0,开始,截取siz=2位数字即05,05就是获取的分区,如果没传默认分配到defaultPartition
九、字符串拆分hash解析
<tableRule name="sharding-by-stringhash">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-stringhashalgorithm>
rule>
tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionByString">
<property name=length>512property>
<property name="count">2property>
<property name="hashSlice">0:2property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数
函数中length代表字符串hash求模基数,count分区数,hashSlice hash预算位
即根据子字符串 hash运算
hashSlice : 0 means str.length(), -1 means str.length()-1
/**
* "2" -> (0,2)<br/>
* "1:2" -> (1,2)<br/>
* "1:" -> (1,0)<br/>
* "-1:" -> (-1,0)<br/>
* ":-1" -> (0,-1)<br/>
* ":" -> (0,0)<br/>
*/
public class PartitionByStringTest {
@Test
public void test() {
PartitionByString rule = new PartitionByString();
String idVal=null;
rule.setPartitionLength("512");
rule.setPartitionCount("2");
rule.init();
rule.setHashSlice("0:2");
// idVal = "0";
// Assert.assertEquals(true, 0 == rule.calculate(idVal));
// idVal = "45a";
// Assert.assertEquals(true, 1 == rule.calculate(idVal));
//last 4
rule = new PartitionByString();
rule.setPartitionLength("512");
rule.setPartitionCount("2");
rule.init();
//last 4 characters
rule.setHashSlice("-4:0");
idVal = "aaaabbb0000";
Assert.assertEquals(true, 0 == rule.calculate(idVal));
idVal = "aaaabbb2359";
Assert.assertEquals(true, 0 == rule.calculate(idVal));
}
十、一致性hash
<tableRule name="sharding-by-murmur">
<rule>
<columns>user_idcolumns>
<algorithm>murmuralgorithm>
rule>
tableRule>
<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0property>
<property name="count">2property>
function>