MySQL 读写分离原理
一、原理
读写分离就是只在主服务器上写,只在从服务器上读。基本的原理是让主数据库处理事务性查询,而从数据库处理 select 查询。数据库复制被用来把主数据库上事务性查询导致的变更同步到集群中的从数据库。
二、为什么要做读写分离
读写分离,解决的是,数据库的写入,影响了查询的效率,就比如说你写入10万条数据到数据库需要4分钟,但是读取就只要5秒钟
PS:什么时候用读写分离
数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询(select)多的情况下会考虑使用。利用数据库主从同步,再通过读写分离可以分担数据库压力,提高性能。
三、实现方式
常见的 MySQL 读写分离分为以下两种:
① 基于程序代码内部实现
在代码中根据 select、insert 进行路由分类,这类方法也是目前生产环境应用最广泛的。
优点是性能较好,因为在程序代码中实现,不需要增加额外的设备为硬件开支;缺点是需要开发人员来实现,运维人员无从下手。
但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的Java应用,如果在程序代码中实现读写分离对代码改动就较大。
② 基于中间代理层实现
代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有以下代表性程序。
(1)MySQL-Proxy。MySQL-Proxy 为 MySQL 开源项目,通过其自带的 lua脚本进行SQL 判断。
(2)Atlas是由奇虎360的Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性。360内部使用Atlas运行的mysql业务,每天承载的读写请求数达几十亿条。支持事物以及存储过程。
(3)Amoeba。由陈思儒开发,作者曾就职于阿里巴巴。该程序由Java语言进行开发,阿里巴巴将其用于生产环境。但是它不支持事务和存储过程。
四、Amoeba介绍
Amoeba:是一个以MySql为底层数据存储,并对应提供MySQL协议接口的proxy(代理),外号变形虫
读取请求发送给从服务器是,采用轮询调度算法
amoeba使用的java语言编写,配置文件为xml
amoeba主要负责对外的一个代理IP
访问这个IP时,发送的请求为“写”请求,则会转给主服务器
当发送的请求为“读”时,会通过调度转发给从服务器,使用轮询算法,轮流分配给两台从服务器
amoeba可以视为调度器,如果主服务器挂掉(单点故障),则会有MHA解决这个问题
五、搭建 MySQL 读写分离
实验机器
master:192.168.38.138
slave1:192.168.38.139
slave2:192.168.38.140
amoeba:192.168.38.142
client:192.168.38.143
Amoeba安装
##安装 Java 环境##
因为 Amoeba 基于是 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,高版本不建议使用。
将jdk-6u14-linux-x64.bin 和 amoeba-mysql-binary-2.2.0.tar.gz.0 上传到/opt目录下。
cd /opt/
cp jdk-6u14-linux-x64.bin /usr/local/
#因为此jdk 是bin文件(二进制可执行文件)所以直接执行即可
cd /usr/local/
chmod +x jdk-6u14-linux-x64.bin
./jdk-6u14-linux-x64.bin # 输入yes,按enter
重命名
mv jdk1.6.0_14/ /usr/local/jdk1.6
#添加环境变量
vim /etc/profile
export JAVA_HOME=/usr/local/jdk1.6
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin
export AMOEBA_HOME=/usr/local/amoeba
export PATH=$PATH:$AMOEBA_HOME/bin
#加载
source /etc/profile
#查看版本
java -version
安装 Amoeba软件
mkdir /usr/local/amoeba
tar zxvf /opt/amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
chmod -R 755 /usr/local/amoeba/
/usr/local/amoeba/bin/amoeba
//如显示amoeba start|stop 说明安装成功
修改主配置文件
##配置 Amoeba读写分离,两个 Slave 读负载均衡##
#先在Master、Slave1、Slave2 的mysql上开放权限给 Amoeba 访问
grant all on *.* to 'kkk'@'192.168.226.%' identified by '123456';
#再回到amoeba服务器配置amoeba服务:
cd /usr/local/amoeba/conf/
cp amoeba.xml amoeba.xml.bak
vim amoeba.xml
#以下配置的是第三个账户 (客户端找amoeba的用户身份)
#30行修改账户名(这里是client访问amoeba服务器时使用的账号)
<property name="user">amoeba</property>
#32行修改账户密码(这里是数据库访问amoeba服务器时使用账号时用的密码)
<property name="password">123123</property>
#115行修改默认池
<propertyname="defaultPoo1">master</propertv>
#117行取消注释
//取消下面的注释符号-->,改到这里
#118-119修改"读池"和"写池"
118 <propertyname="writePool">master</property>
119 <propertyname="readPool">slaves</property>
#修改数据库配置文件
cp dbServers.xml dbServers.xml.bak
vim dbServers.xml
#23行将text修改为mysql
mysql schema:Mysql所有数据库信息(show databases)
<property name="schema">mysql</property>
也如果你不知道是叫什么名字,那么注释掉也行,这里我是注释掉
#26行,amoeba访问三台mysql数据库的账户和密码(账户为test)
<property name="user">kkk</property>
#29行,修改访问数据库的密码
<property name="password">123456</property>
#45行,修改数据库主服务器名/地址
<dbServer name="master" parent="abstractServer">
#48行,修改master服务器ip
<property name="ipAddress">192.168.38.138</property>
#52行修改从服务器名
<dbServer name="slave1" parent="abstractServer">
#55行修改从服务器地址
<property name="ipAddress">192.168.38.139</property>
#紧接复制52-57行,粘贴,添加第二台服务器名
<dbServer name="slave2" parent="abstractServer">
#修改第二台服务器IP
<property name="ipAddress">192.168.38.140</property>
#66行,修改多个服务器池(multiPoo1)的名称(修改为slaves)<dbServer name="slaves" virtual-"true">
#72行,添加两个从服务器的服务器名(slave1 slave2)<property name="poolNames">slave1,slave2</property>
开启
/usr/local/amoeba/bin/amoeba start&
#另开终端,查看是否启动
netstat-natp | grep java
netstat-natp | grep 8066
六、测试
下载 mariadb 客户端
yum -y install mariadb
如果追求完美也可以直接装mysql一样用
systemctl stop firewalld
setenforce 0
#client远程登录 amoeba 服务器
mysql -u amoeba -p123456 -h 192.168.38.142 -P8066 ##这里client登录的amoeba的mysql
测试一:
amoeba服务器是否关联后端mysql#客户端进入数据库创建表
use work;
show tables;
create table test1 (id int(10),name varchar(10),address varchar(20)); ##这里我用test1表
#在三台mysq1服务器中查看是否有此表(amoeba关联)
use work;
show tables;
master
slave1
slave2
测试二:
测试mysgl读写分离
#这里我用test表
#2台从服务器关闭主从同步,测试amoeba读写分离
stop slave;
在slave1上
insert into test values('1','slave1','this_is_slave1');
在slave2上
insert into test values('2','slave2','this_is_slave2');
在master上
insert into test values('3','master','this_is_master');
在cliert上
insert into test values('4','client','this_is_client');
#master(处理写的任务) select * from test:发现:可查到数据
#slave(处理读的任务) select * from test;
发现:从服务器看不到数据
slave1插入数据
slave2插入数据
master插入数据
客户端插入数据并查看
查看从服务器,发现看不到数据(这里我就截图一台服务器了,太多截图了懒得整理)
测试三:
读写分离架构,对于读的任务是怎么操作的测试方式:
分别在slave1和salve2数据库中插入不同数据,然后使用客户端进select查询,观测结果:
#slave1
insert into test values('1','slave1','this_is_slave1');
#slave2
insert into test values('2','slave2','this_is_slave2'); #这里从服务器的两条就是之前插入的两条
#client端
select * from test
#小结:"流量"被轮询读取
这里由于之前的实验关闭主从同步
开启主从同步
七、总结
1、主从同步原理
通过amoeba代理服务器,实现只在主服务器上写,只在从服务上读;
主数据库处理事务性查询,从数据库处理 select 查询;
数据库复制被用来把事务性查询导致的变更同步到集群中的从数据库
2、如何查看主从同步状态是否成功
在从服务器内输入命令 show slave status\G,查看主从信息进行查看,里面有IO线程的状态信息,还有master服务器的IP地址、端口、事务开始号,
当 slave_io_running 和 slave_sql_running 都显示为yes时,表示主从同步状态成功
3、如果I/O和SQL不是yes呢,你是如何排查的
首先排除网络问题,使用ping命令查看从服务是否能与主服务器通信
再者查看防火墙和核心防护是否关闭
接着查看从服务器内的slave是否开启
两个从服务器的 server-id 是否相同导致只能连上一台
master_log_file 和 master_log_pos 的值要是否与Master查询的一致
4、show slave status能看到哪些信息(比较重要的)
IO线程的状态信息
master服务器的IP地址、端口、事务开始位置
最近一次的报错信息和报错位置等
5、主从复制慢(延迟)有哪些可能
主服务器的负载过大,被多个睡眠或者僵尸线程占用,导致系统负载过大
从库硬件比主库差,导致复制延迟
主从复制单线程,如果主库写并发太大,来不及传送到从库,就会导致延迟。
慢SQL语句过多
网络延迟
6、实验前,中的准备工作、或者需要注意的事项
每台机器都要关闭防火墙、核心防护
当java里的8066端口开启错误时,先检查防火墙、核心防护,其次时amoeba的配置文件
否与Master查询的一致
4、show slave status能看到哪些信息(比较重要的)
IO线程的状态信息
master服务器的IP地址、端口、事务开始位置
最近一次的报错信息和报错位置等
5、主从复制慢(延迟)有哪些可能
主服务器的负载过大,被多个睡眠或者僵尸线程占用,导致系统负载过大
从库硬件比主库差,导致复制延迟
主从复制单线程,如果主库写并发太大,来不及传送到从库,就会导致延迟。
慢SQL语句过多
网络延迟
6、实验前,中的准备工作、或者需要注意的事项
每台机器都要关闭防火墙、核心防护
当java里的8066端口开启错误时,先检查防火墙、核心防护,其次时amoeba的配置文件
当这一切都检查完毕后还是出错,三台机器重启数据库,然后amoeba机子重启8066端口(/usr/local/amoeba/bin/amoeba stop& , /usr/local/amoeba/bin/amoeba start&),客户机重新登录数据库就好了