转载请注明出处:http://blog.csdn.net/l1028386804/article/details/60325054
有段时间没有更新博客了,今天就为大家带来一篇关于Mycat数据分片的文章。今天,我们不讲理论,也不对Mycat的配置进行详细剖析,有兴趣的童鞋可以阅读Mycat官方的权威指南,其中,对Mycat的原理和配置进行了详细的描述,这里不再赘述。今天,我们就一起来看看如何利用Myca进行数据分片,从而达到分库分表的目的。
注:MySQL服务器是安装在CentOS6.5服务器上,Mycat Server是安装在本机的Windows系统上,安装在什么环境上无所谓,本人用的是虚拟机安装的CentOS系统,开启多个虚拟机,电脑实在是吃力,所以将Mycat Server装在了本机的Windows系统上。
IP | 端口 | 服务 | 用户名 | 密码 |
192.168.81.131 | 3306 | MySQL数据库 | root | root |
192.168.81.132 | 3306 | MySQL数据库 | root | root |
192.168.81.133 | 3306 | MySQL数据库 | root | root |
192.168.81.130 | 8066/9066 | Mycat Server | admin | admin123 |
现在假设系统的数据库为messagedb,里面只有2张表,一张表为消息表:message,一张表示消息来源的字典表:source,本案例实现的是按自然月分片的规则,因此上述3个mysql实例各自需要创建4个数据库,即
数据库实例 | 存储的数据库 |
192.168.81.131:3306 | message201701、message201702、message201703、message201704 |
192.168.81.132:3306 | message201705、message201706、message201707、message201708 |
192.168.81.133:3306 | message201709、message201710、message201711、message201712 |
说明:如果是刚接触Mycat的小伙伴对分片不太理解,简单地说,对于Mycat,一个分片表示某一个MySQL实例上的某一个数据库,即schema@host,于是当我们原先的一张大表需要分片的时候,mycat就会按照我们设定的规则,把这张大表中的数据分散到各个分片上,即所谓的分表分库,因此我们需要在每个对应的分片上创建相同名称的数据库,相同结构的表。
详情参见博文:《MySQL之——CentOS6.5 编译安装MySQL5.6.16》
根据数据库实例和存储的数据库对应关系表创建所有的数据库,并在每个数据库里执行如下脚本:
create table source (
id int(11) not null auto_increment primary key comment 'pk',
name varchar(10) default '' comment 'source name'
);
create table message (
id int(11) not null auto_increment primary key comment 'pk',
content varchar(255) default '' comment 'message content',
create_time date default null,
source_id int(11) not null,
foreign key(source_id) references source(id)
);
insert into `source`(`id`,`name`) values(1,'weibo');
insert into `source`(`id`,`name`) values(2,'weixin');
insert into `source`(`id`,`name`) values(3,'qq');
insert into `source`(`id`,`name`) values(4,'email');
insert into `source`(`id`,`name`) values(5,'sms');
在message表中,总共有4个字段:
安装Mycat的过程比较简单,在这个地址就可以下载安装包:https://github.com/MyCATApache/Mycat-download/tree/master/1.6-RELEASE。下载完之后,就进行解压到系统相应目录,这里就不细说了。
安装完之后,简单地看一下mycat目录结构:
WIndows下启动需要以管理员身份打开命令行窗口,cd 到Mycat的bin目录下,或者将Mycat的 安装目录加入系统的环境变量path目录里,首先输入命令mycat install进行mycat服务的安装操作,然后 输入命令mycat start 启动Mycat Server。
Linux下进入Mycat的bin目录直接输入./mycat start 启动Mycat Server。
Mycat提供了两个端口,其中,9066端口是管理端口,提供查看当前系统节点的情况,报告心跳状态等相关系统监控的功能,8066是数据端口,相当于数据库的访问端口。我们可以使用mysql命令访问这里两个端口
mysql -h[mycat_host] -u[mycat_user] -p[mycat_passwd] -P [8066|9066]
同时,我们也可以修改这两个端口。
那么mycat_user和mycat_passwd是如何配置呢,下面就需要介绍mycat中最主要的3个配置文件:server.xml,schema.xml和rule.xml。
该配置文件是用于配置mycat的系统信息,主要有两个标签:system和user。这里的user就是上述访问mycat服务的用户,不是后端数据库的用户。如果我们使用默认的配置,server.xml大概是这样的:
0
0
2
0
1
1m
1k
0
384m
admin123
messagedb
user标签下schemas属性表示该用户可以访问的数据库,可以定义多个数据库,用英文逗号隔开。schemas定义的数据库,一定要配置在后面的schema.xml文件对应的逻辑库,否则会提示无法访问。
schema配置文件比较复杂,也是最关键的一个配置文件,定义了mycat中的逻辑库、逻辑表,和分片的相关信息。配置如下:
select user()
select user()
select user()
几点要说明一下:
rule.xml中定义了很多分片的规则,具体规则的算法可以参考官方权威指南,这里我们直接使用默认的就可以了,其中按自然月的分片规则配置如下:
create_time
partbymonth
yyyy-MM-dd
2017-01-01
说明:起始日期是用来计算数据所在的分片位置,例如2017年1月的message就会找到第1个分片,即dn1,2017年12月的message就会找到第12个分片,即dn12,但是如果出现了2018年1月的message,mycat就会去找第13个分片,但是配置文件中又没有对应的配置,那么就会抛出无法找到分片的错误。
综上:server.xml定义了访问mycat服务的用户,以及该用户授权的数据库(逻辑库),schema.xml定义了具体的逻辑库,逻辑表,以及分片和数据库实例的信息,rule.xml分片规则和实现类。
到这里已经完成了mycat的配置文件,但先不急着往里面灌数据,我们先访问管理端口9066,看一下运行情况:
C:\Users\liuyazhuang>mysql -uadmin -padmin123 -P9066
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server (monitor)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show @@datanode;
+------+------------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
| NAME | DATHOST | INDEX | TYPE | ACTIVE | IDLE | SIZE | EXECUTE | TOTAL_TIME | MAX_TIME | MAX_SQL | RECOVERY_TIME |
+------+------------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
| dn1 | mysql-01/message201701 | 0 | mysql | 0 | 4 | 1000 | 412 | 0 | 0 | 0 | -1 |
| dn10 | mysql-03/message201710 | 0 | mysql | 0 | 0 | 1000 | 11 | 0 | 0 | 0 | -1 |
| dn11 | mysql-03/message201711 | 0 | mysql | 0 | 8 | 1000 | 16 | 0 | 0 | 0 | -1 |
| dn12 | mysql-03/message201712 | 0 | mysql | 0 | 1 | 1000 | 412 | 0 | 0 | 0 | -1 |
| dn2 | mysql-01/message201702 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn3 | mysql-01/message201703 | 0 | mysql | 0 | 4 | 1000 | 11 | 0 | 0 | 0 | -1 |
| dn4 | mysql-01/message201704 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn5 | mysql-02/message201705 | 0 | mysql | 0 | 4 | 1000 | 413 | 0 | 0 | 0 | -1 |
| dn6 | mysql-02/message201706 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn7 | mysql-02/message201707 | 0 | mysql | 0 | 4 | 1000 | 11 | 0 | 0 | 0 | -1 |
| dn8 | mysql-02/message201708 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn9 | mysql-03/message201709 | 0 | mysql | 0 | 0 | 1000 | 11 | 0 | 0 | 0 | -1 |
+------+------------------------+-------+-------+--------+------+------+---------+------------+----------+---------+---------------+
12 rows in set (0.00 sec)
mysql> show @@heartbeat;
+--------+-------+----------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| NAME | TYPE | HOST | PORT | RS_CODE | RETRY | STATUS | TIMEOUT | EXECUTE_TIME | LAST_ACTIVE_TIME | STOP |
+--------+-------+----------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
| hostM2 | mysql | 192.168.81.132 | 3306 | 1 | 0 | idle | 0 | 1,1,1 | 2017-03-04 14:22:59 | false |
| hostM1 | mysql | 192.168.81.131 | 3306 | 1 | 0 | idle | 0 | 1,1,1 | 2017-03-04 14:22:59 | false |
| hostM3 | mysql | 192.168.81.133 | 3306 | 1 | 0 | idle | 0 | 2,1,1 | 2017-03-04 14:22:59 | false |
+--------+-------+----------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
3 rows in set (0.00 sec)
mysql>
如果看到各个节点都已经出现,并且心跳状态RS_CODE=1,则表示后端数据库连接正常。
package com.mycat.test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Random;
import org.junit.Test;
/**
* 测试Mycat
* @author liuyazhuang
*
*/
public class TestMycat {
private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://127.0.0.1:8066/messagedb?useServerPrepStmts=false&rewriteBatchedStatements=true";
private static final String username = "admin";
private static final String password = "admin123";
@Test
public void test() throws SQLException {
Calendar calendar = Calendar.getInstance();
Random random = new Random();
calendar.set(2017, 0, 1, 0, 0, 0);
Connection connection = null;
PreparedStatement ps = null;
try {
Class.forName(driver);
connection = (Connection) DriverManager.getConnection(url, username, password);
connection.setAutoCommit(false);
String sql = "insert into message(`content`, `create_time`, `source_id`) values(?,?,?)";
ps = connection.prepareStatement(sql);
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
ps.setString(1, System.currentTimeMillis() + "");
long randomtime = calendar.getTimeInMillis() + (random.nextInt(365) + 1) * 86400 * 1000l;
Date date = new Date(randomtime);
int source_id = random.nextInt(5) + 1;
ps.setDate(2, date);
ps.setInt(3, source_id);
ps.addBatch();
if (i != 0 && i % 10000 == 0) {
System.out.println("execute batch : " + i);
ps.executeBatch();
}
}
ps.executeBatch();
connection.commit();
System.out.println(System.currentTimeMillis() - start);
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null)
ps.close();
if (connection != null)
connection.close();
}
}
}
如果运行的时候报错: Multi-statement transaction required more than ‘max_binlog_cache_size’ bytes of storage;,可以适当调大一下my.cnf下的max_binlog_cache_size参数。
最后我们来检验一下分片的结果,其中message表中的数据根据create_time的值按月进行了分片,而source表作为全局表,则其数据出现在了每个分片上,下面贴出部分结果
mysql -h192.168.81.131 -uroot -proot -P3306 -e "select min(create_time),max(create_time) from message201702.message;":
+------------------+------------------+
| min(create_time) | max(create_time) |
+------------------+------------------+
| 2017-02-01 | 2017-02-28 |
+------------------+------------------+
mysql -h192.168.81.132 -uroot -proot -P3306 -e "select min(create_time),max(create_time) from message201705.message;":
+------------------+------------------+
| min(create_time) | max(create_time) |
+------------------+------------------+
| 2017-05-01 | 2017-05-31 |
+------------------+------------------+
mysql -h192.168.81.133 -uroot -proot -P3306 -e "select min(create_time),max(create_time) from message201709.message;":
+------------------+------------------+
| min(create_time) | max(create_time) |
+------------------+------------------+
| 2017-09-01 | 2017-09-30 |
+------------------+------------------+
mysql -h192.168.81.131 -uroot -proot -P3306 -e "select * from message201701.source"
+----+--------+
| id | name |
+----+--------+
| 1 | weibo |
| 2 | weixin |
| 3 | qq |
| 4 | email |
| 5 | sms |
+----+--------+
mysql -h192.168.81.132 -uroot -proot -P3306 -e "select * from message201707.source"
+----+--------+
| id | name |
+----+--------+
| 1 | weibo |
| 2 | weixin |
| 3 | qq |
| 4 | email |
| 5 | sms |
+----+--------+
mysql -h192.168.81.133 -uroot -proot -P3306 -e "select * from message201711.source"
+----+--------+
| id | name |
+----+--------+
| 1 | weibo |
| 2 | weixin |
| 3 | qq |
| 4 | email |
| 5 | sms |
+----+--------+
本文就mycat分片的特性进行一次实战操作,完成了部署mycat-server以及后端mysql数据库,并以按自然月为分片规则进行了相关的配置,最后做了一个小的测试来验证分片功能的正确性。
mycat还有其他比较强大的特性还有待进一步的研究使用,下一步的工作:
完成读写分离的配置和测试
整合zookeeper实现高可用集群