MySQL 集群(三):MySQL + Mycat 实现读写分离,主从切换集群
本章基于: 本章基于:MySQL 集群(一):Docker 搭建 MySQL,MySQL 主从同步搭建及踩坑
MySQL 主从同步,互为主备:
- master:127.0.0.1:7000
- slave:127.0.0.1:7001
下载 Mycat
我下的是 Mycat-server-1.6
Linux
版本
我的安装目录 /data/mycat
#下载
wget http://dl.mycat.io/1.6-RELEASE/Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
#解压
tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz
Mycat 主要文件
- bin:Mycat 命令,启动、重启、停止等
- catlet:支持跨分片复杂SQL实现以及存储过程支持等
- conf:配置信息,重要
- server.xml: Mycat 的配置文件,设置逻辑库账号,参数等
- schema.xml:Mycat 对应的物理数据库和数据库表的配置
- rule.xml:Mycat分片(分库分表)规则,没用到,暂时不做说明
- wrapper.conf:内存配置
- lib:Mycat 的 jar 包
- logs:日志,重要
- wrapper.log:全日志
- mycat.log:在 Mycat 成功启动后才会出现,本次启动后的日志
端口
8066
:开发端口,正常开发,操作数据库使用
9066
:管理端口,一般就热刷新配置时使用
Mycat 命令
进入 Mycat 的 bin
目录下
-
./mycat start 启动
-
./mycat stop 停止
-
./mycat console 前台运行
-
./mycat install 添加到系统自动启动(暂未实现)
-
./mycat remove 取消随系统自动启动(暂未实现)
-
./mycat restart 重启服务
-
./mycat pause 暂停
-
./mycat status 查看启动状态
配置文件
server.xml
配置看着比较多,但是上面部分都是配置里自带的,只在下面 user 标签
做了修改
PS:
逻辑库可以理解为一个虚拟库,用户只需连接这个逻辑库做操作就行了,剩下至于究竟 SQL 在哪台上执行之类的,由 Mycat 自己去选择
xmlns:mycat
的值在 1.5
版本中是 http://org.opencloudb/
/font>
xmlns:mycat
的值在 1.6
版本中是 http://io.mycat/
/font>
后面文件一样,不多赘述
<user name="root"> ... </user>
配置逻辑库账号<property name="password">123456</property>
配置逻辑库密码<property name="schemas">mysql1</property>
配置对应逻辑库,多个都用这套账号密码的,
隔开
<?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="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sequnceHandlerType">2</property>
<!-- <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-->
<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">1</property>
<!--
单位为m
-->
<property name="memoryPageSize">1m</property>
<!--
单位为k
-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!--
单位为m
-->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">true</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--
<firewall>
<whitehost>
<host host="127.0.0.1" user="mycat"/>
<host host="127.0.0.2" user="mycat"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<!-- 上面都没做改动,下面做了改动 -->
<!-- 配置 Mycat 逻辑库访问账号密码 -->
<user name="root">
<property name="password">123456</property>
<property name="schemas">mysql1</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>
schema.xml
配置
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 定义虚拟数据库名称 -->
<schema name="mysql1" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
<!-- 这里配置分库分表,因只做读写分离所以这里暂不配置 -->
</schema>
<!-- 配置数据库节点 -->
<dataNode name="dn1" dataHost="mysql1-host" database="test" />
<!-- 具体逻辑关系配置 -->
<!-- balance = 1:读请求随机分发到当前 writeHost 对应的 readHost 和 standby 的 writeHost 上 -->
<!-- writeType = 0:所有写操作发送到配置的第一个writeHost,当第一个writeHost宕机时,切换到第二个writeHost,重新启动后以切换后的为准 -->
<!-- switchType = 2:基于MySQL主从同步的状态决定是否切换 -->
<dataHost name="mysql1-host" maxCon="1000" minCon="10" balance="1" writeType="0" switchType="2" dbType="mysql" dbDriver="native" slaveThreshold=“100”>
<!-- 定时执行SQL保持心跳 -->
<heartbeat>show slave status</heartbeat>
<!-- 添加写入库配置 -->
<writeHost host="master-node1" url="118.25.215.105:7000" user="root" password="123456">
<!-- 添加只读库配置-->
<readHost host="slave-node1" url="118.25.215.105:7001" user="root" password="123456" />
</writeHost>
<!-- 备用写节点,这样才能切过来 -->
<writeHost host="master-node2" url="118.25.215.105:7001" user="root" password="123456"/>
</dataHost>
</mycat:schema>
参数说明
PS:配置说明在官方的书上都有,这里对用到的做了整理,也可以自己到官网下载 PDF
schema
逻辑库设置,可以理解成 Mycat 创建的虚拟数据库,用来给用户与实际数据库之间搭建的桥梁
name
:定义逻辑库的名称
checkSQLschema
:设为 true
时会省略数据库前缀,据说有 Bug,一般默认 false
就好了
PS:说下我这里为什么设置为 true,因为设置成 false 的情况下,由于数据库前缀没去除,导致直接用 Navicate 等工具连接 Mycat 逻辑库后直接双击表都打不开,报库名没有。因为虚拟库的库名再实际库里是不存在的,所以写 SQL 不带库名就不存在问题,怎么选择可以自己权衡
sqlMaxLimit
:当查询语句没有 limit
时就会自动帮你加上,不写就不会有这个效果,查询语句中本身有 limit
情况下以查询语句的为准
dataNode
:对应的分片节点
dataNode
对应的分片节点,由于暂时没用到分片,不多介绍,配置一个就好了
name
:节点名称
dataHost
:分片所属的物理数据库,我的理解是对应的物理数据库的逻辑关系
database
:数据库名,你具体这个节点要操作哪个数据库
dataHost(重要)
物理数据库的逻辑关系,什么读写分离,多主负载均衡,自动切换的逻辑都是这里面配,建议对着配置看
dataHost
name
:实例名称,自取
maxCon
:每个读写实例连接池的最大连接数量
minCon
:每个读写实例连接池的最小连接数量,初始化连接池大小
balance
:负载均衡类型
- balance=“0”:不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上
- balance=“1”:读请求随机分发到当前 writeHost 对应的 readHost 和 standby 的 writeHost 上。即全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1 ->S1, M2->S2,并且 M1 与 M2 互为主备),正常情况下, M2,S1,S2 都参与 select 语句的负载均衡
- balance=“2”:读请求随机分发到当前dataHost内所有的 writeHost 和 readHost 上。即所有读操作都随机的在 writeHost、readhost 上分发
- balance=“3”:读请求随机分发到当前 writeHost 对应的 readHost 上。即所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,注意 balance=“3” 只在 1.4 及其以后版本有,1.3 没有。
writeType
:同样是负载均衡类型,功能上略有不同
- writeType=“0”:所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个writeHost。重新恢复第一个 writeHost 后,不会自动切回来,还是以第二个 writeHost 为准,切换记录在配置文件中:dnindex.properties
- writeType=“1”:所有写操作都随机的发送到配置的 writeHost,Mycat 1.5 以后不再推荐使用该值
switchType
:主从切换配置
- switchType="-1":不自动切换
- switchType=“1”:默认值,自动切换
- switchType=“2”:基于MySQL主从同步的状态来决定是否切换。需修改 heartbeat 语句(即心跳语句):show slave status
- switchType=“3”:基于Mysql Galera Cluster(集群多节点复制)的切换机制,适合集群,Mycat 1.4.1 及以上版本支持。需修改heartbeat语句(即心跳语句):show status like 'wsrep%'
slaveThreshold
:需要这个配合 switchType
才能实现主备切换
dbType
:指定后端连接的数据库类型,目前支持 mysql,mongodb,oracle,spark 等
dbDriver
:指定连接后端数据库使用的 Driver
指定连接后端数据库使用的 Driver,目前可选的值有 native 和 JDBC。使用 native 的话,因为这个值执行的
是二进制的 mysql 协议,所以可以使用 mysql 和 maridb。其他类型的数据库则需要使用 JDBC 驱动来支持。
从 1.6 版本开始支持 postgresql 的 native 原始协议。
如果使用 JDBC 的话需要将符合 JDBC 4 标准的驱动 JAR 包放到 MYCAT\lib 目录下,并检查驱动 JAR 包中
包括如下目录结构的文件:META-INF\services\java.sql.Driver。在这个文件内写上具体的 Driver 类名,例如:
com.mysql.jdbc.Driver。
heartbeat:心跳 SQL,注意 switchType
属性所需要使用对应的 SQL
writeHost:配置写实例,具体数据库配置
host
:可以理解成名称,自起,日志里面根据这个 host 就知道执行的哪个数据库
url
:数据库地址
user
:用户名
password
:密码
readHost:配置写实例,具体数据库配置,配置参照 writeHost
启动
看到 finish 10 success 10 这种的就说明这个实例连接成功了,每个实例都要看下,防止有失败的
Mycat 配置热刷新
连接 Mycat 管理端口 9066
服务器上修改完配置保存
#会加载schema.xml配置的调整。
reload @@config;
#刷新全部配置
reload @@config_all;
PS:这个刷新对日志这个刷新好像不太好使,不知道是不是 Bug
日志验证:读写分离
连接 Mycat 开发端口 8066
读写分离,balance=“1”
,因此 master1
用于写,master2
,slave
用于读
- master:127.0.0.1:7000(互为主备)
- slave:127.0.0.1:7001
修改 conf
下 log4j2.xml
文件日志等级为 debug
查看日志
tail -100f mycat.log
建表
CREATE TABLE `people` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NULL COMMENT '名字',
`age` int(11) NULL COMMENT '年龄',
PRIMARY KEY (`id`)
);
通过 7000
端口看出,创建语句在 master 主库上执行的,并且 fromSlaveDB=false
也可以看出走的不是从库
新增 1 条数据
INSERT INTO people (name, age) VALUES ('小明', 12)
依然是写操作,走的 7000
master 主库上
查询列表
SELECT * FROM people
从这回的 7001
端口以及 fromSlaveDB=true
上可以看出走的从库
再次新增一条数据
INSERT INTO people (name, age) VALUES ('小红', 20)
写操作,再次走了 master
查询数据
SELECT * FROM people WHERE age < 20
读操作走了从库
日志验证:主备切换
dnindex.properties
:通过查看 conf
目录下的这个文件,可以知道mycat正在使用的 writeHost
,0表示schema.xml
中 dataHost
标签下的第一个 writeHost
模拟主的宕机
停止 master 容器
可以看到失败一定次数后,开始尝试切换数据源
来看下 dnindex.properties
执行一个写操作
INSERT INTO people (name, age) VALUES ('A', 25)
成功转移到 slave
踩坑
Mycat 启动失败
问题描述
由于内存不足,导致内存分配失败
解决方案
修改 conf
下的 wrapper.conf
文件
这两个值原大小为 4G
和 1G
,我这个服务器一共就 2G
所以内存不足了,可通过 top
查看服务器内存使用情况
Mycat 启动成功,连接数据库失败
日志报 Unknown charsetIndex:255 错误
PS:忘截图了
问题描述
由于 Mycat
字符集的配置文件匹配不到导致的错误
解决方案
conf
下的 index_to_charset.properties
文件里新增 255=utf8mb4
网上很多报的是 Unknown charsetIndex:224
同理新增 224=utf8mb4