【转】mycat基础知识和运用总结
转载自:https://cloud.tencent.com/developer/article/1172518
系统开发中,数据库是非常重要的一个点。除了程序的本身的优化,如:SQL语句优化、代码优化,数据库的处理本身优化也是非常重要的。主从、热备、分表分库等都是系统发展迟早会遇到的技术问题问题。Mycat是一个广受好评的数据库中间件,已经在很多产品上进行使用了。下面就针对Mycat的基础知识和应用做一总结性梳理,这些内容有的是从网上收集的,有的是自己做的测试验证信息,如有错误,烦请谅解和指出!
一、MyCat简单介绍 MyCat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。
MyCat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在MyCat里,都是一个传统的数据库表,支持标准的SQL语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。
Mycat可以简单概括为 - 一个彻底开源的,面向企业应用开发的大数据库集群 - 支持事务、ACID、可以替代MySQL的加强版数据库 - 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群 - 一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server - 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品 - 一个新颖的数据库中间件产品
Mycat关键特性 - 支持SQL92标准 - 遵守Mysql原生协议,跨语言,跨平台,跨数据库的通用中间件代理 - 基于心跳的自动故障切换,支持读写分离,支持MySQL主从,以及galera cluster集群 - 支持Galera for MySQL集群,Percona Cluster或者MariaDB cluster - 基于Nio实现,有效管理线程,高并发问题 - 支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数,支持跨库分页 - 支持单库内部任意join,支持跨库2表join,甚至基于caltlet的多表join - 支持通过全局表,ER关系的分片策略,实现了高效的多表join查询 - 支持多租户方案 - 支持分布式事务(弱xa) - 支持全局序列号,解决分布式下的主键生成问题 - 分片规则丰富,插件化开发,易于扩展 - 强大的web,命令行监控 - 支持前端作为mysq通用代理,后端JDBC方式支持Oracle、DB2、SQL Server 、 mongodb 、巨杉 - 支持密码加密 - 支持服务降级 - 支持IP白名单 - 支持SQL黑名单、sql注入攻击拦截 - 支持分表(1.6) - 集群基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(2.0开发版)
二、为什么要用MyCat 曾经面试时被问到:Mycat和MySQL的区别(Mycat的核心作用)。我们可以把上层看作是对下层的抽象,例如操作系统是对各类计算机硬件的抽象。那么我们什么时候需要抽象?假如只有一种硬件的时候,我们需要开发一个操作系统吗?再比如一个项目只需要一个人完成的时候不需要leader,但是当需要几十人完成时,就应该有一个管理者,发挥沟通协调等作用,而这个管理者对于他的上层来说就是对项目组的抽象。
同样的,当我们的应用只需要一台数据库服务器的时候我们并不需要Mycat,而如果你需要分库甚至分表,这时候应用要面对很多个数据库的时候,这个时候就需要对数据库层做一个抽象,来管理这些数据库,而最上面的应用只需要面对一个数据库层的抽象或者说数据库中间件就好了,这就是Mycat的核心作用。所以可以这样理解:数据库是对底层存储文件的抽象,而Mycat是对数据库的抽象。
三、Mycat工作原理 Mycat的原理并不复杂,复杂的是代码。Mycat的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分 片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
![](https://ask.qcloudimg.com/http-save/yehe-1207996/q3teb3nn77.png?imageView2/2/w/1620)
上述图片里,Orders表被分为三个分片datanode(简称dn),这三个分片是分布在两台MySQL Server上(DataHost),即datanode=database@datahost方式,因此你可以用一台到N台服务器来分片,分片规则为(sharding rule)典型的字符串枚举分片规则,一个规则的定义是分片字段(sharding column)+分片函数(rule function),这里的分片字段为prov而分片函数为字符串枚举方式。
当Mycat收到一个SQL时,会先解析这个SQL,查找涉及到的表,然后看此表的定义,如果有分片规则,则获取到SQL里分片字段的值,并匹配分片函数,得到该SQL对应的分片列表,然后将SQL发往这些分片去执行,最后收集和处理所有分片返回的结果数据,并输出到客户端。以select * from Orders where prov=?语句为例,查到prov=wuhan,按照分片函数,wuhan返回dn1,于是SQL就发给了MySQL1,去取DB1上的查询结果,并返回给用户。
如果上述SQL改为select * from Orders where prov in (‘wuhan’,‘beijing’),那么,SQL就会发给MySQL1与MySQL2去执行,然后结果集合并后输出给用户。但通常业务中我们的SQL会有Order By 以及Limit翻页语法,此时就涉及到结果集在Mycat端的二次处理,这部分的代码也比较复杂,而最复杂的则属两个表的Jion问题,为此,Mycat提出了创新性的ER分片、全局表、HBT(Human Brain Tech)人工智能的Catlet、以及结合Storm/Spark引擎等十八般武艺的解决办法,从而成为目前业界最强大的方案,这就是开源的力量!
四、Mycat应用场景 Mycat发展到现在,适用的场景已经很丰富,而且不断有新用户给出新的创新性的方案,以下是几个典型的应用场景: - 单纯的读写分离,此时配置最为简单,支持读写分离,主从切换; - 分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片; - 多租户应用,每个应用一个库,但应用程序只连接Mycat,从而不改造程序本身,实现多租户化; - 报表系统,借助于Mycat的分表能力,处理大规模报表的统计; - 替代Hbase,分析大数据; - 作为海量数据实时查询的一种简单有效方案,比如100亿条频繁查询的记录需要在3秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时Mycat可能是最简单有效的选择; - Mycat长期路线图; - 强化分布式数据库中间件的方面的功能,使之具备丰富的插件、强大的数据库智能优化功能、全面的系统监控能力、以及方便的数据运维工具,实现在线数据扩容、迁移等高级功能; - 进一步挺进大数据计算领域,深度结合Spark Stream和Storm等分布式实时流引擎,能够完成快速的巨表关联、排序、分组聚合等 OLAP方向的能力,并集成一些热门常用的实时分析算法,让工程师以及DBA们更容易用Mycat实现一些高级数据分析处理功能。 - 不断强化Mycat开源社区的技术水平,吸引更多的IT技术专家,使得Mycat社区成为中国的Apache,并将Mycat推到Apache 基金会,成为国内顶尖开源项目,最终能够让一部分志愿者成为专职的Mycat开发者,荣耀跟实力一起提升。
五、Mycat不适合的应用场景 1)非分片字段查询 Mycat中的路由结果是通过分片字段和分片方法来确定的。例如下图中的一个Mycat分库方案: - 根据 tt_waybill 表的 id 字段来进行分片 - 分片方法为 id 值取 3 的模,根据模值确定在DB1,DB2,DB3中的某个分片
![](https://ask.qcloudimg.com/http-save/yehe-1207996/uh0s518voe.png?imageView2/2/w/1620)
如果查询条件中有 id 字段的情况还好,查询将会落到某个具体的分片。例如:
mysql>select * fromtt_waybill where id =12330;
此时Mycat会计算路由结果
12330 % 3 = 0 –> DB1
并将该请求路由到DB1上去执行。
![](https://ask.qcloudimg.com/http-save/yehe-1207996/6b9tp7bb99.png?imageView2/2/w/1620)
如果查询条件中没有 分片字段 条件,例如:
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;
如果该分片字段选择度高,也是业务常用的查询维度,一般只有一个或极少数个DB节点命中(返回结果集)。示例中只有3个DB节点,而实际应用中的DB节点数远超过这个,假如有50个,那么前端的一个查询,落到MySQL数据库上则变成50个查询,会极大消耗Mycat和MySQL数据库资源。
如果设计使用Mycat时有非分片字段查询,请慎重使用Mycat,可以考虑放弃!
2)分页排序 先看一下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对排序分页的处理逻辑。 假如在前面的分页查询中加上了排序条件(假如表数据的列名为id)
mysql>select * fromtable orderby id limit 2;
Mycat的处理逻辑如下图:
![](https://ask.qcloudimg.com/http-save/yehe-1207996/quu59n6abn.png?imageView2/2/w/1620)
在有排序呢条件的情况下,Mycat接收到各个DB节点的返回结果后,对其进行最小堆运算,计算出所有结果集中最小的两条记录 [0,1] 返回给应用。
但是,当排序分页中有 偏移量 (offset)时,处理逻辑又有不同。假如应用的查询SQL如下:
mysql>select * fromtable order by id limit 5,2;
如果按照上述排序分页逻辑来处理,那么处理结果如下图:
![](https://ask.qcloudimg.com/http-save/yehe-1207996/93fsvqag6d.png?imageView2/2/w/1620)
Mycat将各个DB节点返回的数据 [10,11],[16,17], [20,21] 经过最小堆计算后返回给应用的结果集是 [10,11]。可是,对于应用而言,该表的所有数据明明是 0-29 这30个数据的集合,limit 5,2 操作返回的结果集应该是 [5,6],如果返回 [10,11] 则是错误的处理逻辑。
所以Mycat在处理 有偏移量的排序分页 时是另外一套逻辑——改写SQL 。如下: Mycat在下发有 limit m,n 的SQL语句时会对其进行改写,改写成 limit 0, m+n 来保证查询结果的逻辑正确性。所以,Mycat发送到后端DB上的SQL语句是
mysql>select * fromtable order by id limit 0,7;
各个DB返回给Mycat的结果集是
DB1: [0,1,2,3,4,10,11]
DB2: [5,6,7,8,9,16,17]
DB3: [20,21,22,23,24,25,26]
经过最小堆计算后得到最小序列 [0,1,2,3,4,5,6] ,然后返回偏移量为5的两个结果为 [5,6] 。
虽然Mycat返回了正确的结果,但是仔细推敲发现这类操作的处理逻辑是及其消耗(浪费)资源的。应用需要的结果集为2条,Mycat中需要处理的结果数为21条。也就是说,对于有 t 个DB节点的全分片 limit m, n 操作,Mycat需要处理的数据量为 (m+n)*t 个。比如实际应用中有50个DB节点,要执行limit 1000,10操作,则Mycat处理的数据量为 50500 条,返回结果集为10,当偏移量更大时,内存和CPU资源的消耗则是数十倍增加。
如果设计使用Mycat时有分页排序,请慎重使用Mycat,可以考虑放弃!
3)任意表JOIN 先看一下在单库中JOIN中的场景。假设在某单库中有 player 和 team 两张表,player 表中的 team_id 字段与 team 表中的id 字段相关联。操作场景如下图:
![](https://ask.qcloudimg.com/http-save/yehe-1207996/2cth8hamym.png?imageView2/2/w/1620)
JOIN操作的SQL如下:
mysql>selectp_name,t_name from player p, team t where p.no = 3 and p.team_id = t.id;
此时能查询出结果
如果将这两个表的数据分库后,相关联的数据可能分布在不同的DB节点上,如下图:
![](https://ask.qcloudimg.com/http-save/yehe-1207996/g6bjan43vd.png?imageView2/2/w/1620)
这个SQL在各个单独的分片DB中都查不出结果,也就是说Mycat不能查询出正确的结果集。
设计使用Mycat时如果要进行表JOIN操作,要确保两个表的关联字段具有相同的数据分布,否则请慎重使用Mycat,可以考虑放弃!
4)分布式事务 Mycat并没有根据二阶段提交协议实现 XA事务,而是只保证 prepare 阶段数据一致性的 弱XA事务 ,实现过程如下:
应用开启事务后Mycat标识该连接为非自动提交,比如前端执行:
mysql>begin;
Mycat不会立即把命令发送到DB节点上,等后续下发SQL时,Mycat从连接池获取非自动提交的连接去执行。
![](https://ask.qcloudimg.com/http-save/yehe-1207996/ux6ef14wmb.png?imageView2/2/w/1620)
Mycat会等待各个节点的返回结果,如果都执行成功,Mycat给该连接标识为 Prepare Ready 状态,如果有一个节点执行失败,则标识为 Rollback 状态。
![](https://ask.qcloudimg.com/http-save/yehe-1207996/5h3gs3w6uq.png?imageView2/2/w/1620)
执行完成后Mycat等待前端发送 commit 或 rollback 命令。发送 commit 命令时,Mycat检测当前连接是否为 Prepare Ready 状态,若是,则将 commit 命令发送到各个DB节点。
![](https://ask.qcloudimg.com/http-save/yehe-1207996/l6gj5c4mxj.png?imageView2/2/w/1620)
但是,这一阶段是无法保证一致性的,如果一个DB节点在 commit 时故障,而其他DB节点 commit 成功,Mycat会一直等待故障DB节点返回结果。Mycat只有收到所有DB节点的成功执行结果才会向前端返回 执行成功 的包,此时Mycat只能一直 waiting 直至TIMEOUT,导致事务一致性被破坏。
设计使用Mycat时如果有分布式事务,得先看是否得保证事务得强一致性,否则请慎重使用Mycat,可以考虑放弃!
六、利用MyCAT实现MySQL的读写分离、主从切换、分库分表的操作记录
Mycat实现Mysql主从复制,其中写操作在master主节点上执行,包括insert,delete,update 语句操作;读操作在slave节点上执行,只有select语句操作,其他操作均由主master的二进制文件决定;MyCat支持双主多从,多主多从情况需要配置多个writeHost兄弟节点,多个readHost节点即可!
Mycat的架构其实很好理解,Mycat是数据库代理中间件,Mycat后面就是物理数据库。和Web服务器的Nginx类似。对于使用者来说,访问的都是Mycat,不会接触到后端的数据库。如下案例是做一个主从、读写分离,简单分库分表的示例。结构如下图:
![](https://ask.qcloudimg.com/http-save/yehe-1207996/i9dc2cpmxq.png?imageView2/2/w/1620)
服务器信息如下(实验环境,关闭机器的iptables防火墙和selinux):
服务器主机名 ip 说明
Mycat-node 192.168.10.210 mycat服务器,连接数据库时,连接此服务器
Mysql-node1 192.168.10.205 物理数据库1,真正存储数据的数据库,这里为Master主数据库
Mysql-node2 192.168.10.206 物理数据库2,真正存储数据的数据库,这里为Slave主数据库
三台机器分布修改主机名,并做hosts绑定
# vim /etc/hosts
192.168.10.205 Mysql-node1
192.168.10.206 Mysql-node1
192.168.10.210 Mycat-node
为方便做实验,关闭三台机器的iptables防火墙和selinux
Mycat作为主数据库中间件,肯定是与代码弱关联的,所以代码是不用修改的,使用Mycat后,连接数据库是不变的,默认端口是8066。连接方式和普通数据库一样,比如:jdbc:mysql://192.168.10.210:8066/
1)Mysql安装及主从复制部署(Mysql-node1和Mysql-node2两台机器上操作)
a)安装Mysql,安装过程要在两个Mysql节点机器上都要操作。
安装过程参考:http://www.cnblogs.com/kevingrace/p/6109679.html
这里省去安装过程~~
b)Mysql主从复制(两个节点的mysql登录用户名和密码都是root/123456)
参考:http://www.cnblogs.com/kevingrace/p/6256603.html
====================================================
首先是Mysql-node1主节点配置操作
[root@Mysql-node1 ~]# cp /usr/local/mysql/my.cnf /usr/local/mysql/my.cnf.bak
[root@Mysql-node1 ~]# vim /usr/local/mysql/my.cnf #在[mysqld]区域添加下面内容
......
[mysqld]
server-id=1
log-bin=mysql-bin
#binlog-do-db=kevin #需要同步的数据库。如果是多个同步库,就以此格式另写几行即可。如果不指明对某个具体库同步,就去掉此行,表示同步所有库(除了ignore忽略的库)
binlog-ignore-db = mysql,information_schema
sync_binlog = 1
binlog_checksum = none
binlog_format = mixed
重启mysql服务
[root@Mysql-node1 ~]# /etc/init.d/mysql restart
登录mysql,授予slave从机复制权限
mysql> grant replication slave,replication client on *.* to slave@'192.168.10.206' identified by "slave@123";
Query OK, 0 rows affected (0.05 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.06 sec)
授权之后,要保证192.168.10.206这台slave节点机器能使用上面的权限信息登录到本机的mysql
将数据库锁住,仅仅允许读,以保证数据一致性;
mysql> FLUSH TABLES WITH READ LOCK; #注意,锁定后,如果自己同步对方数据,同步前一定要记得先解锁!
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.05 sec)
查看主节点的master复制信息
mysql> show master status;
+------------------+----------+--------------+--------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+--------------------------+-------------------+
| mysql-bin