共计 12993 个字符,预计需要花费 33 分钟才能阅读完成。
写在后面
Mycat 是基于阿里开源的 Cobar 产品而研发,Cobar 的稳定性、可靠性、优良的架构和性能以及泛滥成熟的应用案例使得 Mycat 一开始就领有一个很好的终点,站在伟人的肩膀上,咱们能看到更远。业界优良的开源我的项目和翻新思路被宽泛融入到 Mycat 的基因中,使得 Mycat 在很多方面都当先于目前其余一些同类的开源我的项目,甚至超过某些商业产品。——来自 Mycat 官网。
作为 Mycat 的外围开发者,怎能不来一波 Mycat 系列文章呢?
背景介绍
作为 Mycat 的外围开发者之一,明天,终于安顿到 Mycat 系列文章了。在 Mycat 系列文章中,咱们一起从一个利用 Mycat 实现分库分表的案例作为入门程序。后续会继续更新 Mycat 原理、架构和底层源码解析的文章。心愿 Mycat 系列文章可能帮忙小伙伴们彻底把握 Mycat。
那么,明天,咱们就先来一波应用 Mycat 实现 MySQL 分库分表的文章。
注:案例中的 MySQL 服务器是装置在 CentOS6.8 服务器上,Mycat Server 是装置在本机的 Windows 零碎上,装置在什么环境上无所谓,这里,我用的是 VMWare 虚拟机,装置的 CentOS 零碎,开启多个虚拟机,电脑切实是吃力,所以将 Mycat Server 装在了本机的 Windows 零碎上。
计划布局
如上表所示,在局域网的 4 台主机中,131——133 的主机各装置有一台 MySQL 实例,130 主机,也就是本机装置了 Mycat Server。
当初假如零碎的数据库为 messagedb,外面只有 2 张表,一张表为音讯表:message,一张示意消息来源的字典表:source,本案例实现的是按天然月分片的规定,因而上述 3 个 mysql 实例各自须要创立 4 个数据库,即
阐明:如果是刚接触 Mycat 的小伙伴对分片不太了解,简略地说,对于 Mycat,一个分片示意某一个 MySQL 实例上的某一个数据库,即 schema@host,于是当咱们原先的一张大表须要分片的时候,mycat 就会依照咱们设定的规定,把这张大表中的数据扩散到各个分片上,即所谓的分表分库,因而咱们须要在每个对应的分片上创立雷同名称的数据库,雷同构造的表。
环境筹备
留神:这里,我就省略了 MySQL 的装置过程,小伙伴们可自行装置 MySQL。我后续也会在 MySQL 相干的专题中给大家分享企业级 MySQL 装置、优化与部署过程。
创立数据库并建表导入数据
依据数据库实例和存储的数据库对应关系表创立所有的数据库,并在每个数据库里执行如下脚本:
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 个字段:
- id:主键
- content:音讯的内容
- create_time:创立工夫,这也是 mycat 进行分片时的参考字段
- source_id:source 表的外键
另外,咱们在 source 表插入了 5 条记录,用于测试。到这里,后端数据库的环境就搭建实现了。
装置和配置 Mycat
装置 Mycat
装置 Mycat 的过程比较简单,在这个地址就能够下载安装包:https://github.com/MyCATApache/Mycat-download/tree/master/1.6-RELEASE。下载完之后,就进行解压到零碎相应目录,这里就不细说了。
Mycat 安装包构造
装置完之后,简略地看一下 mycat 目录构造:
启动 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。
server.xml
该配置文件是用于配置 mycat 的零碎信息,次要有两个标签:system 和 user。这里的 user 就是上述拜访 mycat 服务的用户,不是后端数据库的用户。如果咱们应用默认的配置,server.xml 大略是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="useSqlStat">0</property>
<property name="useGlobleTableCheck">0</property>
<property name="sequnceHandlerType">2</property>
<property name="processorBufferPoolType">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>
</system>
<user name="admin">
<property name="password">admin123</property>
<property name="schemas">messagedb</property>
</user>
</mycat:server>
user 标签下 schemas 属性示意该用户能够拜访的数据库,能够定义多个数据库,用英文逗号隔开。schemas 定义的数据库,肯定要配置在前面的 schema.xml 文件对应的逻辑库,否则会提醒无法访问。
schema.xml
schema 配置文件比较复杂,也是最要害的一个配置文件,定义了 mycat 中的逻辑库、逻辑表,和分片的相干信息。配置如下:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="messagedb" checkSQLschema="false" sqlMaxLimit="100">
<table name="message" dataNode="dn1,dn2,dn3,dn4,dn5,dn6,dn7,dn8,dn9,dn10,dn11,dn12" rule="sharding-by-month" />
<table name="source" primaryKey="id" type="global" dataNode="dn1,dn2,dn3,dn4,dn5,dn6,dn7,dn8,dn9,dn10,dn11,dn12" />
</schema>
<dataNode name="dn1" dataHost="mysql-01" database="message202001" />
<dataNode name="dn2" dataHost="mysql-01" database="message202002" />
<dataNode name="dn3" dataHost="mysql-01" database="message202003" />
<dataNode name="dn4" dataHost="mysql-01" database="message202004" />
<dataNode name="dn5" dataHost="mysql-02" database="message202005" />
<dataNode name="dn6" dataHost="mysql-02" database="message202006" />
<dataNode name="dn7" dataHost="mysql-02" database="message202007" />
<dataNode name="dn8" dataHost="mysql-02" database="message202008" />
<dataNode name="dn9" dataHost="mysql-03" database="message202009" />
<dataNode name="dn10" dataHost="mysql-03" database="message202010" />
<dataNode name="dn11" dataHost="mysql-03" database="message202011" />
<dataNode name="dn12" dataHost="mysql-03" database="message202012" />
<dataHost name="mysql-01" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="-1">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.81.131:3306" user="root"
password="root">
</writeHost>
</dataHost>
<dataHost name="mysql-02" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="-1">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM2" url="192.168.81.132:3306" user="root"
password="root">
</writeHost>
</dataHost>
<dataHost name="mysql-03" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="-1">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM3" url="192.168.81.133:3306" user="root"
password="root">
</writeHost>
</dataHost>
</mycat:schema>
这里,有几点要阐明一下:
- schema 标签定义逻辑库,其下 table 子标签定义逻辑表,datanode 属性定义该逻辑表须要散布到哪几个分片上,rule 属性示意应用何种分片规定,这里咱们抉择 sharding-by-month,这个规定的名称是自定义的,只有和前面的 rule.xml 对应起来即可
- source 表是一张全局表,这里须要应用 type=”global”来定义,这样 mycat 就能够帮咱们在指定的分片上克隆雷同的数据,这对 join 查问是十分有益处的。
- datanode 标签定义了分片,datahost 是主机名,对应 dataHost 标签的 name 属性值,database 定义该主机数据库实例上的具体数据库名。
- dataHost 标签定义数据库实例,其下 heartbeart 标签示意心跳检测所应用的办法,writeHost 标签定义写数据的实例,另外还有 readHost 标签能够定义读数据的实例,这里不思考读写拆散,仅应用写实例,因而须要把 balance 属性设置为 0
- 出于标准和平安思考,最好不应用数据库的 root 用户,而是另外再创立一个用于 mycat 拜访的用户。
rule.xml
rule.xml 中定义了很多分片的规定,具体规定的算法能够参考官网权威指南,这里咱们间接应用默认的就能够了,其中按天然月的分片规定配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="sharding-by-month">
<rule>
<columns>create_time</columns>
<algorithm>partbymonth</algorithm>
</rule>
</tableRule>
<function name="partbymonth"
class="io.mycat.route.function.PartitionByMonth">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2020-01-01</property>
</function>
</mycat:rule>
- tableRule 标签定义分片规定的,其下 columns 标签示意对数据库表中的哪个字段利用规定,algorithm 指定实现算法的名称,对应的是 function 标签中的 name 属性值
- function 标签定义对应的实现类,以及参数,包含 dateFormat(日期格局)和 sBeginDate(起始日期)
阐明:起始日期是用来计算数据所在的分片地位,例如 2020 年 1 月的 message 就会找到第 1 个分片,即 dn1,2020 年 12 月的 message 就会找到第 12 个分片,即 dn12,然而如果呈现了 2018 年 1 月的 message,mycat 就会去找第 13 个分片,然而配置文件中又没有对应的配置,那么就会抛出无奈找到分片的谬误。
综上:server.xml 定义了拜访 mycat 服务的用户,以及该用户受权的数据库(逻辑库),schema.xml 定义了具体的逻辑库,逻辑表,以及分片和数据库实例的信息,rule.xml 分片规定和实现类
测试
到这里曾经实现了 mycat 的配置文件,但先不急着往里面灌数据,咱们先拜访治理端口 9066,看一下运行状况:
C:\Users\binghe>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, 2019, 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/message202001 | 0 | mysql | 0 | 4 | 1000 | 412 | 0 | 0 | 0 | -1 |
| dn10 | mysql-03/message202010 | 0 | mysql | 0 | 0 | 1000 | 11 | 0 | 0 | 0 | -1 |
| dn11 | mysql-03/message202011 | 0 | mysql | 0 | 8 | 1000 | 16 | 0 | 0 | 0 | -1 |
| dn12 | mysql-03/message202012 | 0 | mysql | 0 | 1 | 1000 | 412 | 0 | 0 | 0 | -1 |
| dn2 | mysql-01/message202002 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn3 | mysql-01/message202003 | 0 | mysql | 0 | 4 | 1000 | 11 | 0 | 0 | 0 | -1 |
| dn4 | mysql-01/message202004 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn5 | mysql-02/message202005 | 0 | mysql | 0 | 4 | 1000 | 413 | 0 | 0 | 0 | -1 |
| dn6 | mysql-02/message202006 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn7 | mysql-02/message202007 | 0 | mysql | 0 | 4 | 1000 | 11 | 0 | 0 | 0 | -1 |
| dn8 | mysql-02/message202008 | 0 | mysql | 0 | 2 | 1000 | 9 | 0 | 0 | 0 | -1 |
| dn9 | mysql-03/message202009 | 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 | 2020-03-04 14:22:59 | false |
| hostM1 | mysql | 192.168.81.131 | 3306 | 1 | 0 | idle | 0 | 1,1,1 | 2020-03-04 14:22:59 | false |
| hostM3 | mysql | 192.168.81.133 | 3306 | 1 | 0 | idle | 0 | 2,1,1 | 2020-03-04 14:22:59 | false |
+--------+-------+----------------+------+---------+-------+--------+---------+--------------+---------------------+-------+
3 rows in set (0.00 sec)
mysql>
如果看到各个节点都曾经呈现,并且心跳状态 RS_CODE=1,则示意后端数据库连贯失常。
当初咱们用 JDBC 的形式批量插入 1000 万数据:
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 binghe
*/
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(2020, 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 message202002.message;":
+------------------+------------------+
| min(create_time) | max(create_time) |
+------------------+------------------+
| 2020-02-01 | 2020-02-28 |
+------------------+------------------+
mysql -h192.168.81.132 -uroot -proot -P3306 -e "select min(create_time),max(create_time) from message202005.message;":
+------------------+------------------+
| min(create_time) | max(create_time) |
+------------------+------------------+
| 2020-05-01 | 2020-05-31 |
+------------------+------------------+
mysql -h192.168.81.133 -uroot -proot -P3306 -e "select min(create_time),max(create_time) from message202009.message;":
+------------------+------------------+
| min(create_time) | max(create_time) |
+------------------+------------------+
| 2020-09-01 | 2020-09-30 |
+------------------+------------------+
mysql -h192.168.81.131 -uroot -proot -P3306 -e "select * from message202001.source"
+----+--------+
| id | name |
+----+--------+
| 1 | weibo |
| 2 | weixin |
| 3 | qq |
| 4 | email |
| 5 | sms |
+----+--------+
mysql -h192.168.81.132 -uroot -proot -P3306 -e "select * from message202007.source"
+----+--------+
| id | name |
+----+--------+
| 1 | weibo |
| 2 | weixin |
| 3 | qq |
| 4 | email |
| 5 | sms |
+----+--------+
mysql -h192.168.81.133 -uroot -proot -P3306 -e "select * from message202011.source"
+----+--------+
| id | name |
+----+--------+
| 1 | weibo |
| 2 | weixin |
| 3 | qq |
| 4 | email |
| 5 | sms |
+----+--------+
总结
本文就 mycat 分片的个性进行一次实战操作,实现了部署 mycat-server 以及后端 mysql 数据库,并以按天然月为分片规定进行了相干的配置,最初做了一个小的测试来验证分片性能的正确性。
好了,明天 Mycat 系列文章的入门案例就到这儿吧,大家有啥好的意见或倡议都能够在文末留言,我是冰河,咱们下期见!!
重磅福利
微信搜一搜【冰河技术】微信公众号,关注这个有深度的程序员,每天浏览超硬核技术干货,公众号内回复【PDF】有我筹备的一线大厂面试材料和我原创的超硬核 PDF 技术文档,以及我为大家精心筹备的多套简历模板(不断更新中),心愿大家都能找到心仪的工作,学习是一条时而郁郁寡欢,时而开怀大笑的路,加油。如果你通过致力胜利进入到了心仪的公司,肯定不要懈怠放松,职场成长和新技术学习一样,逆水行舟。如果有幸咱们江湖再见!
另外,我开源的各个 PDF,后续我都会继续更新和保护,感激大家长期以来对冰河的反对!!
写在最初
如果你感觉冰河写的还不错,请微信搜寻并关注「冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者曾经通过浏览「 冰河技术 」微信公众号文章,吊打面试官,胜利跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样晋升本人的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术」微信公众号吧,每天更新超硬核技术干货,让你对如何晋升技术能力不再迷茫!