如果在一个时刻有大量的请求涌向master,master可能由于并发压力问题宕机,为了解决单节点的并发压力问题,就产生了一种新的架构,叫做读写分离架构,读写分离架构是基于主从复制架构和集群的基础之上所做的处理。
在日常服务中,查询请求是要远多于修改请求的,比如京东618时可能在1000次查询之中只会有 10 ~ 100 次订单请求。我们让 master 用来处理写请求(增、删、改),而所有的读请求(查)都交由 slave 处理这就是读写分离架构,这种架构可以有效的解决单节点的并发压力问题。
如果要搭建读写分离结构,我们就需要一种中间件,叫做Mycat,Mycat是一个帮助我们实现读写分离的中间件。应用程序直接去请求Mycat,程序不再面向真实的数据库了,而面向的是数据库中间件Mycat,由Mycat解析sql语句之后再去转向数据库,我们在项目中无需再去连接数据库了,连接的是Mycat,由Mycat去管理真实的数据库。
读写分离架构核心解决的是并发压力问题
关于Mycat
Mycat为什么叫做中间件呢,因为它在应用程序和数据库架构之间起到了中间调协的作用,所以我们管这种产品就叫中间件,中间产品就叫中间件。
使用Mycat时我们不需要在项目中写这么多数据库节点了,我们只需要去连接Mycat就可以了,因为我们要把所有的数据库集群的节点交给Mycat管理。
Mycat内部有一个虚拟库,这个虚拟库日后要去映射真实库,之前我们的应用连的是真实的数据库,但是现在我们连的是虚拟库,连这个虚拟库的目的是为了把所有的请求都交给Mycat处理,Mycat是怎么实现读写分离的呢,是因为它里面有一个 druidSqlParse,Mycat能够把所有的sql语句进行解析,解析的目的是如果是update、delete、insert这样的写语句,它会对应去转向它内部配置的主库(master)去执行,如果是查询相关的,也就是读请求,它会去转向内部的从库去(slave)执行。
Mycat它是一个数据库服务,它也有自己的端口号,是8066,同时Mycat内部的虚拟库我们也会为它去配备用户名和密码,日后我们连接Mycat的时候,不再使用 3306,也不再连接我们的真实库,我们连接的是虚拟库,使用的是8066,用的是Mycat中虚拟库的用户名和密码,日后我们所有的操作,操作的其实是Mycat,由Mycat内部帮我们做sql解析之后,转向我们配置的真正的maste或slave。
另外我们需要在Mycat它的配置文件中配置所有的MySQL集群结点,因为我们要明确告诉Mycat哪些结点是主节点,哪些结点是从节点,以便于它将读请求转给从节点,将写请求转给主节点。
基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得MYCAT一开始就拥有一个很好的起点,站在巨人的肩膀上,我们能看到更远。业界优秀的开源项目和创新思路被广泛融入到MYCAT的基因中,使得MYCAT在很多方面都领先于目前其他一些同类的开源项目,甚至超越某些商业产品。
MYCAT背后有一支强大的技术团队,其参与者都是5年以上资深软件工程师、架构师、DBA等,优秀的技术团队保证了MYCAT的产品质量。MYCAT并不依托于任何一个商业公司,因此不像某些开源项目,将一些重要的特性封闭在其商业产品中,使得开源项目成了一个摆设.
mycat本身是用java写的,所以我们必须保证现有Linux系统中已经安装了JDK
读写分离架构是在主从复制架构的基础上进行搭建的,因为之前 master 和 slave 的主从复制架构已经搭建好了,所以我们无需重新搭建了。
注意:Mycat只是映射了数据库,用一个虚拟库去映射真实的数据库,映射之后是在真实的数据库中执行表的相关操作的。
新克隆一个虚拟机,在其中安装mycat
# 1.下载mycat
http://dl.mycat.org.cn/
在网站中下载相应版本的mycat就好了,我下载的是1.6.5版本的mycat
下载完成之后将mycat安装包传到Linux系统中
# 2.解压mycat
将安装包移到 /usr 下 然后解压
mv Mycat-server-1.6.5-release-20180122220033-linux.tar.gz /usr
cd /usr
tar -zxvf Mycat-server-1.6.5-release-20180122220033-linux.tar.gz
或者
直接解压到 /usr 目录下
tar -zxvf Mycat-server-1.6.5-release-20180122220033-linux.tar.gz -C /usr
# 3.查看解压之后目录]
cd /usr/mycat
ls
总用量 12
drwxr-xr-x. 2 root root 190 10月 14 22:58 bin
drwxrwxrwx. 2 root root 6 3月 1 2016 catlet
drwxrwxrwx. 4 root root 4096 10月 14 22:58 conf
drwxr-xr-x. 2 root root 4096 10月 14 22:58 lib
drwxrwxrwx. 2 root root 6 10月 28 2016 logs
-rwxrwxrwx. 1 root root 217 10月 28 2016 version.txt
这里我们来解释一下解压出来的mycat包下的目录的基本信息:
bin目录是用来启动和关闭mycat的目录,bin目录下面有mycat命令去启动和关闭mycat
catlet是用来放一些小的程序(无需关注)
conf是咱们要修改的mycat的配置文件目录,conf目录下面有schema.xml和server.xml文件需要重点关注,
schema.xml使用来描述mycat管理的哪些集群结点,同时也是用来配置真实库和虚拟库之间的映射的。
server.xml是用来指定mycat启动时的端口、用户名、密码
lib目录中是一些mycat启动时依赖的第三方jar包
logs是mycat启动时的日志目录,这个目录里面会有一些日志文件
# 4.配置Mycat的虚拟库和真实库之间的映射以及Mycat管理了哪些节点
配置conf目录下的schema.xml文件去完成Mycat的虚拟库和真实库之间的映射以及Mycat管理了哪些节点
vim /usr/mycat/conf/schema.xml
在其中添加上这些
select user()
逻辑库名字随便改,checkSQLschema是否去校验sql语句,我们不校验,所以里面写false
sqlMaxLimit代表sql的最大长度,100个字符,这个逻辑库要映射数据库中的哪个真实的数据库呢
dataNode代表映射的真实的数据结点(在下面会配置), 标签代表我们映射了一个逻辑库。
之后定义MyCat的数据节点, 标签的name必须和 标签的
dateNode一致,因为 schema 找的就是 dateNode, 标签的 dataHost 代表映射的真
实的数据主机(在下面会配置), 标签的 database 代表映射的真实的数据库(写实际
存在的数据库的名称)
标签的name 要和上面 标签的dataHost保持一致
mycat怎么知道某个结点活没活着呢,它们之间需要保持心跳,通过 select user()
这个sql语句去检测心跳
写结点用 标签去配置,属性host代表名字,随便写,url写maser的ip地址以及端口
号,之后写这个master的mysql的用户名和密码
读结点用 标签去配置,属性host代表名字,随便写 ,url写ip地址以及端口号,之后写
这个节点的mysql的用户名和密码
在写结点里面写它的多个从节点,把从节点放在了写节点的内部,代表这个从节点是指定主节点的从节点
其实schema这里的配置就是告诉mycat它管理的是哪些mysql结点,其中哪些mysql结点是主节点,哪些mysql结点
是从节点,进而我们也在这里面明确的告诉它了逻辑库映射的真实的数据库是什么,日后在schema中,schema这一
套标签也可以出现很多回,我们也可以有另一个逻辑库映射其他的真实库。
# 6.配置Mycat的用户名和密码是什么
配置conf目录中的server.xml文件指定mycat的用户名和密码是什么
vim /usr/mycat/conf/server.xml
在其中添加上这些
druidparser
utf8
root
aa
是用来对系统进行配置的,告诉它默认的sql解析方式,这里使用的sql解析方式是
druidparser 代表使用阿里巴巴的解析方式,mycat是阿里巴巴开发的中间件,所以它也用了阿里巴巴的
druid连接池,在解析sql语句时使用什么编码进行解析sql语句,utf8mb4是8.0版本以后的,我们这里装的
是5版本,所以必须用utf8。
标签就是用来配制这个mycat的用户名以及密码(mycat独立的密码,这里把Mycat看成一个
MySQL,需要用户名和密码外部才可以连接),以及这个用户和这个密码可以操作哪个逻辑库(这里我们的逻辑库是
aa)
# 7.启动mycat
在mycat目录的bin目录下有一个叫做mycat的文件,我们可以用它去启动mycat或者关闭mycat
cd /usr/mycat/bin
在执行bin下面的命令时我们必须以一个明确的位置告诉它,启动mycat是后面还需要加一个参数
./mycat { console | start | stop | restart | status | dump }
console代表以前台窗口启动
start代表以后台守护进程启动
stop代表停止
restart代表重启
status代表状态
dump代表以快照方式
用console的话可以在前台启动的时候看它的启动日志,这样如果出错的话可以立即发现它的错误
如果用start,那就要去它的logs日志文件中去查看了,所以这里我们建议使用console进行启动
./mycat console
# 8.查看日志
tail -f /usr/mycat/logs/mycat.log
# 9.项目连接配置,测试
mycat默认的端口是 8066
接下来我们来具体演示一些读写分离架构的搭建:
- 下载 Mycat 然后将Mycat压缩包传到Linux系统,然后安装(解压到)/usr 目录下
tar -zxvf Mycat-server-1.6.5-release-20180122220033-linux.tar.gz -C /usr
- 编辑conf目录下的schema.xml文件去完成Mycat的虚拟库和真实库之间的映射以及Mycat管理了哪些节点
vim /usr/mycat/conf/schema.xml
初始时schema.xml 文件中有许多无关信息,所以在添加之前我们需要将其删成下面这个样子
之后我们在它的 mycat 标签之间加上下面的内容
<schema name="aa" checkSQLschema="false" sqlMaxLimit="100" dataNode="emsNode">schema>
<dataNode name="emsNode" dataHost="emsHost" database="ems" />
<dataHost name="emsHost" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="-1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="192.168.72.132:3306" user="root"
password="root">
<readHost host="hostS1" url="192.168.72.133:3306" user="root" password="root" />
writeHost>
dataHost>
mycat:schema>
- 编辑conf目录下的server.xml文件去配置Mycat的用户名和密码是什么
vim /usr/mycat/conf/server.xml
初始时server.xml 文件中有许多无关信息,所以在添加之前我们需要将其删成下面这个样子
之后我们在它的 mycat 标签之间加上下面的内容
<system>
<property name="defaultSqlParser">druidparserproperty>
<property name="charset">utf8property>
system>
<user name="root">
<property name="password">rootproperty>
<property name="schemas">aaproperty>
user>
- 启动 mycat
注意:在启动mycat的时候一定要开启master和slave的数据库
cd /usr/mycat/bin
./mycat console
显示下面信息就说明mycat启动成功了
- 如何在项目中应用Mycat
在没有使用Mycat之前我们项目中连接的是数据库,写的都是真实的数据库所在服务器ip、Mysql端口以及真实库的名字、用户名、密码,但是在使用Mycat之后我们需要写Mycat所在服务器的ip、Mycat的端口、以及虚拟库的名字、用户名、密码,因为我们已经在Mycat的配置文件里面配置好了它管理的节点,我们让Mycat去管理那些主从节点就好了。代码在具体连接的时候连接的不再是mysql了,连接的是mycat,
mycat默认的端口是8066,我们配置的虚拟库的名字叫 aa,用户名和密码都是root
在项目中连接数据库时应该连接mycat所在的服务器,并写上mycat的端口,mycat的虚拟库的名字,mycat虚拟库的用户名和密码
注意:
最后还有非常重要的一点:
mycat在设计的时候除了看sql语句之外,还看发给mycat时这个连接是否开启了事务,如果开启了事务,不管你是读请求还是写请求,统一走master,查询操作走master,与我们的读写分离架构的设计初衷违背,这也就是为什么我们需要在事物的实现类中的查询方法上加上 @Transactional(propagation = Propagation.SUPPORTS) 去让它支持事务(有:融入,没有:不开启)而不是使用类上加的开启事务的原因。如果明确本次没有开启事务,那么一定是查询操作,直接走从库。
读写结构分离这里主要借助的它的逻辑库和它内部的sql解析,正是因为它有sql解析,它才能够监听我们的sql语句到底是读请求还是写请求,还会验证是否含有事务,如果含有事务,即使是查询,也怕在查询中执行了增删改操作,如果到从库会影响主库的数据状态,所以会把它交给主库。