Mycat 是数据库中间件,就是介于数据库与应用之间,进行数据处理与交互的中间服务。 由于前面讲的对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构 成了整个完整的数据库存储。
如上图所表示,数据被分到多个分片数据库后,应用如果需要读取数据,就要需要处理多个数据源的数据。 如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换、事务处理、数据聚合都需要应用直接处理, 原本该是专注于业务的应用,将会花大量的工作来处理分片后的问题,最重要的是每个应用处理将是完全的重复造轮子。
Mycat作为一个中间件,应用程序直接访问它,不用再去管真实的数据库,而由Mycat来与真实的数据库进行交互,真实的数据库可能有多个
mycat 是使用 JAVA 语言进行编写开发,使用前需要先安装 JAVA 运行环境(JRE),由于 mycat 中使用了 JDK7 中的一些特性,所以要求必须在 JDK7 以上的版本上运行。
MyCAT 有提供编译好的安装包,支持 windows、Linux、Mac、Solaris 等系统上安装与运行。
linux 下可以下载 Mycat-server-xxxxx.linux.tar.gz 解压在某个目录下,注意目录不能有空格,在 Linux(Unix)下,建议放在 usr/local/Mycat 目录下,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N8mpWGLW-1581683528869)(…\mycat操作文档\tu\1.png)]
目录解释如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vtTllFvl-1581683528870)(…\mycat操作文档\tu\2.png)]
bin 程序目录,存放了 window 版本和 linux 版本,除了提供封装成服务的版本之外,也提供了 nowrap 的 shell 脚本命令,方便大家选择和修改,进入到 bin 目录:
Linux 下运行:./mycat console,首先要 chmod +x *
注:mycat 支持的命令{ console | start | stop | restart | status | dump }
conf 目录下存放配置文件,server.xml 是 Mycat 服务器参数调整和用户授权的配置文件,schema.xml 是逻 辑库定义和表以及分片定义的配置文件,rule.xml 是分片规则的配置文件,分片规则的具体一些参数信息单独存 放为文件,也在这个目录下,配置文件修改,需要重启 Mycat 或者通过 9066 端口 reload.
lib 目录下主要存放 mycat 依赖的一些 jar 文件.
日志存放在 logs/mycat.log 中,每天一个文件,日志的配置是在 conf/log4j.xml 中,根据自己的需要,可 以调整输出级别为 debug,debug 级别下,会输出更多的信息,方便排查问题
注意:Linux 下部署安装 MySQL,默认不忽略表名大小写,需要手动到/etc/my.cnf 下配置 lower_case_table_names=1 使 Linux 环境下 MySQL 忽略表名大小写,否则使用 MyCAT 的时候会提示找不到 表的错误!
windows 下可以下载 Mycat-server-xxxxx-win.tar.gz 解压在某个目录下,建议解压到本地某个盘符根目录 下,如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Ugsm8rO-1581683528871)(…\mycat操作文档\tu\3.png)]
目录解释如下:
bin 程序目录,存放了 window 版本和 linux 版本,除了提供封装成服务的版本之外,也提供了 nowrap 的 shell 脚本命令,方便大家选择和修改,进入到 bin 目录:
Windows 下运行:运行: mycat.bat 在控制台启动程序,也可以装载成服务,若此程序运行有问题,也可以 运行 startup_nowrap.bat,确保 java 命令可以在命令执行。
Windows 下将 MyCAT 做成系统服务:MyCAT 提供 warp 方式的命令,可以将 MyCAT 安装成系统服务并 可启动和停止。
conf 目录下存放配置文件,server.xml 是 Mycat 服务器参数调整和用户授权的配置文件,schema.xml 是逻 辑库定义和表以及分片定义的配置文件,rule.xml 是分片规则的配置文件,分片规则的具体一些参数信息单独存 放为文件,也在这个目录下,配置文件修改,需要重启 Mycat 或者通过 9066 端口 reload。
lib 目录下主要存放 mycat 依赖的一些 jar 文件。
日志存放在 logs/mycat.log 中,每天一个文件,日志的配置是在 conf/log4j.xml 中,根据自己的需要,可 以调整输出级别为 debug,debug 级别下,会输出更多的信息,方便排查问题。
本案例使用Windows
以管理员身份运行cmd,进入mycat的bin目录,这里有几个简单的操作命令
安装:
mycat install
启动:
mycat start
停止:
mycat stop
重启:
mycat restart
安装以后,我们就可以直接去服务里面启动与停止了,不用再使用命令[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8uEYxMxN-1581683528872)(…\mycat操作文档\tu\5.png)]
需求:我在云服务器与本机都安装有Myql,现在我要使用这两个Mysql共同来给我的应用程序提供服务,主要涉及到的是MAVEN_SSM数据下面的user表,下面分别给出sql:
本机Mysql执行的sql为:
drop database if exists MAVEN_SSM;
create database MAVEN_SSM;
use MAVEN_SSM;
create table user(
id int not null auto_increment,
username char(20) not null,
password char(33) not null,
address char(8) not null,
birthday date,
department_id int not null,
primary key (id)
);
insert into user(id,username,password,address,birthday,department_id) values(1,'小余','123456','四川成都','2018-06-01',2);
insert into user(id,username,password,address,birthday,department_id) values(2,'小余','982352','成都大学','2015-04-03',3);
云服务器上执行的sql为:
drop database if exists MAVEN_SSM;
create database MAVEN_SSM;
use MAVEN_SSM;
create table user(
id int not null auto_increment,
username char(20) not null,
password char(33) not null,
address char(8) not null,
birthday date,
department_id int not null,
primary key (id)
);
insert into user(id,username,password,address,birthday,department_id) values(3,'小王','982352','四川绵阳','1996-08-26',2);
Mycat的默认端口是:8066,对于应用程序来说,数据库名为Mycat的中间件逻辑数据库名,不再是某个真实的数据库名
Mycat的配置文件,在conf目录下面有:server.xml、schema.xml、rule.xml,以及ehcache.xml、log4j2.xml,我们主要使用前三个
首先配置:server.xml,将默认的该配置文件下的user全都删掉,新建了一个用户peng:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aa8srFzC-1581683528874)(…\mycat操作文档\tu\6.png)]
用户名:peng
其密码为:123456
其逻辑数据库名为:MYCAT_DB
是否为只读数据库:否
由于Mycat安装在本机,需要更换连接数据库的url为本机的Mycat中间件
将
jdbc:mysql://localhost:3306/MAVEN_SSM1
改为
jdbc:mysql://localhost:8806/MYCAT_DB1
其次配置schema.xml,配置数据库的表结构,配置分片
select user()
select user()
这里的47.55.478.991:3306是云服务器mysql
最后配置rule.xml,我们在schema.xml中使用了mod-long规则,由于是两个节点来提供服务,这里我就将其设置为均分:比如插入时,一个一库,轮流进行[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0e5Vin9u-1581683528875)(…\mycat操作文档\tu\7.png)]
将count设置为2
更多的配置规则与详情,请查看Mycat的官方文档,官网可以找到
测试:为了方便这里使用的是Navicat
首先查看本机Mysql:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rsHGCzpu-1581683528875)(…\mycat操作文档\tu\8.png)]
查看云服务器Mysql:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jkL1tUlU-1581683528876)(…\mycat操作文档\tu\9.png)]
连接Mycat中间件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-buT7XWXm-1581683528877)(…\mycat操作文档\tu\10.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-alNZs6gZ-1581683528878)(…\mycat操作文档\tu\11.png)]
查询操作:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NKbN3eNq-1581683528879)(…\mycat操作文档\tu\12.png)]
插入操作1:
insert into user(id,username,password,address,birthday,department_id) values(4,'帅哥1','123456','四川成都','2018-06-01',2);
插入操作2:
insert into user(id,username,password,address,birthday,department_id) values(5,'帅哥2','123456','四川成都','2018-06-01',2);
这时候本机与云服务器的数据库,各插了一条,均匀分配。再次进入Mysql进行验证:
本机Mysql:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B1bGM70d-1581683528880)(…\mycat操作文档\tu\13.png)]
云服务器Mysql:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MVTL3IRC-1581683528880)(…\mycat操作文档\tu\14.png)]
再次查询:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HY1MpzeQ-1581683528881)(…\mycat操作文档\tu\15.png)]
可以看到这里顺序是有问题的,我们需要使用order by
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2rAzRUL1-1581683528882)(…\mycat操作文档\tu\16.png)]
Schema.xml 作为 MyCat 中重要的配置文件之一,管理着 MyCat 的逻辑库、表、分片规则、DataNode 以 及 DataSource。
server.xml 几乎保存了所有 mycat 需要的系统配置信息。其在代码内直接的映射类为 SystemConfig 类。
rule.xml 里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法, 或者对表使用相同的算法但具体的参数不同。这个文件里面主要有 tableRule 和 function 这两个标签。在具体使用过程中可以按照需求添加 tableRule 和 function
Mycat 目前仅支持 跨库2 个表 Join,
性能建议
尽量避免使用 Left join 或 Right join,而用 Inner join
在使用 Left join 或 Right join 时,ON 会优先执行,where 条件在最后执行,所以在使用过程中,条件尽可能的在 ON 语句中判断,减少 where 的执行
少用子查询,而用 join。
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。MyCat 提供了全局序列号,并且提供了包含本地配置和数据库配置等多种实现方式,这里介绍本地配置实现方式:
配置方式:
在 sequence_conf.properties 文件中做如下配置:
GLOBAL_SEQ.HISIDS=
GLOBAL_SEQ.MINID=1001
GLOBAL_SEQ.MAXID=1000000000
GLOBAL_SEQ.CURID=1000
其中 HISIDS 表示使用过的历史分段(一般无特殊需要可不配置),MINID 表示最小 ID 值,MAXID 表示最大 ID 值,CURID 表示当前 ID 值。
server.xml 中配置:
0
注:sequnceHandlerType 需要配置为 0,表示使用本地文件方式。
使用示例:
insert into table1(id,name) values( 1703345641876492288,‘test’);
缺点:当 MyCAT 重新发布后,配置文件中的 sequence 会恢复到初始值。
优点:本地加载,读取速度较快。
按照官方文档修改配置文件,如: 取模分片
此规则为对分片字段求摸运算。
user_id
mod-long
3
配置说明: 上面 columns 标识将要分片的表字段,algorithm 分片函数, 此种配置非常明确即根据 id 进行十进制求模预算
官方文档中展示了十几种分片规则,这里不再一一举例
单节点 mycat 的部署指的是只部署一台 mycat 服务器,如果这台 mycat 服 务器宕机了,mycat 就不可用了。
生产环境下建议部署mycat高可用集群
下图是一个典型的 Mycat 系统高可用的方案:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqnWOT3V-1581683528883)(tu/18.png)]
不支持 Insert into 中不包括字段名的 SQL。
insert into x select from y 的 SQL,若 x 与 y 不是相同的拆分规则,则不被支持。
Mycat中的路由结果是通过分片字段和分片方法来确定的。例如下图中的一个Mycat分库方案:
根据 tt_waybill 表的 id 字段来进行分片
分片方法为 id 值取 3 的模,根据模值确定在DB1,DB2,DB3中的某个分片
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWzYQKU6-1581683528884)(tu/19.png)]
如果查询条件中有 id 字段的情况还好,查询将会落到某个具体的分片。例如:
mysql>select * fromtt_waybill where **id** =12330;
此时Mycat会计算路由结果
12330 % 3 = 0 –> DB1
并将该请求路由到DB1上去执行。
如果查询条件中没有 分片字段 条件,例如:
mysql>select * fromtt_waybill where waybill_no =88661;
此时Mycat无法计算路由,便发送到所有节点上执行:
DB1 –> select * fromtt_waybill where waybill_no =88661;
DB2 –> select * from tt_waybill where waybill_no =88661;
DB3 –> select * from tt_waybill where waybill_no =88661;
先看一下Mycat是如何处理分页操作的,假如有如下Mycat分库方案:
一张表有30份数据分布在3个分片DB上,具体数据分布如下
DB1:[0,1,2,3,4,10,11,12,13,14]
DB2:[5,6,7,8,9,16,17,18,19]
DB3:[20,21,22,23,24,25,26,27,28,29]
当应用执行如下分页查询时
mysql>select * fromtable limit 2;
Mycat将该SQL请求分发到各个DB节点去执行,并接收各个DB节点的返回结果
DB1: [0,1]
DB2: [5,6]
DB3: [20,21]
但Mycat向应用返回的结果集取决于哪个DB节点最先返回结果给Mycat。
如果Mycat最先收到DB1节点的结果集,那么Mycat返回给应用端的结果集为 [0,1],如果Mycat最先收到DB2节点的结果集,那么返回给应用端的结果集为 [5,6]。
也就是说,相同情况下,同一个SQL,在Mycat上执行时可能会有不同的返回结果。
在Mycat中执行分页操作时必须加上排序条件才能保证结果的正确性,下面看一下Mycat对排序分页的处理逻辑。
mysql>select * fromtable orderby id limit 2;
各个DB节点的返回结果:
DB1: [0,1]
DB2: [5,6]
DB3: [20,21]
Mycat接收到各个DB节点的返回结果后,对其进行最小堆运算,计算出所有结果集中最小的两条记录 [0,1] 返回给应用。
但是,当排序分页中有 偏移量 (offset)时,处理逻辑又有不同。假如应用的查询SQL如下:
mysql>select * fromtable order by id limit 5,2;
各个DB节点返回的数据:
DB1:[10,11]
DB2:[16,17]
DB3:[20,21]
计算后返回给应用的结果集是 [10,11]
可是,对于应用而言,该表的所有数据明明是 0-29 这30个数据的集合,limit 5,2 操作返回的结果集应该是 [5,6],如果返回 [10,11] 则是错误的处理逻辑。
所以Mycat在处理 有偏移量的排序分页 时是另外一套逻辑——它会自动改写SQL再计算
最后mycat能返回正确的结果,但这个过程极其消耗内存和CPU资源。当偏移量更大时,资源消耗则是数十倍增加。
一句话概括:使用mycat分页排序极其消耗资源,效率不高
先看一下在单库中JOIN的场景。假设在某库中有 player 和 team 两张表,player 表中的 team_id 字段与 team 表中的id 字段相关联。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bflHdYx7-1581683528885)(tu/20.png)]
JOIN操作的SQL如下
mysql>selectp_name,t_name from player p, team t where p.no = 3 and p.team_id = t.id;
此时能查询出结果
如果将这两个表的数据分库后,相关联的数据可能分布在不同的DB节点上,如下图:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jftzFTHb-1581683528886)(tu/22.png)]
这个SQL在各个单独的分片DB中都查不出结果,也就是说Mycat不能查询出正确的结果集。
mycat官方给出了四种解决方法,但跨库join目前还没有完美的解决方案。
share join是mycat提供的一种跨库join方式
目前支持 2 个表的 join,原理就是解析 SQL 语句,拆分成单表的 SQL 语句执行,然后把各个节点的数据汇集。
具体操作需要修改配置文件,以下是普通join和share join的区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrC7oNLy-1581683528887)(tu/23.png)]
lHdYx7-1581683528885)]
JOIN操作的SQL如下
mysql>selectp_name,t_name from player p, team t where p.no = 3 and p.team_id = t.id;
此时能查询出结果
如果将这两个表的数据分库后,相关联的数据可能分布在不同的DB节点上,如下图:[外链图片转存中…(img-jftzFTHb-1581683528886)]
这个SQL在各个单独的分片DB中都查不出结果,也就是说Mycat不能查询出正确的结果集。
mycat官方给出了四种解决方法,但跨库join目前还没有完美的解决方案。
share join是mycat提供的一种跨库join方式
目前支持 2 个表的 join,原理就是解析 SQL 语句,拆分成单表的 SQL 语句执行,然后把各个节点的数据汇集。
具体操作需要修改配置文件,以下是普通join和share join的区别
[外链图片转存中…(img-XrC7oNLy-1581683528887)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-92VFIncn-1581683528888)(tu/24.png)]