首页 > 技术文章 > 朝花夕拾系列文章 >

使用mycat实现数据库读写分离

更新时间:2019-09-06 | 阅读量(617)

### 01前言 在上一篇文章中,我们基于CentOS7实现了MySQL的主从复制,我们需要把DML操作放在MySQL的主节点执行,需要把DQL操作放在MySQL的从节点执行.那在应用程序中我们怎么控制这个逻辑呢? 下面是一种可行的方案: 1.在项目中配置两个数据源(一主一从),分别是A(主)和B(从)。 2.在程序中判断是不是select操作,如果是就把数据源更换成B,如果不是select操作就更换成A,这样就可以实现程序中的读写分离了. 3.关于步骤2的更换数据源,Spring中是提供了`AbstarctRoutingDataSource`这个类在程序中实现数据源的切换的. 这种方案可行是可行,但是在应用程序端需要写挺多代码的,而且这些属于非业务层面的东西,写在项目中对项目来说是代码侵入了. 所以对于读写分离我们可以使用Mycat数据库中间件来完成. ### 02MyCat介绍 关于MyCat的详细简介,同学们可以去[Mycat官网](http://example.com/ "With a Title").下载`Mycat权威指南.pdf`来看,文章中的前世今生写得幽默有趣,挺有意思的,从描述来看都感觉看的不是本技术书,而是本小说哈.在这里我简单的介绍一下Mycat: 2008年阿里开源了一款数据库中间件Amoeba,随着业务的增长,这块产品不能支持目前已有的业务.阿里又开源了Cobar这款中间件,这款产品在阿里内部得到了很广泛的应用.(应用在了3000多台服务器,每天超过10亿次的数据库访问),当时一开源就得到了开发者和运维人员的追捧. 开源之后,阿里就没有对这款产品维护了,这款产品在生产环境中还是存在挺多问题的.在这样的场景下MyCat诞生了,2013年MyCat社区在Cobar的基础上进行了很大的改造和升级. ### 03Mycat优势 优势这个我就从官网上给同学们摘抄下来了: 基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得MYCAT一开始就拥有一个很好的起点,站在巨人的肩膀上,我们能看到更远。业界优秀的开源项目和创新思路被广泛融入到MYCAT的基因中,使得MYCAT在很多方面都领先于目前其他一些同类的开源项目,甚至超越某些商业产品。 MYCAT背后有一支强大的技术团队,其参与者都是5年以上软件工程师、架构师、DBA等,优秀的技术团队保证了MYCAT的产品质量。 MYCAT并不依托于任何一个商业公司,因此不像某些开源项目,将一些重要的特性封闭在其商业产品中,使得开源项目成了一个摆设。 目前也有挺多公司在生产环境上使用Mycat的,[使用案例](https://github.com/MyCATApache/Mycat-Server/issues/112),所以来说还是一款很不错,很稳定的产品. ### 04主要作用 主要作为分布式数据库系统的中间层 1.可以实现数据库的读写分离 2.支持负载均衡 3.支持后端MySQL高可用 4.数据库的垂直拆分 5.数据库水平拆分 关于Mycat如何实现数据库的分库分表,我在之前的简书中已经写了案例,[数据库中间件Mycat+SpringBoot完成分库分表](https://www.jianshu.com/p/f81422b1c915). ### 05Mycat的基本概念 ![](http://www.wolfcode.cn/data/upload/20190906//5d71ff54ab2c1.png) 从上图可以看到,我们应用程序不是直接连接真实数据库的的,而是连接Mycat的.所以在Mycat的配置文件中我们就需要去定义`逻辑库`、`逻辑表`和`真实库`、`真实表`的映射关系 ### 06安装JDK环境变量 0.我们Mycat的安装是基于CentOS7环境来安装的,所以同学们先把基础环境搭建好哈. 1.Mycat是基于Java开发的一款中间件产品,所以要使用Mycat前需要把JDK的环境装上.[JDK1.8安装包](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). 2.下载好jdk1.8安装包后,通过工具上传到服务器上.我们通常会把文件上传到`/usr/local/software`目录下 ![](https://upload-images.jianshu.io/upload_images/5815733-e7bc30ef1e76c69a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 3.然后使用解压命令解压到`/usr/local`目录下 ```linux tar -zxvf jdk-8u161-linux-x64.tar.gz -C /usr/local ``` ![](https://upload-images.jianshu.io/upload_images/5815733-3ced02e08434a0a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 但是名字有些长,不方便我们设置环境变量,所以我们可以对这个文件夹重命名 ```linux mv jdk1.8.0_161/ jdk1.8 ``` ![](https://upload-images.jianshu.io/upload_images/5815733-d2d262c9c1ae1a84.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 4.jdk解压好之后,我们就可以来配置环境变量了. ```linux vi /etc/profile ``` 编辑界面如下: ![](https://upload-images.jianshu.io/upload_images/5815733-98dffffffa572307.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 需要在这个文件中添加这两行配置: ```linux export JAVA_HOME=/usr/local/jdk1.8 export PATH=.:$JAVA_HOME/bin:$PATH ``` 编辑后如下图所示: ![](http://www.wolfcode.cn/data/upload/20190906//5d72005b7309e.png) 然后保存退出即可. 然后需要通过这个命令来重新加载配置文件: ```linux source /etc/profile ``` 设置好之后,通过这个命令查看环境变量是否配置好了. ```linux java -version ```` 如果看到下面这个内容,说明已经配置好了. ![](http://www.wolfcode.cn/data/upload/20190906//5d7200cb4c704.png) ###07安装Mycat 我们这次使用的是[Mycat-server-1.6.7](http://dl.mycat.io/1.6.7.1/)版本,下载好之后上传到服务器上. ![](https://upload-images.jianshu.io/upload_images/5815733-c0f348f8cdcd943e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 然后使用命令进行解压 ```linux tar -zxvf Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz -C /usr/local/ ``` ![](https://upload-images.jianshu.io/upload_images/5815733-786f949da71a987e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 这样我们就已经装好Mycat了,下一步就需要去配置Mycat的配置文件了. ###08配置读写分离 我们可以先看看Mycat的文件目录结构 ![](https://upload-images.jianshu.io/upload_images/5815733-55e4406f4b070620.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) `bin目录`:存放Mycat运行的脚本 `conf目录`:存放Mycat的配置文件 `logs目录`:存放Mycat启动及运行的日志文件 我们现在需要进入到`conf目录`,修改配置文件. ![](https://upload-images.jianshu.io/upload_images/5815733-dc0d62c8acd0f21f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 目前来说,我们只需要关心`server.xml`、`schema.xml`、`rule.xml`这三个文件. `server.xml`:主要配置Mycat的启动参数,连接密码,白名单,黑名单,访问权限控制等参数,我们暂时不修改,用默认的就可以了. `schema.xml`:主要配置逻辑库、逻辑表和真实库、真实表之间的映射关系. `rule.xml`:主要配置分库分表时的分片规则,此次试验中我们并不需要修改到. 目前我们的环境是这样的: 主机IP | 角色 -|- 192.168.142.128 | MySQL主节点 | 192.168.142.129 | MySQL从节点 | 192.168.142.130 | MyCat服务器 | 然后我们需要修改`schema.xml`文件,修改内容如下(建议直接复制我的,然后把Ip换一下就好了.): ```xml select user() ``` #### 一、schema 解释 `` `name属性`:表示的是逻辑库的名字,是应用程序连接的时候的数据库名称. `checkSQLschema属性`:当该值设置为 true 时,如果我们执行语句**select * from TESTDB.travelrecord;**则 MyCat 会把语句修改 为**select * from travelrecord;**。即把表示 schema 的字符去掉,避免发送到后端数据库执行时报**(ERROR 1146 (42S02): Table ‘testdb.travelrecord’ doesn’t exist)。** 不过,即使设置该值为 true ,如果语句所带的是并非是 schema 指定的名字,例如:**select * from db1.travelrecord;** 那么 MyCat 并不会删除 db1 这个字段,如果没有定义该库的话则会报错,所以在提供 SQL 语句的最好是不带这个字段。 `sqlMaxLimit属性`:当该值设置为某个数值时。每条执行的 SQL 语句,如果没有加上 limit 语句,MyCat 也会自动的加上所对应 的值。例如设置值为 100,执行**select * from TESTDB.travelrecord;**的效果为和执行**select * from TESTDB.travelrecord limit 100;**相同。 设置该值的话,MyCat 默认会把查询到的信息全部都展示出来,造成过多的输出。所以,在正常使用中,还 是建议加上一个值,用于减少过多的数据返回。 当然 SQL 语句中也显式的指定 limit 的大小,不受该属性的约束。 需要注意的是,如果运行的 schema 为非拆分库的,那么该属性不会生效。需要手动添加 limit 语句 #### 二、table标签解释 `
` Table 标签定义了 MyCat 中的逻辑表,所有需要拆分的表都需要在这个标签中定义。 `name 属性`:定义逻辑表的表名,这个名字就如同我在数据库中执行 create table 命令指定的名字一样,同个 schema 标 签中定义的名字必须唯一。 `dataNode属性`:定义这个逻辑表所属的 dataNode, 该属性的值需要和后面 dataNode 标签中 name 属性的值相互对应。 #### 三、dataNode标签解释 ` ` dataNode 标签定义了 MyCat 中的数据节点,也就是我们通常说所的数据分片。一个 dataNode 标签就是一个独立的数据分片。 `name 属性`:定义数据节点的名字,这个名字需要是唯一的,我们需要在 table 标签上应用这个名字,来建立表与分片对 应的关系。 `dataHost 属性`:该属性用于定义该分片属于哪个数据库实例的,属性值是引用 dataHost 标签上定义的 name 属性。 `database 属性`:该属性用于定义该分片属性哪个具体数据库实例上的具体库,因为这里使用两个纬度来定义分片,就是:实例+具体的库。因为每个库上建立的表和表结构是一样的。所以这样做就可以轻松的对表进行水平拆分。 #### 四、dataHost标签解释 ` ` 作为 Schema.xml 中最后的一个标签,该标签在 mycat 逻辑库中也是作为最底层的标签存在,直接定义了具 体的数据库实例、读写分离配置和心跳语句。 `name 属性`:唯一标识 dataHost 标签,供上层的标签使用 `maxCon 属性`:指定每个读写实例连接池的最大连接。也就是说,标签内嵌套的 writeHost、readHost 标签都会使用这个属 性的值来实例化出连接池的最大连接数 `minCon 属性`:指定每个读写实例连接池的最小连接,初始化连接池的大小 `balance 属性`: 负载均衡类型,目前的取值有 3 种: 1. balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。 2. balance="1",全部的 readHost 与 stand by writeHost参与 select 语句的负载均衡,简单的说,当双 主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载 均衡。 3. balance="2",所有读操作都随机的在 writeHost、readhost 上分发。 4. balance="3",所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力, 注意 balance=3 只在 1.4 及其以后版本有,1.3 没有。 `writeType 属性`: 负载均衡类型,目前的取值有 3 种: 1. writeType="0", 所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个 writeHost, 重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties . 2.writeType="1",所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐。switchType 属 性 - -1 表示不自动切换。 - 1 默认值,自动切换。 - 2 基于 MySQL 主从同步的状态决定是否切换。 `dbType 属性`:指定后端连接的数据库类型,目前支持二进制的 mysql 协议,还有其他使用 JDBC 连接的数据库。例如: mongodb、oracle、spark 等。 `dbDriver 属性`:指定连接后端数据库使用的 Driver,目前可选的值有 native 和 JDBC。使用 native 的话,因为这个值执行的 是二进制的 mysql 协议,所以可以使用 mysql 和 maridb。其他类型的数据库则需要使用 JDBC 驱动来支持。 从 1.6 版本开始支持 postgresql 的 native 原始协议。 如果使用 JDBC 的话需要将符合 JDBC 4 标准的驱动 JAR 包放到 MYCAT\lib 目录下,并检查驱动 JAR 包中 包括如下目录结构的文件:META-INF\services\java.sql.Driver。在这个文件内写上具体的 Driver 类名,例如: com.mysql.jdbc.Driver。 #### 五、heartbeat标签解释 `select user() ` 这个标签内指明用于和后端数据库进行心跳检查的语句。例如,MYSQL 可以使用 select user(),Oracle 可以 使用 select 1 from dual 等。 ####六、writeHost 标签、readHost 标签解释 这两个标签都指定后端数据库的相关配置给 mycat,用于实例化后端连接池。唯一不同的是,writeHost 指定写实例、readHost 指定读实例,组着这些读写实例来满足系统的要求。在一个 dataHost 内可以定义多个 writeHost 和 readHost。但是,如果 writeHost 指定的后端数据库宕机,那么这个 writeHost 绑定的所有 readHost 都将不可用。另一方面,由于这个 writeHost 宕机系统会自动的检测到,并切换到备用的 writeHost 上去。 `host 属性`:用于标识不同实例,一般 writeHost 我们使用M1,readHost 我们用S1。 `url 属性`:后端实例连接地址,如果是使用 native 的 dbDriver,则一般为 address:port 这种形式。用 JDBC 或其他的 dbDriver,则需要特殊指定。当使用 JDBC 时则可以这么写jdbc:mysql://localhost:3306/。 `user 属性`:后端存储实例需要的用户名字。 `password 属性`:后端存储实例需要的密码。 ### 08启动Mycat 使用命令 ```linux /usr/local/mycat/bin/mycat start ``` 然后观察wrapper.log文件看是否已经启动成功. ![](https://upload-images.jianshu.io/upload_images/5815733-f443d05960221d41.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 出现这样的界面说明已经启动成功.可以通过Navicat客户端连接Mycat,或者通过应用程序连接也是可以的. ![](https://upload-images.jianshu.io/upload_images/5815733-366047da0f69c901.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 如果需要修改默认端口或者默认密码的话需要修改server.xml文件.在应用程序中连接的时候,和之前连接MySQL一样的.只是URL地址不一样而已,代码还是一样的. ### 09其他 目前我们连接Mycat之后,所有的读操作都是走从节点的,如果有需求需要读走主节点的话,可以使用Mycat中的注解,强制让读操作走主节点. ```xml /*#mycat:db_type=master*/ 真实SQL ```
叩丁狼学员采访 叩丁狼学员采访
叩丁狼头条 叩丁狼头条
叩丁狼在线课程 叩丁狼在线课程