MyCat是基于阿里巴巴的开源产品CoBar演变而来,以代替昂贵的oracle的MySQL集群的中间件。关于MyCat的介绍在此不多赘述(详细介绍 : 查看Mycat的详细介绍)。
基于上篇的实验,我们当前的环境为:
角色 服务端口 用户 密码
master 3306 root root@3306
slave 3307 root root@3307
slave 3308 root root@3308
已经实现master中发生写操作会自动同步到两个slave服务,下面将在此环境下配置mycat实现读写分离,将3306作为写库,3307、3308作为读操作用的库。
一、下载、配置MyCat
1.官方网站下载已编译好的安装包
cd /software/ 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
2.配置java环境
mycat基于jdk1.7,最低要用jdk1.7的版本,当前测试用的jdk1.8
[root@iZ2ze1np3s3kfsddhxqr0gZ software]# java -version java version "1.8.0_141" Java(TM) SE Runtime Environment (build 1.8.0_141-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
省略配置过程...
3.配置mycat
mycat配置文件下都在conf目录下,本测试需要配置两个文件
server.xml : 保存了所有mycat需要的系统配置信息。其在代码内直接的映射类为SystemConfig类。现在就对这个文件中的配置。
Schema.xml :作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、表、分片规则、DataNode以及DataSource等。
配置server.xml:
<user name="root">
<property name="password">123456</property>
<property name="schemas">test_db</property></user>
<user name="user">
<property name="password">user</property>
<property name="schemas">test_db</property>
<property name="readOnly">true</property>
</user>
配置两个用户 ,root 和user (只读权限),这里的用户与数据库无关,作为mycat登录时用,test_db为逻辑数据库,与schema.xml中对应
配置schema.xml:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/"><table name="goods" primaryKey="ID" type="global" dataNode="dn1" />
<schema name="test_db" checkSQLschema="false" sqlMaxLimit="100" >
<!--只有一个dataNode-->
<dataNode name="dn1" dataHost="localhost1" database="test_db" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slav eThreshold="100">
<heartbeat>select user()</heartbeat>
<!--方案一 ,M1写 S1 S2读 -->
<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="root@3306">
<readHost host="hostS1" url="127.0.0.1:3307" user="root" password="root@3307" />
<readHost host="hostS2" url="127.0.0.1:3308" user="root" password="root@3308" />
</writeHost></mycat:schema>
这里要配置 balance=“1” ,默认为0
table标签属性说明:
Table 标签定义了MyCat中的逻辑表,所有需要拆分的表都需要在这个标签中定义。name定义逻辑表的表名,这个名字就如同我在数据库中执行create table命令指定的名字一样,同个schema标签中定义的名字必须唯一。我们定义一个 goods 表来测试读写分离。
dataHost标签属性说明:
- name属性
唯一标识dataHost标签,供上层的标签使用。 - maxCon属性
指定每个读写实例连接池的最大连接。也就是说,标签内嵌套的writeHost、readHost标签都会使用这个属性的值来实例化出连接池的最大连接数。 - minCon属性
指定每个读写实例连接池的最小连接,初始化连接池的大小。 - balance属性
负载均衡类型,目前的取值有3种:
1. balance=“0”, 所有读操作都发送到当前可用的writeHost上。
2. balance=“1”,所有读操作都随机的发送到readHost。
3. balance=“2”,所有读操作都随机的在writeHost、readhost上分发。 - writeType属性
负载均衡类型,目前的取值有3种:
1. writeType=“0”, 所有写操作都发送到可用的writeHost上。
2. writeType=“1”,所有写操作都随机的发送到readHost。
3. writeType=“2”,所有写操作都随机的在writeHost、readhost分上发。 - dbType属性
指定后端连接的数据库类型,目前支持二进制的mysql协议,还有其他使用JDBC连接的数据库。例如:mongodb、oracle、spark等。 - dbDriver属性
指定连接后端数据库使用的Driver,目前可选的值有native和JDBC。使用native的话,因为这个值执行的是二进制的mysql协议,所以可以使用mysql和marib。其他类型的数据库则需要使用JDBC驱动来支持。如果使用JDBC的话需要将符合JDBC 4标准的驱动JAR包放到MYCAT\lib目录下,并检查驱动JAR包中包括如下目录结构的文件:META-INF\services\java.sql.Driver。在这个文件内写上具体的Driver类名,例如:com.mysql.jdbc.Driver。
4.创建测试表goods
登录3306主库,在test_db中创建测试表goods:
mysql>use test_db; mysql>create table goods (id int primary key , gname varchar(50), gprice float , gdesc varchar(200) ) ;
二、启动MyCat,测试读写分离是否配置成功
操作命令:./bin/mycat start/stop/restart
日志:logs/mycat.log 和 logs/wrapper.log
1.启动mycat
./bin/mycat start
查看wrapper.log日志,如果出现
Error: Exception thrown by the agent : java.net.MalformedU
RLException: Local host name unknown: java.net.UnknownHostException: iZ2ze1np3s3kfsddhxqr0gZ: iZ2ze1
np3s3kfsddhxqr0gZ: Name or service not known 之类的错误
可能是由于hosts文件中 iZ2ze1np3s3kfsddhxqr0gZ 主机名没有映射到127.0.0.1, 加一条记录就好
vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
127.0.0.1 iZ2ze1np3s3kfsddhxqr0gZ
重新启动mycat并检查日志
查看服务成功启动:
ps -ef | grep mycat
查看服务端口,mycat默认监听8066和9066:
netstat -ant | grep 8066 / 9066
2.成功启动后,登录mycat,向goods表插入测试数据
当前操作登录到mycat(8066):
#这里的用户密码是在 mycat中 server.xml配置的 数据库 为逻辑数据库
mysql -uroot -p123456 -h127.0.0.1 -P8066 test_db
#查看表,我们只配置了一个goods表,所以只能看到一个
mysql> show tables;
+-------------------+
| Tables in test_db |
+-------------------+
| goods |
+-------------------+
1 row in set (0.00 sec)
#插入测试数据,为了区别我们的数据是从哪里来的,这里使用了@@PORT全局变量获取了mysql服务端端口,在select的时候发现端口为3307
insert into goods (id, gname , gprice , gdesc ) values ( 100,'果粒橙',3.5,@@PORT );
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> select * from goods;
+-----+-----------+--------+-------+
| id | gname | gprice | gdesc |
+-----+-----------+--------+-------+
| 100 | 果粒橙 | 3.5 | 3307 |
+-----+-----------+--------+-------+
1 row in set (0.00 sec)
mysql>
猜想:由于我们mysql已经做过主从同步,所以执行insert之后 3306和3307中会同步这条记录,但是由于端口不同所以gdesc中会有各自的端口
论证:
接下来登录到3306,看看goods表的内容
当前操作登录到3306:
Database changed mysql> select * from goods; +-----+-----------+--------+-------+ | id | gname | gprice | gdesc | +-----+-----------+--------+-------+ | 100 | 果粒橙 | 3.5 | 3306 | +-----+-----------+--------+-------+ 1 row in set (0.00 sec)
再登录到3307查看goods表中的数据
当前操作登录到3307:
mysql> select * from goods; +-----+-----------+--------+-------+ | id | gname | gprice | gdesc | +-----+-----------+--------+-------+ | 100 | 果粒橙 | 3.5 | 3307 | +-----+-----------+--------+-------+ 1 row in set (0.00 sec)
再登录到3308查看goods表中的数据
当前操作登录到3308:
mysql> select * from goods; +-----+-----------+--------+-------+ | id | gname | gprice | gdesc | +-----+-----------+--------+-------+ | 100 | 果粒橙 | 3.5 | 3308 | +-----+-----------+--------+-------+ 1 row in set (0.00 sec)
毫无疑问,刚刚我们在mycat控制台查询到的数据,来源为3307中的数据!
接下来我们停掉3307服务,再查询goods表
[root@iZ2ze1np3s3kfsddhxqr0gZ software]# sh mysqlStop.sh 3307
回到mycat(8066)控制台,查询goods表:
mysql> select * from goods; +-----+-----------+--------+-------+ | id | gname | gprice | gdesc | +-----+-----------+--------+-------+ | 100 | 果粒橙 | 3.5 | 3308 | +-----+-----------+--------+-------+ 1 row in set (0.00 sec)
如图所示:
结论:由于我们在writeHost 配置了两个readHost ,mycat在查询的时候优先查询hostS1(3307),当hostS1不可用时,自动切换到hsotS2(3308)。该方案缺点,当写库hostM1挂掉,hostS1、hostS2均不能正常服务。
另一种配置方式--- 不使用mycay托管主从切换
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slav eThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 配置一个用于写的库 -->
<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="root@3306">
<!-- 可以配置多个用于读的库 -->
<writeHost host="hostS1" url="127.0.0.1:3307" user="root" password="root@3307" />
<writeHost host="hostS2" url="127.0.0.1:3308" user="root" password="root@3308" />
</writeHost>
这种方式也是优先将M1作为写,S1、S2作为随机读,当M1挂掉 S1、S2能继续使用,且S1变为写库,S2变为读库,当M1恢复后,M1变为读库,S1仍然为写库不变,由于此时并没有使用mycat托管主从切换,所以当M1挂掉,S1作为写库新增的数据不会同步到S2,导致读取S2库里的数据可能为旧数据!
切换主机的记录在 conf/dnindex.properties ,查看该文件:
#update
#Tue Aug 15 14:27:34 CST 2017
localhost1=1
此时应当重新配置主从同步服务,将S1变为主,M1变为从加入到从库后!或者将M1同步到最新,mycat中执行切换命令switch @@datasource localhost1:0
另一种配置方式--使用mycat托管主从切换
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="2" writeType="0" dbType="mysql" dbDriver="native" switchType="2" slav eThreshold="100">
<heartbeat>show slave status</heartbeat>
<!-- 配置一个用于写的库 -->
<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="root@3306">
<!-- 可以配置多个用于读的库 -->
<writeHost host="hostS1" url="127.0.0.1:3307" user="root" password="root@3307" />
<writeHost host="hostS2" url="127.0.0.1:3308" user="root" password="root@3308" />
</writeHost>
当配置了Mycat托管主从,且 balance="2" 所有读操作在三个主机随机进行,写操作在M1进行。当M1挂掉,自动切换S1为主库
附录
- Mycat 捐赠地址 :http://www.mycat.io/donate.html
- Mycat 官斱网站:http://www.mycat.io/
- Mycat 源码:https://github.com/MyCATApache/Mycat-Server
- Mycat 下载地址:https://github.com/MyCATApache/Mycat-download