Mycat 实现 MySQL 读写分离

Mycat 实现 MySQL 读写分离

目前MyCat配置Mysql 读写分离的文章虽然很多,但是看起来比较杂乱,造成很多困扰,所以自己根据实践并采用新的Mycat 版本将搭建步骤自始至终重写一遍。

在根据本文档操作前,基于服务器的环境已经配置了Mysql 的 主从复制模式,若没有配置参考如下文章:

https://www.cnblogs.com/ryxiong-blog/p/12513383.html

1.环境介绍

  • 主服务器(master)
  • mycat服务器
IP:10.16.195.49
系统:Linux(redhat centos7 64)
mysql版本:8.0.17
同步数据库:AIDCDATA, AIHsMdlSrv
同步帐号:model_slave
同步密码:slave123
  • 从服务器(slave)两台
IP:10.16.195.50/10.16.195.51
系统:Linux(redhat centos7 64)
mysql版本:8.0.17
同步数据库:AIDCDATA, AIHsMdlSrv

2.mycat安装

1.安装java环境

mycat依赖于java环境,下载jdk:http://dl.mycat.io/jdk-8u20-linux-x64.tar.gz

# 解压到/usr/local
tar -zxcf jdk-8u20-linux-x64.tar.gz /usr/local
mv jdk-8u20-linux-x64 jdk

添加环境变量

sudo vim /etc/profile
export JDK_HOME=/usr/local/jdk
export PATH=%PATH:$JDK_HOME/bin

# 保存退出
source /etc/profile

2.mycat安装

下载mycat安装包:http://www.mycat.io/

注意下载release版linux系统的安装包

image-20200318180659523

# 解压到/usr/local
tar -zxcf Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz /usr/local
# 解压后文件名就是mycat

配置环境变量

sudo vim /etc/profile
export MYCAT_HOME=/usr/local/mycat
export PATH=%PATH:$MYCAT_HOME/bin

# 保存退出
source /etc/profile

3.mycat配置详解

mycat核心配置文件有三个:

  • server.xml:定义用户以及系统相关变量,如端口等

  • Schema.xml:定义逻辑库,表、分片节点等内容

  • rule.xml:定义分片规则

server.xml配置详解

1.user标签--用户配置节点

mycat提供给用户登录的节点

<!-- 登陆用户名, 即MyCat连接用户名 -->

<!-- 写入帐号的配置 -->
<user name="mycat_writer">
    <!-- 登陆密码, 即MyCat连接密码 -->
	<property name="password">123456</property>
    <!-- 数据库名, 即MyCat的逻辑库, 此处会与schema.xml的配置存在关联 -->
	<property name="schemas">AIDCDATA,AIHsMdlSrv</property>
	
	<!-- 表级 DML 权限设置, 对数据库权限进行精细化管理 -->
    <!-- check表示是否开启权限检查, 默认为false, 表示不检查 -->
	<privileges check="false">
        <!-- 一个schema表示对一个数据库的权限管理, dml顺序:Insert, Update, Select, Delete -->
		<schema name="db_user" dml="0110" >
            <!-- 一个table表示对某张表进行权限管理 -->
			<table name="users" dml="1111"></table>
			<table name="useraddres" dml="1110"></table>
		</schema>
	</privileges>
</usr>
<user name="mycat_reader">
		<property name="password">123456</property>
		<property name="schemas">AIDCDATA,AIHsMdlSrv</property>
    <property name="readOnly">true</property>
</user>

2.system标签 -- 系统配置节点

默认端口8066,默认管理端口9066

<system>
    <!-- 字符集 -->
    <property name="charset">utf8</property> 
    <!-- 是否开启实时统计 1:开启, 0:关闭-->
	<property name="useSqlStat">0</property>
	<!-- 是否开启全局表一致性检测。1为开启;0为关闭 -->
	<property name="useGlobleTableCheck">0</property>
	<!-- mycat 模拟的 mysql 版本号,默认值为 5.6 版本,如非特需,不要修改这个值,目前支持设置 5.5,5.6,5.7 版本,其他版本可能会有问题 -->
    <property name="fakeMySQLVersion">5.6.20</property>
    <!-- 每次读取流的数量, 默认为4096 -->
	<property name="processorBufferChunk">40960</property>
    <!-- 处理线程数量, 默认为cpu核数 -->
	<property name="processors">1</property> 
    <!-- 创建共享buffer需要占用的总空间大小 -->
    <!-- 值计算方式: processors * processorBufferChunk * 100-->
    <property name="processorBufferPool">4096000</property>
	<!-- 服务线程池大小 -->
	<property name="processorExecutor">32</property> 
    <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
	<property name="processorBufferPoolType">0</property>
    <!-- 二级共享buffer是processorBufferPool的百分比,这里设置的是百分比 -->
    <property name="processorBufferLocalPercent">100</property>
    <!-- 清理 NIOProcessor 上前后端空闲、超时和关闭连接的间隔时间。默认是 1 秒,单位毫秒 -->
    <property name="processorCheckPeriod">1000</property>
    <!--默认是65535 64K 用于sql解析时最大文本长度 -->
	<property name="maxStringLiteralLength">65535</property>
    <!-- 全局ID生成方式。(0:为本地文件方式,1:为数据库方式;2:为时间戳序列方式;3:为ZK生成ID;4:为ZK递增ID生成-->
	<property name="sequnceHandlerType">0</property>
    <!-- 是否开启mysql压缩协议。1为开启,0为关闭,默认关闭 -->
    <property name="useCompression">1</property>
    <!-- 指定 Mysql 协议中的报文头长度。默认 4 -->
    <property name="packetHeaderSize">4</property> 
    <!-- 指定 Mysql 协议可以携带的数据最大长度。默认 16M -->
    <property name="maxPacketSize">16M</property> 
	<property name="backSocketNoDelay">1</property>
	<property name="frontSocketNoDelay">1</property>
	<property name="processorExecutor">16</property>
    <!-- 指定连接的空闲超时时间。某连接在发起空闲检查下,发现距离上次使用超过了空闲时间,那么这个连接会被回收,就是被直接的关闭掉。默认 30 分钟,单位毫秒 -->
	<property name="idleTimeout">1800000</property> 
    <!-- 前端连接的初始化事务隔离级别,只在初始化的时候使用,后续会根据客户端传递过来的属性对后端数据库连接进行同步。默认为 REPEATED_READ,设置值为数字默认 3 -->
    <!-- READ_UNCOMMITTED=1;READ_COMMITTED=2;REPEATED_READ=3;SERIALIZABLE=4; -->
    <property name="txIsolation">3</property>
    <!-- SQL 执行超时的时间,Mycat 会检查连接上最后一次执行 SQL 的时间,若超过这个时间则会直接关闭这连接。默认时间为 300 秒,单位秒 -->
    <property name="sqlExecuteTimeout">300</property>
	<!-- 对后端连接进行空闲、超时检查的时间间隔,默认是 300 秒,单位毫秒 -->
	<property name="dataNodeIdleCheckPeriod">300000</property>
	<!-- 对后端所有读、写库发起心跳的间隔时间,默认是 10 秒,单位毫秒 -->
	<property name="dataNodeHeartbeatPeriod">10000</property>
	<!-- mycat 服务监听的 IP 地址,默认值为 0.0.0.0 -->
    <property name="bindIp">0.0.0.0</property> 
	<!-- 定义 mycat 的使用端口,默认值为 8066 -->
	<property name="serverPort">8066</property> 
	<!-- 定义 mycat 的管理端口,默认值为 9066 -->
    <property name="managerPort">9066</property> 
	<property name="frontWriteQueueSize">4096</property> 
	<!-- 分布式事务开关。0为不过滤分布式事务;1为过滤分布式事务;2 为不过滤分布式事务,但是记录分布式事务日志 -->
	<property name="handleDistributedTransactions">0</property>
	<property name="useOffHeapForMerge">1</property>
	<property name="memoryPageSize">1m</property>
	<property name="spillsFileBufferSize">1k</property>
	<property name="useStreamOutput">0</property>
	<property name="systemReserveMemorySize">384m</property>
	<!-- 是否采用zookeeper协调切换 -->
	<property name="useZKSwitch">true</property>
</system>

3.firewall标签 -- 防火墙配置节点

<!-- 白名单和黑名单一般只对一个进行设置 -->
<firewall> 
	<!-- 白名单 -->
	<whitehost>
		<host host="127.0.0.1" user="mycat"/>
		<host host="127.0.0.2" user="mycat"/>
	</whitehost>
	<!-- 黑名单 -->
	<blacklist check="false">
	</blacklist>
</firewall>

4.本案例中server.xml配置文件

只修改用户配置部分,其他默认。

<user name="mycat_writer">
	<property name="password">123456</property>
	<property name="schemas">AIDCDATA,AIHsMdlSrv</property>
</usr>
<user name="mycat_reader">
		<property name="password">123456</property>
		<property name="schemas">AIDCDATA,AIHsMdlSrv</property>
    <property name="readOnly">true</property>
</user>

schema.xml配置详解

1.schema标签 -- mycat逻辑库相关配置

注意:

  • schema是mycat服务中会生成的逻辑库,是虚拟的。<table>是真实的表明。
  • 一个逻辑库<schema>可以对应多个真实数据库,通过dataNode来指定,dn1,dn2,dn3。
  • name与server.xml中schemas要对应
  • 如果<schema>中不指定<table>属性的话,需要指定dataNode属性,<schema name="db_store" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1,dn2" />
<!-- 数据库设置,此数据库为逻辑数据库,name与server.xml中schemas对应 -->
<!-- schema : 定义mycat实例中的逻辑库,也就是mycat服务中有数据库,mycat可以有多个逻辑库,每个逻辑库都有自己的相关配置 -->
<!-- name : 逻辑数据库名,与server.xml中的schemas对应-->
<!-- checkSQLschema	: 数据库前缀相关设置,当该值为true时,例如我们执行语句select * from db_store.company 。mycat会把语句修改为 select * from company 去掉db_store -->
<!-- sqlMaxLimit : 当该值设置为某个数值时,每条执行的sql语句,如果没有加上limit语句,Mycat会自动加上对应的值。不写的话,默认返回所有的值。
* 需要注意的是,如果运行的schema为非拆分库的,那么该属性不会生效。需要自己sql语句加limit -->
<!-- 如果schema中没有table配置,需要指定dataNode属性 -->
<schema name="db_store" checkSQLschema="false" sqlMaxLimit="100">
	<table name="store" dataNode="db_store_dataNode" primaryKey="storeID"/>
	<table name="employee" dataNode="db_store_dataNode" primaryKey="employeeID"/>
</schema>
<schema name="db_user" checkSQLschema="false" sqlMaxLimit="100">
	<table name="data_dictionary" type="global" dataNode="db_user_dataNode1,db_user_dataNode2" primaryKey="dataDictionaryID"/>
	<!-- name : 表名, 物理数据库中表名 -->
	<!-- dataNode : 表存在节点, 多个节点用","隔开, 连续节点通过"$"进行连接处理, 如下 -->
	<!-- primaryKey	: 主键字段名,自动生成主键时需要设置 -->
	<!-- autoIncrement : 是否自增
		* mysql对非自增长主键,使用last_insert_id() 是不会返回结果的,只会返回0.所以,只有定义了自增长主键的表,才可以用last_insert_id()返回主键值
		* mycat提供了自增长主键功能,但是对应的mysql节点上数据表,没有auto_increment,那么在mycat层调用last_insert_id()也是不会返回结果的 -->
	<!-- rule : 分片规则名, 具体在rule.xml解析 -->
	<!-- type : 该属性定义了逻辑表的类型,目前逻辑表只有全局表和普通表。全局表: global 普通表:无 -->
	<!-- needAddLimit : 指定表是否需要自动的在每个语句后面加上limit限制,由于使用了分库分表,数据量有时候会特别庞大,这时候执行查询语句,忘记加上limt就会等好久,所以mycat自动为我们加上了limit 100,这个属性默认为true,可以自己设置为false禁用。如果使用这个功能,最好配合使用数据库模式的全局序列 -->
	<!-- subTables : 分表, 分表目前不支持join -->
	<table name="users" dataNode="db_user_dataNode$1-2"  rule="mod-userID-long" primaryKey="userID">
		<!-- childTable : 定义ER分片的子表, 通过属性与父表进行关联 -->
		<!-- name : 子表(物理表)名称 -->
		<!-- joinKey : 子父表关联字段在子表的名称 -->
		<!-- parentKey : 子父表关联字段在父表的名称 -->
		<childTable name="user_address"  joinKey="userID" parentKey="userID" primaryKey="addressID"/>
	</table>
</schema>

2.dataNode标签:分片分库信息相关配置

<!-- 节点配置 : 分片信息,也就是分库相关配置-->
<!-- dataNode : 定义了mycat中的数据节点,也就是我们所说的数据分片。一个datanode标签就是一个独立的数据分片
	* 如下表述的意思为,使用名字为db_storeHOST数据库实例上的db_store物理数据库,这就组成一个数据分片,最后我们用db_store_dataNode来标示这个分片-->
<!-- name : 定义数据节点的唯一名称, 在table标签上用这个名字建立表与分片对应的关系 -->
<!-- dataHost : 用于定义该分片属于哪个数据库实例,属性与datahost标签上定义的name对应 -->
<!-- database : 用于定义该分片属于数据库实例上的物理库,也就是真实的数据库名 -->
<dataNode name="db_store_dataNode" dataHost="db_storeHOST" database="db_store" />
<dataNode name="db_user_dataNode1" dataHost="db_userHOST1" database="db_user" />
<dataNode name="db_user_dataNode2" dataHost="db_userHOST2" database="db_user" />

3.dataHost标签:mysql物理数据库相关配置

<!-- 节点主机配置 : 物理数据库,真正存储数据的数据库 -->
<!-- 这个标签直接定义了具体数据库实例,读写分离配置和心跳语句 -->
<!-- name : 唯一标示dataHost标签,供上层dataNode使用 -->
<!-- maxCon : 指定每个读写实例连接池的最大连接 -->
<!-- minCon : 指定每个读写实例连接池的最小连接 -->
<!-- balance : 负载均称类型
	* 0 : 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上
	* 1 : 全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1-S1,M2-S2 并且M1 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡
	* 2 : 所有读操作都随机的在writeHost、readHost上分发
	* 3 : 所有读请求随机的分发到writeHst对应的readHost执行,writeHost不负担读写压力 -->
<!-- writeType : 负载均衡类型
	* 0 : 所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties
	* 1 : 所有写操作都随机的发送到配置的 writeHost。1.5以后版本废弃不推荐 -->
<!-- switchType	: 节点切换方式
	* -1 : 不自动切换
	*  1 : 默认值 自动切换
	*  2 : 基于MySql主从同步的状态决定是否切换心跳语句为 show slave status
	*  3 :  基于mysql galary cluster 的切换机制(适合集群)1.4.1 心跳语句为 show status like 'wsrep%'-->
<!-- dbType : 指定后端链接的数据库类型目前支持二进制的mysql协议,还有其他使用jdbc链接的数据库,例如:mongodb,oracle,spark等 -->
<!-- dbDriver : 指定连接后段数据库使用的driver,目前可选的值有native和JDBC。使用native的话,因为这个值执行的是二进制的mysql协议,所以可以使用mysql和maridb,其他类型的则需要使用JDBC驱动来支持; 如果使用JDBC的话需要符合JDBC4标准的驱动jar 放到mycatlib目录下 -->
<!-- tempReadHostAvailable : 如果配置了这个属性 writeHost 下面的 readHost 仍旧可用,默认 0 可配置(0、1) -->
<dataHost name="db_storeHOST" maxCon="1000" minCon="10" balance="1"
		  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
	<!-- 指明用于和后端数据库进行心跳检查的语句 -->
	<!-- mysql : 可以用select user()-->
	<!-- oracle : 可以用select 1 from dual-->
	<heartbeat>select user()</heartbeat>
	<!-- readHost/writeHost : 这两个标签都指定后端数据库的相关配置,用于实例化后端连接池。唯一不同的是,writeHost 指定写实例、readHost 指定读实例 -->
	<!-- host : 标识不同的示例, 一般 writeHost 我们使用*M1,readHost 我们用*S1 -->
	<!-- url : 后端实例连接地址。
		* Native:地址:端口
		* JDBC:jdbc的url -->
	<!-- user : 后端存储实例需要的用户名称 -->
	<!-- password : 后端存储实例需要的密码 -->
	<!-- weight : 权重, 配置在 readhost 中作为读节点的权重 -->
	<!-- usingDecrypt : 是否对密码加密,默认0(不加密)。具体加密方法看官方文档 -->
	<writeHost host="hostM1" url="192.168.8.137:3306" user="root"  password="123456">
		<readHost host="hostS1" url="192.168.8.101:3306" user="root" password="123456" />
	</writeHost>
</dataHost>
<dataHost name="db_userHOST1" maxCon="1000" minCon="10" balance="0"
		  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
	<heartbeat>select user()</heartbeat>
	<writeHost host="userHost1" url="192.168.8.137:3306" user="root"  password="123456">
	</writeHost>
</dataHost>
<dataHost name="db_userHOST2" maxCon="1000" minCon="10" balance="0"
		  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
	<heartbeat>select user()</heartbeat>
	<writeHost host="userHost2" url="192.168.8.101:3306" user="root"  password="123456">
	</writeHost>
</dataHost>

4.该案例中schema.xml配置

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">

<mycat:schema xmlns:mycat="http://io.mycat/">
    <schema name="AIDCDATA" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1" />
    <schema name="AIHsMdlSrv" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn2" />

    <dataNode name="dn1" dataHost="localhost" database="backend" />
    <dataNode name="dn2" dataHost="localhost" database="AIHsMdlSrv" />

    <dataHost name="localhost" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <!-- 添加一个写入库配置 -->
        <writeHost host="hostM1" url="10.16.195.49:3306" user="root" password="mysql123">
        <!-- 添加两个只读库配置-->
        <readHost host="hostS1" url="10.16.195.50:3306" user="root" password="mysql123" />
        <readHost host="hostS2" url="10.16.195.51:3306" user="root" password="mysql123" />
        </writeHost>

    </dataHost>
     
</mycat:schema>

rule.xml配置详解

1.tableRule标签 -- 表拆分规则

<!-- 定义表拆分规则 -->
<!-- name : 属性指定唯一的名字,用于标识不同的表规则, 与scheme.xml中的rule属性对应 -->
<tableRule name="sharding-by-intfile">
	<rule>
		<!-- 指定要拆分的列名字 -->
		<columns>sharding_id</columns>
		<!-- 表拆分算法, 与function的name对应; 连接表规则和具体路由算法 -->
		<algorithm>hash-int</algorithm>
	</rule>
</tableRule>

2.function标签 -- 分片规则

<!-- name : 指定算法名称 -->
<!-- class : 指定算法具体实现的类名称 -->
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
	<!-- 具体算法需要的属性文件, 该文件在mycat文件的conf路径下 -->
	<property name="mapFile">partition-hash-int.txt</property>
</function>

3.分片规则种类

  1. 连续分片之自定义数字范围分片,0-2标识节点为数据库节点,取在schema中的节点配置顺序;该配置表示按照索引进行数据分片,索引0-5000000分配在0节点,5000000-10000000分配到1节点,以此类推
<function name="rang-long"
		class="io.mycat.route.function.AutoPartitionByLong">
	<property name="mapFile">autopartition-long.txt</property>
    <!-- 超过范围后的默认节点 -->
	<property name="defaultNode">0</property> 
</function>

2018120922271111

  1. 连续分片之按时间(月,天,小时)分片
<!-- 按日期(天)分片: 从开始日期算起,按照天数来分片 -->
<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>            <!—开始日期-->
	<property name=“sPartionDay”>10</property> <!—每分片天数-->
</function>
 
<!-- 按日期(自然月)分片: 从开始日期算起,按照自然月来分片 -->
<function name=“sharding-by-month” class=“io.mycat.route.function.PartitionByMonth">
	<property name=“dateFormat”>yyyy-MM-dd</property>        <!—日期格式-->
	<property name=“sBeginDate”>2014-01-01</property>            <!—开始日期-->
</function>
 
<!-- 按月小时进行分片, 最多24个分片, 最少1个分片, 一个月完成后从头开始循环, 每个月月尾, 需要手动清理备份数据 -->
<function name="sharding-by-hour" class=“io.mycat.route.function.LatestMonthPartion"> 
	<property name=“splitOneDay”>24</property> <!-- 将一天的数据拆解成几个分片-->
</function>
  1. 离散分片之枚举分片 -- 通过在配置文件中配置可能的枚举id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的
<function name="hash-int" class=“io.mycat.route.function.PartitionByFileMap">
    <!-- 配置文件名称 -->
 	<property name="mapFile">partition-hash-int.txt</property> 
    <!-- type默认值为0(0表示Integer,非零表示String) -->
	<property name="type">0</property> 
    <!-- 默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点 -->
	<property name="defaultNode">0</property> 
</function>

20181209223640111

  1. 十进制求模分片:规则为对分片字段十进制取模运算,数据分布最均匀
<function name="mod-long" class=“io.mycat.route.function.PartitionByMod"> 
	<!-- 参与分片的节点数量  --> 
	<property name="count">3</property> 
</function>
  1. 离散分片之应用指定分片 -- 规则为对分片字段进行字符串截取,获取的字符串即指定分片

    例如 id=05-100000002 在此配置中代表根据 id 中从startIndex=0 开始,截取 size=2 位数字即 05,05 就是获取的分区,如果没传默认分配到 defaultPartition

<function name="sharding-by-substring“ class="io.mycat.route.function.PartitionDirectBySubString">
    <!-- 开始截取的位置 -->
	<property name="startIndex">0</property>
    <!-- 截取的长度 -->
	<property name="size">2</property>
    <!-- 分片数量 -->
	<property name="partitionCount">8</property>
    <!-- 默认分片 -->
	<property name="defaultPartition">0</property>
</function>
  1. 离散分片之字符串截取数字hash分片 -- 此规则是截取字符串中的int数值hash分片
<function name="sharding-by-stringhash" class=“io.mycat.route.function.PartitionByString"> 
    <!-- 字符串hash求模基数, count表示分片数量  lenth * count = 1024 -->
	<property name=length>512</property>
	<property name="count">2</property> 
    <!--hash预算位, 没搞懂...-->
	<property name="hashSlice">0:2</property>
</function>
  1. 离散分片之一致性Hash分片 -- 此规则优点在于扩容时迁移数据量比较少
<function name="murmur" class=“io.mycat.route.function.PartitionByMurmurHash"> 
    <!-- 创建hash对象的种子,默认0--> 
	<property name=“seed”>0</property>
    <!-- 要分片的数据库节点数量,必须指定,否则没法分片--> 
	<property name="count">2</property>
    <!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
	<property name="virtualBucketTimes">160</property>
</ function>
  1. 综合分片之范围求模分片

    • 先进行范围分片计算出分片组,组内再求模,可以进行范围分片到0-200M,再组内分片到5个分片其中之一
    • 可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题
    • 分片组内使用求模可以保证组内数据比较均匀,分片组之间是范围分片可以兼顾范围查询
<function name="rang-mod" class=“io.mycat.route.function.PartitionByRangeMod"> 
	<property name="mapFile">partition-range-mod.txt</property>
 	<property name="defaultNode">32</property> 
</function>

以下配置一个范围代表一个分片组,=号后面的数字代表该分片组所拥有的分片的数量

img

  1. 综合分片之取模范围约束分片 -- 对指定分片列进行取模后再由配置决定数据的节点分布

    • 1-32 即代表id%256后分布的范围,=后面的数字表示分配的节点

    • 如果id非数字,则分配在defaoultNode默认节点

<function name="sharding-by-pattern" class=“io.mycat.route.function.PartitionByPattern">
    <!-- 求模基数 -->
 	<property name="patternValue">256</property>
 	<property name="defaultNode">2</property> 
	<property name="mapFile">partition-pattern.txt</property> 
 </function>

20181209225242710

4.该案例中没有涉及拆分表的操作,没有配置rule.xml

4.Mycat启动

主从mysql数据库中都需要新建scheme.xml配置中的数据库用户。案例使用的是root。

1.启动mycat服务

/usr/local/mycat/bin/mycat start
# 配置了环境变量
mycat start

2.查看错误日志

cat /usr/local/mycat/logs/wrapper.log

image-20200318195455346

看到这个表示成功启动

3.mycat登录

如果在server.xml中没有配置serverPort,默认连接端口为8066

# 登录mycat服务
mysql -h 127.0.0.1 -umycat_writer -pmycat_writer -P8066

可以在mysql控制台执行sql命令

5.Mycat 管理命令与监控

管理命令

1.登录方式

MyCAT 自身有类似其他数据库的管理监控方式,可以通过 Mysql 命令行,登录管理端口(9066)执行相应 的 SQL 进行管理

  • 8066 数据端口默认
  • 9066 管理端口默认

命令行的登陆是通过 9066 管理端口来操 作,登录方式类似于 mysql 的服务端登陆。

mysql -h127.0.0.1 -umycat_writer -pmycat_writer -P9066 [-dmycat]
-h 后面是主机,即当前 mycat 按照的主机地址,本地可用 127.0.0.1 远程需要远程 ip -u Mycat server.xml 中配置的逻辑库用户
-p Mycat server.xml 中配置的逻辑库密码
-P 后面是端口 默认 9066,注意 P 是大写
-d Mycat server.xml 中配置的逻辑库 

管理端口用于执行管理命令:

mysql -h127.0.0.1 -umycat_writer -pmycat_writer -P9066 

命令端口用户执行增删改查等 SQL 语句:

mysql -h127.0.0.1 -umycat_writer -pmycat_writer -P8066 

2.管理命令

从9066 管理端口登陆后,执行 show @@help 可以查看到所有命令

mysql> show @@help;
+-------------------------------------+--------------------------------------------+
| STATEMENT                           | DESCRIPTION                                |
+-------------------------------------+--------------------------------------------+
| show @@time.current                 | Report current timestamp                   |
| show @@time.startup                 | Report startup timestamp                   |
														·
														·
| online                              | Change MyCat status to ON                  |
| clear @@slow where schema = ?       | Clear slow sql by schema                   |
| clear @@slow where datanode = ?     | Clear slow sql by datanode                 |
+-------------------------------------+--------------------------------------------+
58 rows in set (0.00 sec)

3.查看mycat版本

mysql> show @@version;
+-----------------------------------------+
| VERSION                                 |
+-----------------------------------------+
| 5.6.29-mycat-1.6-RELEASE-20161028204710 |
+-----------------------------------------+
1 row in set (0.00 sec)

4.查看当前的库

mysql> show @@database;
+----------+
| DATABASE |
+----------+
| testdb   |
+----------+
1 row in set (0.00 sec)

5.查看 MyCAT 的数据节点的列表,对应 schema.xml 配置文件的 dataNode 节点:

mysql> show @@datanode;
+------+------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
| NAME | DATHOST    | INDEX | TYPE  | ACTIVE | IDLE | SIZE | EXECUTE | TOTAL_TIME | MAX_TIME | MAX_SQL | RECOVERY_TIME |
+------+------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
| dn1  | vm3306/db1 |     0 | mysql |      0 |    8 | 1000 |     244 |          0 |        0 |       0 |            -1 |
+------+------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
1 row in set (0.00 sec)

其中,“NAME”表示 dataNode 的名称;“dataHost”表示对应 dataHost 属性的值,即数据主机; “ACTIVE”表示活跃连接数;“IDLE”表示闲置连接数;“SIZE”对应总连接数量。

这里有 8 个空闲连接,去主从节点用 netstat -ntp 命令看看建立的连接情况:

  • master:
[root@vm1 ~]# netstat -ntp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 10.16.195.49            192.168.0.104:60060         ESTABLISHED 1492/sshd
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58636  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58640  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58582  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58644  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58646  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58641  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58635  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.121:58632  ESTABLISHED 1414/mysqld
tcp        0      0 ::ffff:10.16.195.49:3306   ::ffff:192.168.0.122:48205  ESTABLISHED 1414/mysqld
  • slave:
[root@vm3 ~]# netstat -ntp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 10.16.195.50:48205         192.168.0.120:3306          ESTABLISHED 1607/mysqld
tcp        0      0 10.16.195.50:22            192.168.0.104:60102         ESTABLISHED 1196/sshd
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45593  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45591  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45583  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45589  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45579  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45580  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45588  ESTABLISHED 1607/mysqld
tcp        0      0 ::ffff:10.16.195.50:3306   ::ffff:192.168.0.121:45577  ESTABLISHED 1607/mysqld

可看到有很多从 mycat 服务器发起数据库连接(主有9个连接,从有8个连接)。

6.查看心跳报告:

mysql> show @@heartbeat;
+--------+-------+---------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| NAME   | TYPE  | HOST          | PORT | RS_CODE | RETRY | STATUS | TIMEOUT | EXECUTE_TIME | LAST_ACTIVE_TIME    | STOP  |
+--------+-------+---------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| hostM1 | mysql | 10.16.195.49 | 3306 |       1 |     0 | idle   |       0 | 1,1,1        | 2020-02-18 06:44:38 | false |
| hostS1 | mysql | 10.16.195.50 | 3306 |       1 |     0 | idle   |       0 | 1,1,1        | 2020-03-18 06:44:38 | false |
+--------+-------+---------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
2 rows in set (0.00 sec)

该命令用于报告心跳状态

RS_CODE 状态:
    OK_STATUS = 1;正常状态
    ERROR_STATUS = -1; 连接出错
    TIMEOUT_STATUS = -2; 连接超时
    INIT_STATUS = 0; 初始化状态

若节点故障,会连续默认 5 个周期检测,心跳连续失败,就会变成-1,节点故障确认,然后可能发生切换

7.查看 Mycat 的前端连接状态,即应用与 mycat 的连接:

mysql> show @@connectionG
*************************** 1. row ***************************
    PROCESSOR: Processor0
           ID: 1
         HOST: 127.0.0.1
         PORT: 9066
   LOCAL_PORT: 50317
       SCHEMA: NULL
      CHARSET: latin1:8
       NET_IN: 257
      NET_OUT: 6343
ALIVE_TIME(S): 1264
  RECV_BUFFER: 4096
   SEND_QUEUE: 0
      txlevel:
   autocommit:
1 row in set (0.00 sec)

从上面获取到的连接 ID 属性,可以手动杀掉某个连接。

kill @@connection id,id,id

8.显示后端连接状态:

mysql> show @@backendG
...
...
...
*************************** 16. row ***************************
 processor: Processor0
        id: 4
   mysqlId: 8
      host: 192.168.0.122
      port: 3306
    l_port: 45583
    net_in: 7018
   net_out: 1646
      life: 6287
    closed: false
  borrowed: false
SEND_QUEUE: 0
    schema: db1
   charset: utf8:33
   txlevel: 3
autocommit: true
16 rows in set (0.00 sec)

一共有16个后端连接,这里截取最后一个。

9.显示数据源:

mysql> show @@datasource;
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| DATANODE | NAME   | TYPE  | HOST          | PORT | W/R  | ACTIVE | IDLE | SIZE | EXECUTE | READ_LOAD | WRITE_LOAD |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| dn1      | hostM1 | mysql | 10.16.195.49 | 3306 | W    |      0 |    8 | 1000 |     231 |         0 |          2 |
| dn1      | hostS1 | mysql | 10.16.195.50 | 3306 | R    |      0 |    8 | 1000 |     211 |         8 |          0 |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
2 rows in set (0.00 sec)

可以看到主从信息。

执行SQL语句

登录数据管理端口

mysql -h127.0.0.1 -utest -ptest -P8066

创建 users表:

mysql> show databases;
+------------+
| DATABASE   |
+------------+
| AIDCDATA   |
| AIHsMdlSrv |
+------------+
1 row in set (0.00 sec)

mysql> use AIDCDATA;create table users (id INT, name VARCHAR(20));
Database changed
Query OK, 0 rows affected (0.25 sec)

mysql> show tables;
+---------------+
| Tables_in_db1 |
+---------------+
| users         |
+---------------+
1 row in set (0.01 sec)

插入两条数据:

mysql> insert into users values (1, 'guli'), (2, 'xie');
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

查看一下插入结果:

mysql> select * from users;
+------+------+
| id   | name |
+------+------+
|    1 | guli |
|    2 | xie  |
+------+------+
2 rows in set (0.00 sec)

没问题。

再分别到主从节点看数据插入没有:

master:

mysql> use AIDCDATA;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+--------------------+
| Tables_in_AIDCDATA |
+--------------------+
| users              |
+--------------------+
1 row in set (0.00 sec)

mysql> select * from users;
+------+------+
| id   | name |
+------+------+
|    1 | guli |
|    2 | xie  |
+------+------+
2 rows in set (0.00 sec)

slave:

mysql> use AIDCDATA;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+---------------+
| Tables_in_db1 |
+---------------+
| tb1           |
+---------------+
1 row in set (0.00 sec)

mysql> select * from users;
+------+------+
| id   | name |
+------+------+
|    1 | guli |
|    2 | xie  |
+------+------+
2 rows in set (0.00 sec)

好,可以看到 OK 了。

查看刚才执行过的 sql 语句:

mysql> show @@sql;
+------+------+---------------+--------------+-------------------+
| ID   | USER | START_TIME    | EXECUTE_TIME | SQL               |
+------+------+---------------+--------------+-------------------+
|    1 | mycat_writer | 1485212346188 |            1 | select * from AIDCDATA |
|    2 | mycat_writer | 1485212040101 |            1 | select * from AIDCDATA |
|    3 | mycat_writer | 1485211834831 |            1 | select * from AIDCDATA |
|    4 | mycat_writer | 1485211803688 |            1 | select * from AIDCDATA |
|    5 | mycat_writer | 1485209518691 |            2 | select * from AIDCDATA |
+------+------+---------------+--------------+-------------------+
5 rows in set (0.00 sec)

遇到的问题:

似乎无法统计 insert 语句,不知为什么。

查看统计数据:

mysql> show @@sql.sum;
+------+------+------+------+------+------+--------+---------+--------------+--------------+---------------+
| ID   | USER | R    | W    | R%   | MAX  | NET_IN | NET_OUT | TIME_COUNT   | TTL_COUNT    | LAST_TIME     |
+------+------+------+------+------+------+--------+---------+--------------+--------------+---------------+
|    1 | mycat_writer |    5 |    0 | 1.00 | 1    |     85 |     709 | [5, 0, 0, 0] | [5, 0, 0, 0] | 1485212346189 |
+------+------+------+------+------+------+--------+---------+--------------+--------------+---------------+
1 row in set (0.00 sec)

端口号: 该命令工作在 9066 端口,用来记录用户通过本地 8066 端口向 Mycat-Server 发送的 SQL 请求执行
信息。信息包括有 ID 值,执行 SQL 语句的用户名称,执行的 SQL 语句,命令执行的起始时间,命令执行消耗时间

查看慢查询语句:

设置慢查询阈值为0:reload @@sqlslow=0;

mysql> reload @@sqlslow=0;
Query OK, 1 row affected (0.00 sec)
Reset show  @@sql.slow time success

在8066端口执行查询:select * from users;

mysql> select * from users;
+------+-------+
| id   | name  |
+------+-------+
|    1 | guli  |
|    2 | xie   |
|    3 | xu    |
|    4 | he    |
|    5 | huang |
|    6 | ma    |
|    7 | liu   |
|    8 | zeng  |
+------+-------+
8 rows in set (0.00 sec)

在 9066 端口执行 show @@sql.slow 查看抓取的慢查询SQL语句:

mysql> show @@sql.slow;
+------+------------+---------------+--------------+-------------------+
| USER | DATASOURCE | START_TIME    | EXECUTE_TIME | SQL               |
+------+------------+---------------+--------------+-------------------+
| test | NULL       | 1485213017329 |            1 | select * from tb1 |
+------+------------+---------------+--------------+-------------------+
1 row in set (0.00 sec)

验证读写分离是否已经成功

使用mysql客户端连接9066管理端口,执行 show @@datasource 可以观察到 READ_LOAD,WRITE_LOAD 两个统计参数的变化:

这里显示 hostM1 为写节点,hostS1、hostS2 为读节点:

hostM1 的 WRITE_LOAD = 2

hostS1 的 READ_LOAD = 3

hostS2 的 READ_LOAD = 5

mysql> show @@datasource;
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| DATANODE | NAME   | TYPE  | HOST          | PORT | W/R  | ACTIVE | IDLE | SIZE | EXECUTE | READ_LOAD | WRITE_LOAD |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| dn1      | hostM1 | mysql | 10.16.195.49 | 3306 | W    |      0 |    8 | 1000 |     287 |         0 |          2 |
| dn1      | hostS1 | mysql | 10.16.195.50 | 3306 | R    |      0 |    8 | 1000 |     271 |         3 |          0 |
| dn1      | hostS1 | mysql | 10.16.195.51 | 3306 | R    |      0 |    8 | 1000 |     271 |         5 |          0 |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
2 rows in set (0.00 sec)

使用mysql客户端连接8066管理端口,执行查询,插入语句,同时使用mysql客户端连接 9066 端口观察一下读写统计参数的变化:

8066:执行查询 select * from users;

mysql> select * from users;
+------+------+
| id   | name |
+------+------+
|    1 | guli |
|    2 | xie  |
|    3 | xu   |
|    4 | he   |
+------+------+
4 rows in set (0.00 sec)

9066:

mysql> show @@datasource;
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| DATANODE | NAME   | TYPE  | HOST          | PORT | W/R  | ACTIVE | IDLE | SIZE | EXECUTE | READ_LOAD | WRITE_LOAD |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| dn1      | hostM1 | mysql | 10.16.195.49 | 3306 | W    |      0 |    8 | 1000 |     287 |         0 |          2 |
| dn1      | hostS1 | mysql | 10.16.195.50 | 3306 | R    |      0 |    8 | 1000 |     271 |         4 |          0 |
| dn1      | hostS1 | mysql | 10.16.195.51 | 3306 | R    |      0 |    8 | 1000 |     271 |         5 |          0 |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
2 rows in set (0.00 sec)

读节点的读计数加1,

hostM1 的 WRITE_LOAD = 2
hostS1 的 READ_LOAD = 4
hostS2 的 READ_LOAD = 5

8066:执行插入操作 insert into users values (5,'huang');

mysql> insert into users values (5,'huang');
Query OK, 1 row affected (0.02 sec)

9066:

mysql> show @@datasource;
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| DATANODE | NAME   | TYPE  | HOST          | PORT | W/R  | ACTIVE | IDLE | SIZE | EXECUTE | READ_LOAD | WRITE_LOAD |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
| dn1      | hostM1 | mysql | 10.16.105.49 | 3306 | W    |      0 |    8 | 1000 |     332 |         0 |          4 |
| dn1      | hostS1 | mysql | 10.16.105.52 | 3306 | R    |      0 |    8 | 1000 |     315 |        13 |          0 |
| dn1      | hostS2 | mysql | 10.16.105.51 | 3306 | R    |      0 |    8 | 1000 |     315 |        13 |          0 |
+----------+--------+-------+---------------+------+------+--------+------+------+---------+-----------+------------+
2 rows in set (0.00 sec)

写节点的读计数加1

hostM1 的 WRITE_LOAD = 3
hostS1 的 READ_LOAD = 4
hostS2 的 READ_LOAD = 5

由此可见读写分离是成功的。可以看到数据也成功写入数据库:

mysql> select * from users;
+------+-------+
| id   | name  |
+------+-------+
|    1 | guli  |
|    2 | xie   |
|    3 | xu    |
|    4 | he    |
|    5 | huang |
+------+-------+
5 rows in set (0.00 sec)

到此基本演示了 mycat 的主从读写分离功能,配置的前提是已经有一个配置好的 mysql 主从复制架构,mycat 工作于 mysql 主从架构的前端,负责 SQL 语句的分发。

6.Mycat服务踩坑

1.错误现象

查看mycat日志,一直显示连不上服务器

cat /usr/local/mycat/logs/mycat.log

错误内容

2020-03-18 17:52:34.787 INFO [$_NIOREACTOR-7-RW] (io.mycat.sqlengine.SQLJob.connectionError(SQLJob.java:125)) - can't get connection for sql: select user()
2020-03-18 17:52:34.787 ERROR [$_NIOREACTOR-7-RW] (io.mycat.backend.heartbeat.MySQLHeartbeat.nextDecter(MySQLHeartbeat.jave:215)) - set Error 3 DBHsotConfig [hostName=hostS1, url=10.16.195.50:3306]
2020-03-18 17:02:03.787 INFO [S_NIOREACTOR-7-RW]  (io.mycat.net.AbstractConnection.close (Abstractconnection.java:520)) - close connection, reason:stream closed,MySQLConnection[id=165, LastTime=1584522123784, user=root, schema=AIHsMdl
Srv, old shema=AIHsMdlSrv, borrowed=false, fromSlaveDB=true, threadId=0, charset=utf8, txIsoation=3, autocommit=true, attachment=null, respHandler=null, host=10.16.195.50, port=3306, statusSync=null, writeQueue=0, modifiedSQLExecuted=false]
2020-03-18 17:52:34.787 INFO [$_NIOREACTOR-7-RW] (io.mycat.backend.datasource.PhysicalDatasource$1$1.connectionError(PhysicalDatasource.java508))

遇到这个错误,去网上搜罗了很多解决方法,最主要的说法是:

  • 检查schema.xml中读写用户的帐号和密码,是否和数据库配置的一致。

我确认了几次,确实是一致的。

2.错误现象2

然后偶然通过navicate连接mysql服务的时候,遇到错误:

1251 - Client does not support authentication protocol requested by server; consider upgrading MySQL client

查到解决办法:https://www.cnblogs.com/ryxiong-blog/p/12518573.html

修改用户密码的加密规则,降到原来的规则。

alter user 'root'@'%' identified with mysql_native_password by "123456";
flush privileges;

重新连接就可以。

3.解决方法

那么是不是mycat也有这种问题呢,导致连接不上mysql服务器。

于是将3台mysql服务器的密码加密规则全部退回mysql_native_password。

4.重启mysql服务和mycat服务

service mysql restart
mycat restart
原文地址:https://www.cnblogs.com/ryxiong-blog/p/12519977.html