MyCat介绍、MyCat核心概念、MyCat分片规则、MyCat读写分离
理解MyCat介绍和核心概念
掌握MyCat分片规则
掌握MyCat读写分离
能够使用MyCat进行分库分表和读写分离
官方网站:http://www.mycat.org.cn/
和 shradingJDBC不同,它是一个软件,可以认为是Mysql的代理,shradingJDBC只是一个jar包。
注意:需要先安装jdk(操作系统如果是64位,必须安装64位的JDK)
第一步:下载MyCat
wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
第二步:解压缩,得到mycat目录
tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
第三步:进入mycat/bin,启动MyCat
- 启动命令:./mycat start
- 停止命令:./mycat stop
- 重启命令:./mycat restart
- 查看状态:./mycat status
第四步:访问Mycat
使用mysql的客户端直接连接mycat服务。默认服务端口为【8066】
mysql -uroot -p123456 -h127.0.0.1 -P8066
目录结构介绍
schema.xml作为Mycat中重要的配置文件之一,管理着Mycat的逻辑库、表、分片规则、DataNode以及DataHost之间的映射关系
。弄懂这些配置,是正确使用Mycat的前提。
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<table name="item" dataNode="dn1,dn2,dn3" rule="mod-long" primaryKey="ID"/>
schema>
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="192.168.24.129:3306" user="root" password="root" >
writeHost>
dataHost>
mycat:schema>
server.xml几乎保存了所有mycat需要的系统配置信息。最常用的是在此配置用户名、密码及权限。
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="defaultSqlParser">druidparserproperty>
system>
<user name="mycat">
<property name="password">mycatproperty>
<property name="schemas">TESTDBproperty>
user>
mycat:server>
rule.xml里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。这个文件里面主要有tableRule
和function
这两个标签。在具体使用过程中可以按照需求添加tableRule和function。
<mycat:rule xmlns:mycat=”http://io.mycat/“ >
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_idcolumns>
<algorithm>hash-intalgorithm>
rule>
tableRule>
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partitio n-hash-int.txtproperty>
function>
mycat:rule>
<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>
<tableRule name="sharding-by-month">
<rule>
<columns>create_timecolumns>
<algorithm>sharding-by-monthalgorithm>
rule>
tableRule>
<function name="sharding-by-month" class="io.mycat.route.function..PartitionByMonth">
<property name="dateFormat">yyyy-MM-ddproperty>
<property name="sBeginDate">2014-01-01property>
function>
<tableRule name="sharding-by-hour">
<rule>
<columns>create_timecolumns>
<algorithm>sharding-by-houralgorithm>
rule>
tableRule>
<function name="sharding-by-hour" class="io.mycat.route.function..LastestMonthPartition">
<property name="splitOneDay">24property>
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>
autopartition-long.txt文件内容:
连续分片优势和缺点
- 默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点。
- 如果不配置默认节点(defaultNode值小于0表示不配置默认节点),碰到不识别的枚举值就会报错:can’t find datanode for sharding column:column_name val:ffffffff
<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>
此种配置非常明确,即根据id与count(你的结点数)进行求模运算,相比方式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>
此规则是截取字符串中的int数值hash分片
配置说明:
“2” -> (0,2)
“1:2” -> (1,2)
“1:” -> (1,0)
“-1:” -> (-1,0)
“:” -> (0,0)
<tableRule name="sharding-by-stringhash">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-stringhashalgorithm>
rule>
tableRule>
<function name="sharding-by-stringhash" class="io.mycat.route.function.PartitionByString">
<property name="length">512property>
<property name="count">2property>
<property name="hashSlice">0:2property>
function>
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));
}
<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>
@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);
}
<function name="func1" class="org.opencloudb.route.function.PartitionByLong">
<property name="partitionCount">4property>
<property name="partitionLength">256property>
function>
<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>
<property name="virtualBucketTimes">160property>
function>
一致性hash环的范围是 0到2的32次方减1
, 预算有效解决了分布式数据的扩容问题,前1-9中id规则都多少存在数据扩容难题,而10规则解决了数据扩容难点必须是数字
)计算分区号(由应用传递参数,显式指定分区号)。
<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>
离散分片优点和缺点
<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>
# 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
<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>
只不过采取的是将列中前prefixLength位所有ASCII码的和与patternValue 进行求模,即 sum%patternValue ,获取的值在通配范围内的,即分片数。
ASCII编码:
- 48-57=0-9阿拉伯数字
- 64、65-90=@、A-Z
- 97-122=a-z
# 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
MyCat的读写分离是建立在MySQL主从复制基础之上
实现的,所以必须先搭建MySQL的主从复制。
数据库读写分离对于大型系统或者访问量很高的互联网应用来说,是必不可少的一个重要功能。对于MySQL来说,标准的读写分离是主从模式,一个写节点Master后面跟着多个读节点,读节点的数量取决于系统的压力,通常是1-3个读节点的配置
Mycat实现的读写分离和自动切换机制,需要mysql的主从复制机制配合。
Mycat 1.4 支持MySQL主从复制状态绑定的读写分离机制,让读更加安全可靠,配置如下:
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="2"
slaveThreshold="100">
<heartbeat>show slave statusheartbeat>
<writeHost host="hostM" url="192.168.25.134:3306" user="root" password="root">
<readHost host="hostS" url="192.168.25.166:3306" user="root" password="root" />
writeHost>
dataHost>
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456">
<readHost host="hostS1" url="localhost2:3306" user="root" password="123456" weight="1" />
writeHost>
dataHost>
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> writeHost>
<writeHost host="hostS1" url="localhost:3307" user="root" password="123456"> writeHost>
dataHost>
以上两种取模第一种当写挂了读不可用,第二种可以继续使用,事务内部的一切操作都会走写节点,所以读操作不要加事务,如果读延时较大,使用根据主从延时的读写分离,或者强制走写节点
:
/*!mycat:db_type=slave*/ select * from travelrecord
/*#mycat:db_type=slave*/ select * from travelrecord
/*#mycat:db_type=master*/ select * from travelrecord
/*!mycat:db_type=master*/ select * from travelrecord
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="3" >
<heartbeat> show status like ‘wsrep%’heartbeat>
writeHost>
writeHost>
dataHost>