MyCAT的一个典型应用是用于数据库的读写分离,本文以Mysql为例,展示读写分离的MyCAT应用场景。本次操作使用的是Ubuntu14.04。
安装Mysql主从复制
本文仅仅展示最简单的Mysql主从复制配置,实际使用中应该会更加复杂。
先配置主Mysql,先要修改/etc/mysql/my.cnf文件,
1
2
3
[mysqld]
log-bin=mysql-bin
server-id=1
然后重启主Mysql,并登陆,运行以下命令,
1
2
3
4
5
6
7
8
9
10
mysql> GRANT REPLICATION SLAVE ON *.* to 'root' @'%' identified by '111111' ;
Query OK, 0 rows affected, 1 warning (0.05 sec)
+------------------+----------+--------------+------------------+-------------------+
+------------------+----------+--------------+------------------+-------------------+
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
以上命令表示我们是用root用户镜像主从复制,实际使用中应该会使用其他用户。
主Mysql的配置比较简单,从Mysql的配置稍微麻烦一点。先修改/etc/mysql/my.cnf文件,
注意,server-id一定不能与主Mysql相同,这是表示服务器的唯一id。然后重启从Mysql,登陆,运行如下命令,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> change master to master_host='master-1',master_port=3306,master_user='root',master_password='111111',master_log_file='mysql-bin.000007',master_log_pos=429;
Query OK, 0 rows affected, 2 warnings (0.44 sec)
mysql> start slave;
Query OK, 0 rows affected (0.02 sec)
mysql> show slave status\G
* * * * * * * * * * * * * * * * * * * * * * * * * * * 1. row * * * * * * * * * * * * * * * * * * * * * * * * * * *
Slave_IO_State: Waiting for master to send event
Master_Host: master-1
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000007
Read_Master_Log_Pos: 429
Relay_Log_File: 1d508b1aa846-relay-bin.000002
Relay_Log_Pos: 595
......
注意,以上命令中的master_log_file='mysql-bin.000007',master_log_pos=429要与主Mysql中的show master status结果一致。另外,如果配置change master的时候配置错了,需要先运行stop slave停止slave,再运行reset slave清空slave的配置,然后再从头开始配置。
配置完成之后,在主Mysql上进行create database可以同步到从Mysql。按MyCAT测试库的案例,创建db1、db2、db3三个数据库。
MyCAT配置主从复制
MyCAT主从复制的配置主要修改schema.xml文件,修改如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
......
<dataHost name ="localhost1" maxCon ="1000" minCon ="10" balance ="1"
writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="2" slaveThreshold ="100" >
<heartbeat > show slave status</heartbeat >
<writeHost host ="hostM1" url ="workstation:3306" user ="root"
password ="111111" >
<readHost host ="hostS" url ="workstation:3316" user ="root"
password ="111111" />
</writeHost >
</dataHost >
......
对比原文件如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
<dataHost name ="localhost1" maxCon ="1000" minCon ="10" balance ="0"
writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="1" slaveThreshold ="100" >
<heartbeat > select user()</heartbeat >
<writeHost host ="hostM1" url ="localhost:3306" user ="root"
password ="123456" >
</writeHost >
<writeHost host ="hostS1" url ="localhost:3316" user ="root"
password ="123456" />
</dataHost >
修改了以下几个地方,
删除了writehost hostS1;
增加了writehost hostM1下的一个readHost hostS;
修改了dataHost的几个参数, balance="1" writeType="0" switchType="2" slaveThreshold="100";
修改了检测心跳的方法为show slave status;
(1) 设置 balance="1"与writeType="0"
Balance参数设置:
balance=“0”, 所有读操作都发送到当前可用的writeHost上。
balance=“1”,所有读操作都随机的发送到readHost。
balance=“2”,所有读操作都随机的在writeHost、readhost上分发
WriteType参数设置:
writeType=“0”, 所有写操作都发送到可用的writeHost上。
writeType=“1”,所有写操作都随机的发送到readHost。
writeType=“2”,所有写操作都随机的在writeHost、readhost分上发。
“readHost是从属于writeHost的,即意味着它从那个writeHost获取同步数据,因此,当它所属的writeHost宕机了,则它也不会再参与到读写分离中来,即“不工作了”,这是因为此时,它的数据已经“不可靠”了。基于这个考虑,目前mycat 1.3和1.4版本中,若想支持MySQL一主一从的标准配置,并且在主节点宕机的情况下,从节点还能读取数据,则需要在Mycat里配置为两个writeHost并设置banlance=1。”
(2) 设置 switchType="2" 与slaveThreshold="100"
“Mycat心跳检查语句配置为 show slave status ,dataHost 上定义两个新属性: switchType="2" 与slaveThreshold="100",此时意味着开启MySQL主从复制状态绑定的读写分离与切换机制。Mycat心跳机制通过检测 show slave status 中的 "Seconds_Behind_Master", "Slave_IO_Running", "Slave_SQL_Running" 三个字段来确定当前主从同步的状态以及Seconds_Behind_Master主从复制时延。“
配置好之后,即可运行mycat start启动MyCAT。
测试MyCAT主从复制
在MyCAT上运行建表和插入语句,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
+-----------+---------------------------------------------------------------------+
+-----------+---------------------------------------------------------------------+
| dn1 | create table company(id int not null primary key,name varchar(100)) |
| dn2 | create table company(id int not null primary key,name varchar(100)) |
+-----------+---------------------------------------------------------------------+
3 rows in set (0.06 sec)
mysql> create table company(id int not null primary key,name varchar(100));
Query OK, 0 rows affected (1.82 sec)
+-----------+---------------------------------------------------+
+-----------+---------------------------------------------------+
| dn1 | insert into company(id, name) values (100, 'abc' ) |
| dn2 | insert into company(id, name) values (100, 'abc' ) |
+-----------+---------------------------------------------------+
3 rows in set (0.00 sec)
mysql> insert into company(id, name) values (100, 'abc' );
Query OK, 1 row affected (0.24 sec)
分别在主从数据库上查看,
可以看到,数据已经在主从Mysql上都存在。
如果想看更多的MyCAT处理细节,需要打开debug级别的log,修改log4j.xml,
1
2
3
4
5
<root>
<level value="debug" />
<appender-ref ref ="FILE" />
<!--<appender-ref ref ="FILE" />-->
</root>
修改后,重启mycat,然后运行sql,select * from company where id = 100;,在mycat/logs/mycat.log中可以看到以下结果,
可以发现mycat实际上是从hostS上读取数据,查看从Mysql的状态,如下,
上图最后一行显示,Seconds_Behind_Master是0,按MyCAT文档中的说法,此时的确是要去读hostS。
MyCAT自动切换
在主Mysql突然断线的时候,MyCAT会自动将连接转到从Mysql上,在从Mysql上读取数据。根据MyCAT文档中的说法,这种情况下不能设置一写一读两个主机,而要设置两个写主机。略微修改schema.xml如下,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
......
<dataHost name ="localhost1" maxCon ="1000" minCon ="10" balance ="1"
writeType ="0" dbType ="mysql" dbDriver ="native" switchType ="2" slaveThreshold ="100" >
<heartbeat > show slave status</heartbeat >
<writeHost host ="hostM1" url ="workstation:3306" user ="root"
password ="111111" >
</writeHost >
<writeHost host ="hostS" url ="workstation:3316" user ="root"
password ="111111" />
</dataHost >
......
几乎与之前的配置相同,只是将readHost改为writeHost,与原先的hostM1并列。
重启MyCAT,再次运行select * from company where id = 100;,发现log中,仍然是从hostS上查询,与之前相同;运行insert into company(id, name) values (200, 'xyz');,发现log中,也仍然是从hostM1上插入,与之前相同。插入时的log如下,
因此,可以判断,在该配置下,在Mysql没有宕机的时候,与之前一写一读的配置情况一样。此时,模拟Mysql宕机,再次运行查询select * from company where id = 100;,发现log如下,
与之前的查询完全相同,运行insert into company(id, name) values (300, 'def');,log如下,
此时的插入语句已经自动转到hostS上,说明此时的从Mysql已经取代主Mysql。此时,恢复主Mysql,发现所有的查询和插入也仍然是从hostS上走,相当于主Mysql没有在集群中。这种配置虽然简单,但系统的可靠性很差,建议使用多个主Mysql的集群,如Galera。
关于分库和分表
MyCAT主要完成了Mysql集群的水平分库问题。其中水平分库是说,把一个大表按行进行拆分,放在多个数据库中,比如,id为1~100万的在A库,100w~200w的在B库,以此类推。将大表进行水平拆分还有一种方法是水平分表,指的是,仍然按行进行拆分,但是放在一个数据库的多个表中,比如,id为1~100万的在A表,100w~200w的在B表,以此类推。表的水平拆分大致是这两种方法。
水平分表拆分的优点是,仍然是一个数据库,管理成本较低,集群方案等都与单库无差异,而且事务特性支持好,没有跨库的分布式事务问题;缺点是,由于本质上仍然是单个数据库,无法突破单个数据库实例上的CPU、内存、IO等硬件资源限制,性能提升并不会很明显。虽然,Oracle可以通过设置多个表空间来达到突破IO瓶颈的问题,Mysql也逐渐开始支持表空间特性,但是,CPU、内存仍然局限在一台服务器上,无法获得扩展。
水平分库拆分的优点是,引入了多个数据库,这些数据库可以放在多台服务器上,硬件资源扩展性理论上是无限的,性能提升会比较显著。缺点是,由于引入了多个数据库,管理成本提高,需要引入自动化工具,而且,事务特性会比较受限,因为多个数据库之间必然会存在分布式事务问题,这个问题一般都很难解决,也很难做到与单库上完全一致的事务特性,或者限制开发中分布式事务的使用,这对业务实现也不利。