MyCat 实战篇-从入门到落地

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值