文章目录
MyCat 实战-从入门到落地
本博主linux亲测版本:centos7
1 安装
注意:安装之前,需要先安装JDK(操作系统如果是64位,必须按照64位的JDK)
推荐JDK安装博客:
1.1 下载
下载版本网站:http://dl.mycat.org.cn/1.6.7.1/
wget http://dl.mycat.org.cn/1.6.7.1/Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz
1.2 解压
tar -zxvf Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz
1.3 启动
进入mycat/bin,启动MyCat
- 启动命令:./mycat start
- 停止命令:./mycat stop
- 重启命令:./mycat restart
- 查看状态:./mycat status
启动示例
[root@localhost bin]# ./mycat start
Starting Mycat-server...
1.4 日志说明
路径:mycat/logs/
[root@localhost logs]# pwd
/home/mycat/mycat/logs
[root@localhost logs]# ls
2020-12 mycat.log switch.log wrapper.log
[root@localhost logs]#
说明:
mycat.log:MyCat程序运行日志,即程序启动后运行错误,例如数据库连接异常等。
switch.log:dataNode心跳日志,记录每个节点状态日志。
wrapper.log:程序启动日志,启动成功/失败打印的日志,例如xml文件加载异常等。
1.5 访问
使用mysql的客户端直接连接mycat服务。默认服务端口为【8066】
2 MyCat配置
MyCat核心配置文件有:schema.xml、server.xml、rule.xml三个文件
掌握这三个文件的配置才是使用MyCat的核心。
2.1 schema.xml 配置
2.1.1 schema.xml介绍
- schema.xml作为Mycat中重要的配置文件之一,管理着Mycat的逻辑库、表、分片规则、DataNode以及DataHost之间的映射关系。弄懂这些配置,是正确使用Mycat的前提。
- schema 标签用于定义MyCat实例中的逻辑库
- Table 标签定义了MyCat中的逻辑表
- dataNode 标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片。
- dataHost标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句
2.1.2 schema.xml配置详情
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
# schema 属性介绍
# name:逻辑库的名称
# checkSQLschema:该字段就是用户执行sql语句时,是否检查表名的schema,一般设置成false
当该值为true时,例如我们执行语句select * from TESTDB.company 。
mycat会把语句修改为 select * from company 去掉TESTDB;
# sqlMaxLimit: mycat查询限制条数,当该值设置为某个数值时
每条执行的sql语句,如果没有加上limit语句,Mycat会自动加上对应的值。不写的话,默认返回所有的值。
需要注意的是,如果运行的schema为非拆分库的,那么该属性不会生效。需要自己sql语句加limit。
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
# table 属性介绍
# name:物理数据库中的表名(真实表名)
# dataNode: 表存储到哪些节点,多个节点用逗号分开,对应dataNode标签的name属性
# primaryKey: 表主键字段名, 注意: 分片规则,能不用主键做分片规则的话,尽量不用(提高性能方案)
# rule:分片规则名,对应rule.xml中标签tableRule的name属性值
# type 属性定义了逻辑表的类型,目前逻辑表只有“全局表”和”普通表”两种类型。
对应的配置:全局表:global, 普通表:无配置
# autoIncrement 属性:
1. mysql 对非自增长主键,使用 last_insert_id()是不会返回结果的,只会返回 0。
所以,只有定义了自增长主键的表才可以用 last_insert_id()返回主键值
2.mycat 目前提供了自增长主键功能,但是如果对应的 mysql 节点上数据表,
没有定义 auto_increment,那么在 mycat 层调用 last_insert_id()也是不会返回结果的
3. 由于 insert 操作的时候没有带入分片键,mycat 会先取下这个表对应的全局序列,
然后赋值给分片键。这样才能正常的插入到数据库中,最后使用 last_insert_id()才会返回插入的分片键值
4. 如果要使用这个功能最好配合使用数据库模式的全局序列。
5,使用 autoIncrement=“ true” 指定这个表有使用自增长主键,这样 mycat 才会不抛出分片键找不到的异常
6. 使用 autoIncrement=“ false” 来禁用这个功能,当然你也可以直接删除掉这个属性。默认就是禁用的
# needAddLimit: 该属性与schema标签的sqlMaxLimit配合使用,如果needAddLimit值为false,则语句不会加上limit
<table name="dual" primaryKey="ID" dataNode="dnx,dnoracle2" type="global"
needAddLimit="false"/>
<table name="customer" primaryKey="ID" dataNode="dn1,dn2"
rule="sharding-by-intfile">
# childTable标签:用于定义 E-R 分片的子表。通过标签上的属性与父表进行关联
1.name 属性定义子表的表名
2.joinKey 属性插入子表的时候会使用这个列的值查找父表存储的数据节点。
3.parentKey 属性指定的值一般为与父表建立关联关系的列名。Mycat首先获取 joinkey 的值,
再通过 parentKey 属性指定的列名产生查询语句,通过执行该语句得到父表存储在哪个分片上。
从而确定子表存储的位置。
4. primaryKey 属性同 table 标签描述。
5. needAddLimit 属性同 table 标签所描述的
<childTable name="orders" primaryKey="ID" joinKey="customer_id"
parentKey="id">
<childTable name="order_items" joinKey="order_id"
parentKey="id" />
</childTable>
<childTable name="customer_addr" primaryKey="ID" joinKey="customer_id"
parentKey="id" />
</table>
</schema>
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"
/> -->
# dataNode 标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片
# name 属性定义数据节点的名字,这个名字需要是唯一的,我们需要在 table 标签上应用这个名字,
来建立表与分片对应的关系。
# dataHost 属性定义该分片属于哪个数据库实例,对应dataHost 标签的name值
# database 属性用于定义该分片,使用的是数据库实例上,具体的哪一个数据库,即数据库名。
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
# dataHost标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。
# name 指定dataHost的名字,一般指向DataNode标签的dataHost属性
# maxCon 指定每个读写实例连接池的最大连接数量。
writeHost、 readHost 标签都会使用这个属性的值来实例化出连接池的最大连接数。
# minCon 指定每个读写实例连接池的最小连接,初始化连接池的大小。
writeHost、 readHost 标签都会使用这个属性的值来实例化出连接池的最大连接数。
# balance 负载均衡类型
1. balance=“0”, 不开启读写分离机制,所有读写操作都发送到当前可用的 writeHost 上。
2. balance=“1”,全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,
简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,
M2,S1,S2 都参与 select 语句的负载均衡。
3. balance=“2”,所有读操作都随机的在 writeHost、 readhost 上分发。
4. balance=“3”,所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,
注意 balance=3 只在 1.4 及其以后版本有,1.3 没有。
# writeType 负载均衡类型(自动切换数据库),一般默认配置0
1. writeType=“0”, 所有写操作发送到配置的第一个 writeHost,
第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为准,
切换记录在配置文件中:dnindex.properties.
2. writeType=“1”,所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐
# dbType 指定后端连接的数据库类型,目前支持二进制的 mysql 协议,
还有其他使用 JDBC 连接的数据库。例如:mongodb、 oracle、 spark 等。
# dbDriver 指定连接后端数据库使用的 Driver,目前可选的值有 native 和 JDBC。
使用native 的话,因为这个值执行的是二进制的 mysql 协议,所以可以使用 mysql 和 maridb。
其他类型的数据库则需要使用 JDBC 驱动来支持。
# switchType 主从切换策略 (schema.xml 可以配置多主多从)
1. 表示不自动切换: -1
2. 默认值,自动切换: 1
3. 基于 MySql 主从同步(是否延时)的状态决定是否切换: 2, 心跳语句为 show slave status
备注:switchType=2、slaveThreshold="100"(设置延时时间)、心跳语句 show slave status 三者结合,从应用层方面来解决主从延时,进行切换操作
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
# heartbeat:这个标签内指明用于和后端数据库进行心跳检查的语句。例如:MYSQL 可以使用 select user(),Oracle 可以使用 select 1 from dual 等。
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
# writeHost 与 readHost: 这两个标签都指定后端数据库的相关配置,用于实例化后端连接池。唯一不同的是,writeHost 指定写实例、readHost 指定读实例。
# host:实例名,一般 writeHost 我们使用*M1,readHost 我们用*S1
# url:物理地址
# user:连接数据库账号
# password:连接数据库密码
# weight:配置在 readhost 中作为读节点的权重
# usingDecrypt: 是否对密码加密,默认0。加密操作参考博客:https://blog.csdn.net/lzb348110175/article/details/103353850
<writeHost host="hostM1" url="localhost:3306" user="root"
password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>
<writeHost host="hostS1" url="localhost:3316" user="root"
password="123456" />
</mycat:schema>
2.2 server.xml配置
2.2.1 server.xml介绍
server.xml几乎保存了所有mycat需要的系统配置信息。最常用的是在此配置用户名、密码及权限。
2.2.2server.xml 详细配置
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<!-- 配置该属性的时候一定要保证mycat的字符集和mysql 的字符集是一致的 -->
<property name="charset">utf8</property>
<!-- 配置系统可用的线程数量,默认值为CPU核心X每个核心运行线程的数量 -->
<property name="processors">1</property>
<!-- 指定每次分配socker direct buffer 的值,默认是4096字节 -->
<property name="processorBufferChunk">40960</property>
<!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
<property name="nonePasswordLogin">0</property>
<property name="useHandshakeV10">1</property>
<!-- 1为开启实时统计、0为关闭 -->
<property name="useSqlStat">0</property>
<!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="useGlobleTableCheck">0</property>
<!-- 用来指定Mycat全局序列类型,0为本地文件,1为数据库方式,2为时间戳列方式,默认使用本地文件方式,文件方式主要用于测试-->
<property name="sequnceHandlerType">2</property>
<!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>-->
<!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
<property name="subqueryRelationshipCheck">false</property>
<!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
<!--
<property name="processorExecutor">32</property>
-->
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
<property name="processorBufferPoolType">0</property>
<!--默认是65535 64K 用于sql解析时最大文本长度 -->
<!--<property name="maxStringLiteralLength">65535</property>-->
<!--<property name="sequnceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!--
off heap for merge/order/group/limit 1开启 0关闭
-->
<property name="useOffHeapForMerge">0</property>
<!--
单位为m
-->
<property name="memoryPageSize">64k</property>
<!--
单位为k
-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!-- 单位为m -->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">false</property>
<!-- XA Recovery Log日志路径 -->
<!--<property name="XARecoveryLogBaseDir">./</property>-->
<!-- XA Recovery Log日志名称 -->
<!--<property name="XARecoveryLogBaseName">tmlog</property>-->
<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
<property name="strictTxIsolation">false</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--白名单可以使用通配符%或着*-->
<!--例如<host host="127.0.0.*" user="root"/>-->
<!--例如<host host="127.0.*" user="root"/>-->
<!--例如<host host="127.*" user="root"/>-->
<!--例如<host host="1*7.*" user="root"/>-->
<!--这些配置情况下对于127.0.0.1都能以root账户登录-->
<!--
<firewall>
<whitehost>
<host host="1*7.0.0.*" user="root"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<!-- 定义登录mycat对的用户权限 -->
<user name="root">
<property name="password">123456</property>
<!-- 若要访问TESTDB 必须现在server.xml 中定义,否则无法访问TESTDB-->
<property name="schemas">TESTDB</property>
<!-- 配置是否允许只读,默认false -->
<property name="readOnly">false</property>
<!-- 定义限制前端整体的连接数,如果其值为0,或者不设置,则表示不限制连接数量 -->
<property name="benchmark">11111</property>
<!-- 设置是否开启密码加密功能,默认为0不开启加密,为1则表示开启加密 -->
<property name="usingDecrypt">1</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
</mycat:server>
2.3 rule.xml 配置
2.3.1 rule.xml 介绍
rule.xml里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对
表使用相同的算法但具体的参数不同。这个文件里面主要有tableRule和function这两个标签。在具体使用过程中可
以按照需求添加tableRule和function。
2.3.2 rule.xml 详细配置
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<!-- tableRule 标签配置说明:
name 属性指定唯一的名字,用于标识不同的表规则
rule 标签则指定对物理表中的哪一列进行拆分和使用什么路由算法。
columns 内指定要拆分的列名字。
algorithm 使用 function 标签中的 name 属性。连接表规则和具体路由算法。当然,多个表规则可以连接到
同一个路由算法上。 table 标签内使用。让逻辑表使用这个规则进行分片。-->
<tableRule name="murmur">
<rule>
<columns>id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>
<!--
function标签配置说明:
name 指定算法的名字。
class 制定路由算法具体的类名字。
property 为具体算法需要用到的一些属性
-->
<!-- 一致性hash -->
<function name="murmur"
class="io.mycat.route.function.PartitionByMurmurHash">
<!-- 默认是0 -->
<property name="seed">0</property>
<!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="count">2</property>
<!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的
160倍 -->
<property name="virtualBucketTimes">160</property>
<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权
重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节
点权重值为值。所有权重值必须是正整数,否则以1代替 -->
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur
hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
</function>
</mycat:rule>
2.4 十个常用的分片规则
2.4.1 枚举法
<tableRule name="sharding-by-intfile">
<rule>
<columns>user_id</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<!-- mapFile :指定分片函数需要的配置文件名称 -->
<property name="mapFile">partition-hash-int.txt</property>
<!-- 默认值为0,0表示Integer,非零表示String -->
<property name="type">0</property>
<!-- defaultNode :指定默认节点,小于0表示不设置默认节点,大于等于0表示设置默认节点,0代表节点1,以此类推。 -->
<property name="defaultNode">0</property>
</function>
partition-hash-int.txt文件配置
# 0代表第一个节点,以此类推
10000=0
10010=1
分片规则:user_id = 10000,存储到第一个节点;user_id= 10010,存储到第二个节点,其他情况默认存储到dataNode=1的节点
2.4.2 固定分片Hash算法
实现方式:该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制低 10 位 与 1111111111 进行 & 运算。
<tableRule name="rule1">
<rule>
<columns>user_id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>
<function name="func1"
class="io.mycat.route.function.PartitionByLong">
<!-- partitionCount :指定分片个数列表
设置三个分片,前两个各占25%,第三个占30%
三个分片的区间为:0-255,256-511,512-1024-->
<property name="partitionCount">2,1</property>
<!-- partitionLength : 分片范围列表,分区长度:默认为最大2^n=1024 ,即最大支持1024分区
1024 = sum((partitionCount[i]*partitionLength[i])). count 和 length 两个向量的点积恒等于 1024-->
<property name="partitionLength">256,512</property>
</function>
2.4.2.1 案例
user_id分别为1111111,4444444
分片结果:
-
1111111 的二进制低10位为:0001000111,与1111111111相与之后是0001000111,然后再转换为十进制数为71,满足 0-255,在第一个分区,即dataNode1;
-
4444444的二进制低10位为:0100011100,与1111111111相与之后是0100011100,然后再转换为十进制数为284,满足256-511,在第二个分区;
2.4.3 范围约定
<tableRule name="auto-sharding-long">
<rule>
<columns>user_id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<!-- mapFile :指定分片函数需要的配置文件名称 -->
<property name="mapFile">autopartition-long.txt</property>
</function>
2.4.3.1 autopartition-long.txt配置
所有的节点配置都是从0开始,及0代表节点1,此配置非常简单,即预先制定可能的id范围对应某个分片
# columns标签值的范围
# 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
2.4.4 求模法 - 常用方法
此种配置非常明确,即根据id与count(你的结点数)进行求模运算,相比方式1,此种在批量插入时需要切换数据
源,id不连续
<tableRule name="mod-long">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long"
class="io.mycat.route.function.PartitionByMod">
<!-- 有多少个分片节点dataNode -->
<property name="count">3</property>
</function>
2.4.5 日期列分区法
<tableRule name="sharding-by-date">
<rule>
<columns>create_time</columns>
<algorithm>sharding-by-date</algorithm>
</rule>
</tableRule>
<function name="sharding-by-date"
class="io.mycat.route.function..PartitionByDate">
<!-- 日期格式 -->
<property name="dateFormat">yyyy-MM-dd</property>
<!-- 开始日期 -->
<property name="sBeginDate">2014-01-01</property>
<!-- 分区天数,即默认从开始日期算起,分隔10天一个分区 -->
<property name="sPartionDay">10</property>
</function>
2.4.6 通配取模
<tableRule name="sharding-by-pattern">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-pattern</algorithm>
</rule>
</tableRule>
<function name="sharding-by-pattern"
class="io.mycat.route.function.PartitionByPattern">
<!-- 求模基数 -->
<property name="patternValue">256</property>
<!-- 默认节点,如果不配置了默认,则默认是0即第一个结点 -->
<property name="defaultNode">2</property>
<!-- 配置文件路径 -->
<property name="mapFile">partition-pattern.txt</property>
</function>
2.4.6.1 partition-pattern.txt配置
配置文件中, 1-32 即代表 id%256 后分布的范围,如果在1-32则在分区1,其他类推,如果id非数字数据,则会
分配在defaultNode 默认节点
# id partition range start-end ,data node index
###### first host configuration0-32=0
0-32
33-64=1
65-96=2
97-128=3
...
2.4.7 ASCII码求模通配
<tableRule name="sharding-by-prefixpattern">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-prefixpattern</algorithm>
</rule>
</tableRule>
<function name="sharding-by-pattern"
class="io.mycat.route.function.PartitionByPrefixPattern">
<!-- 求模基数 -->
<property name="patternValue">256</property>
<!-- ASCII 截取的位数 -->
<property name="prefixLength">5</property>
<!-- 配置文件路径 -->
<property name="mapFile">partition-pattern.txt</property>
</function>
2.4.7.1 partition-pattern.txt配置
配置文件中, 1-32 即代表 id%256 后分布的范围,如果在1-32则在分区1,其他类推
此种方式类似方式6,只不过采取的是将列中前prefixLength位所有ASCII码的和与patternValue 进行求模,即sum%patternValue ,获取的值在通配范围内的,即分片数。
# 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
...
2.4.8 编程指定
此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。
例如id=0-10000002,在此配置中代表根据id中从startIndex=0,开始,截取siz=1位数字即0,0就是获取的存储分区(即对应第一个dataNode),如果没传默认分配到defaultPartition
<tableRule name="sharding-by-substring">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-substring</algorithm>
</rule>
</tableRule>
<function name="sharding-by-substring"
class="io.mycat.route.function.PartitionDirectBySubString">
<!-- 字符串截取的起始索引位置 -->
<property name="startIndex">0</property>
<!-- 截取的位数 -->
<property name="size">1</property>
<!-- 分区数量 -->
<property name="partitionCount">8</property>
<!-- 默认分区 -->
<property name="defaultPartition">0</property>
</function>
2.4.9 字符串拆分hash解析
<tableRule name="sharding-by-stringhash">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-stringhash</algorithm>
</rule>
</tableRule>
<function name="sharding-by-substring"
class="io.mycat.route.function.PartitionByString">
<!-- 代表字符串hash求模基数 -->
<property name="length">512</property>
<!-- 分区数 -->
<property name="count">2</property>
<!-- hash预算位,即根据子字符串 hash运算 -->
<property name="hashSlice">0:2</property>
</function>
2.4.10 一致性hash
<tableRule name="sharding-by-murmur">
<rule>
<columns>user_id</columns>
<algorithm>murmur</algorithm>
</rule>
</tableRule>
<function name="murmur"
class="io.mycat.route.function.PartitionByMurmurHash">
<!-- 默认是0 -->
<property name="seed">0</property>
<!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="count">2</property>
<!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的
160倍 -->
<property name="virtualBucketTimes">160</property>
<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权
重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节
点权重值为值。所有权重值必须是正整数,否则以1代替 -->
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur
hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
</function>
注意:一致性hash预算有效解决了分布式数据的扩容问题,前1-9中id规则都多少存在数据扩容难题,而10规则解决了数据扩容难点
3 案例演示
3.1 物理模型
MySql:192.168.233.101:test
MySql:192.168.233.101:test1
MyCat:192.168.233.103
3.2 配置文件
3.2.1 schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- tb_user 两个节点均存储 求模法 -->
<table name="tb_user" dataNode="dn1,dn2" rule="mod-long" />
<!-- 全局表,所有节点数据一致 -->
<table name="tb_global_user" dataNode="dn1,dn2" type="global" />
<!-- tb_user1指定dn1节点存储 -->
<table name="tb_user1" dataNode="dn1" />
<!-- tb_user1指定dn2节点存储 -->
<table name="tb_user2" dataNode="dn2" />
</schema>
<!-- 配置数据源,指向数据源数据量 test,test1 -->
<dataNode name="dn1" dataHost="h1" database="test" />
<dataNode name="dn2" dataHost="h2" database="test1" />
<dataHost name="h1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!-- 心跳sql -->
<heartbeat>select user()</heartbeat>
<!-- 主机 -->
<writeHost host="hostM1" url="192.168.233.101:3306" user="root" password="123456">
<!-- 从机 -->
<readHost host="hostS1" url="192.168.233.100:3306" user="root" password="123456" />
</writeHost>
</dataHost>
<dataHost name="h2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!-- 心跳sql -->
<heartbeat>select user()</heartbeat>
<!-- 主机 -->
<writeHost host="hostM2" url="192.168.233.101:3306" user="root" password="123456"/>
</dataHost>
</mycat:schema>
3.2.2 rule.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="mod-long">
<rule>
<columns>hash_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<!-- 求模法 -->
<function name="mod-long"
class="io.mycat.route.function.PartitionByMod">
<!-- 有多少个分片节点dataNode -->
<property name="count">2</property>
</function>
</mycat:rule>
3.2.3 server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
<property name="useHandshakeV10">1</property>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sequnceHandlerType">2</property>
<!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>-->
<!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
<property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
<!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
<!-- <property name="processorBufferChunk">40960</property> -->
<!--
<property name="processors">1</property>
<property name="processorExecutor">32</property>
-->
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
<property name="processorBufferPoolType">0</property>
<!--默认是65535 64K 用于sql解析时最大文本长度 -->
<!--<property name="maxStringLiteralLength">65535</property>-->
<!--<property name="sequnceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!--
off heap for merge/order/group/limit 1开启 0关闭
-->
<property name="useOffHeapForMerge">0</property>
<!--
单位为m
-->
<property name="memoryPageSize">64k</property>
<!--
单位为k
-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!--
单位为m
-->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">false</property>
<!-- XA Recovery Log日志路径 -->
<!--<property name="XARecoveryLogBaseDir">./</property>-->
<!-- XA Recovery Log日志名称 -->
<!--<property name="XARecoveryLogBaseName">tmlog</property>-->
<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
<property name="strictTxIsolation">false</property>
<property name="useZKSwitch">true</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--白名单可以使用通配符%或着*-->
<!--例如<host host="127.0.0.*" user="root"/>-->
<!--例如<host host="127.0.*" user="root"/>-->
<!--例如<host host="127.*" user="root"/>-->
<!--例如<host host="1*7.*" user="root"/>-->
<!--这些配置情况下对于127.0.0.1都能以root账户登录-->
<!--
<firewall>
<whitehost>
<host host="1*7.0.0.*" user="root"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<!-- 超级用户 -->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
</user>
<!-- 只读用户 -->
<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
</mycat:server>
3.3 启动连接MyCat
3.3.1 启动并观察日志
[root@localhost bin]# ./mycat start
Starting Mycat-server...
[root@localhost bin]# ./mycat status
Mycat-server is running (10552).
3.3.1.1 日志输出
- wrapper.log
程序启动过程打印的日志,一般打印三个xml文件加载过程存在的报错信息以及程序启动成功日志
STATUS | wrapper | 2020/12/29 10:32:33 | Launching a JVM...
INFO | jvm 1 | 2020/12/29 10:32:34 | Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=64M; support was removed in 8.0
INFO | jvm 1 | 2020/12/29 10:32:34 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
INFO | jvm 1 | 2020/12/29 10:32:34 | Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.
INFO | jvm 1 | 2020/12/29 10:32:34 |
INFO | jvm 1 | 2020/12/29 10:32:50 | MyCAT Server startup successfully. see logs in logs/mycat.log
-
mycat.log
程序运行过程的日志。例如:mysql连接异常日志,sql执行异常日志等。 -
switch.log
打印异常节点dataNode的状态信息。
3.3.2 Navicat for MySql连接MyCat
ip:mycat运行的服务器
port:mycat默认运行的端口
3.3.2.1 连接,查看连接界面信息
3.3.3 sql操作及验证
3.3.3.1 创建表
在MyCat连接端执行
tb_user,tb_user1,tb_user1,tb_global_user表结构一致,分别在mycat客户端执行建表语句,查看结果
CREATE TABLE `tb_user` (
`id` int(16) NOT NULL,
`hash_id` bigint(32) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`sex` varchar(8) DEFAULT NULL,
`age` int(6) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_has_id` (`hash_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `tb_user1` (
`id` int(16) NOT NULL,
`hash_id` bigint(32) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`sex` varchar(8) DEFAULT NULL,
`age` int(6) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_has_id` (`hash_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `tb_user2` (
`id` int(16) NOT NULL,
`hash_id` bigint(32) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`sex` varchar(8) DEFAULT NULL,
`age` int(6) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_has_id` (`hash_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `tb_global_user` (
`id` int(16) NOT NULL,
`hash_id` bigint(32) NOT NULL,
`name` varchar(32) DEFAULT NULL,
`sex` varchar(8) DEFAULT NULL,
`age` int(6) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_has_id` (`hash_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.3.3.2 检查各主库,表是否创建
- 主库dn1 192.168.233.101 test
- 主库dn2 192.168.233.101 test1
3.3.3.3 验证
3.3.3.3.1 tb_user求模法验证
- 在MyCat中新增一下三条数据
INSERT INTO `tb_user` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (1, 1, 1, 1, 1);
INSERT INTO `tb_user` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (2, 2, 2, 2, 2);
INSERT INTO `tb_user` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (3, 3, 3, 3, 3);
- dn1下查询
- dn2下查询
- 结论:成功根据hash_id求模分库存储
3.3.3.3.2 tb_user1指定dn1下test库存储验证
- 执行以下sql
INSERT INTO `tb_user1` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (1, 1, 1, 1, 1);
INSERT INTO `tb_user1` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (2, 2, 2, 2, 2);
INSERT INTO `tb_user1` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (3, 3, 3, 3, 3);
- 查询dn1下的test库
- 结论:tb_user1成功指定dn1下的数据库test存储
3.3.3.3.3 tb_user2指定dn2下test1存储验证
- 执行以下sql
INSERT INTO `tb_user2` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (1, 1, 1, 1, 1);
INSERT INTO `tb_user2` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (2, 2, 2, 2, 2);
INSERT INTO `tb_user2` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (3, 3, 3, 3, 3);
- 查询dn2下的test1库
- 结论:tb_user2成功指定dn2下的数据库test1存储
3.3.3.3.4 tb_global_user全局存储验证(即指定的dataNode节点均存储所有数据)
- 执行以下sql
INSERT INTO `tb_global_user` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (1, 1, 1, 1, 1);
INSERT INTO `tb_global_user` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (2, 2, 2, 2, 2);
INSERT INTO `tb_global_user` (`id`, `hash_id`, `name`, `sex`, `age`) VALUES (3, 3, 3, 3, 3);
-
查询dn1下的test库
-
查询dn2下的test1库
-
结论:tb_global_user在各个节点均存储同样的数据
4 其他
4.1 MyCat全局序列实现
建议:全局序列自定义生成,避免一些极端的情况造成系统异常。
参考:https://blog.csdn.net/lzb348110175/article/details/103389661
4.2 MyCat查询原理
https://blog.csdn.net/pingdouble/article/details/79730164
4.3 join查询解决方案
浏览了很多资料,以下推荐易懂易实现的博客方案:
https://blog.csdn.net/zouhuixing/article/details/80274064
https://blog.csdn.net/weixin_30256901/article/details/99492349