目录
2、配置 amoeba 读写分离,之前两个 slave做读负载均衡
一、主从复制
MySQL 的主从复制和 MySQL 的读写分离两者有着紧密联系,首先要部署主从复制,只有主
从复制完成了,才能在此基础上进行数据的读写分离。
1、原理
主从复制的核心:2个日志、3个线程
master 线程:bin log 二进制日志、dump 线程
master 上的 dump 线程会监控 bin log 二进制日志的更新,若有更新会通知 slave的 I/O 线程
slave:relay log 中继日志、I/O 线程、SQL 线程
线程1:
slave上的 I/O 线程会向 master 申请同步 bin log 二进制日志的更新内容,slave的 I/O 线程把更新
内容写入自己的中继日志
线程2:
slave 的 SQL 线程把日志中的更新语句同步执行到内容,以达到和 master 数据库趋于一致
2、mysql支持的复制类型
基于语句的复制:在主服务器上执行的 SQL 语句,在从服务器上执行同样的语句;MySQL
默认采用基于语句的复制,效率比较高。
基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍。
混合类型的复制:默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用
基于行的复制。
3、主从复制的工作过程
(1)Master节点将数据的改变记录成二进制日志(bin log),当Master上的数据发生改变时,则
将其改变写入二进制日志中。
(2)Slave节点会在一定时间间隔内对Master的二进制日志进行探测其是否发生改变,如果发生改
变,则开始一个I/O线程请求 Master的二进制事件。
(3)同时Master节点为每个I/O线程启动一个dump线程,用于向其发送二进制事件,并保存至
Slave节点本地的中继日志(Relay log)中,Slave节点将启动SQL线程从中继日志中读取二进制
日志,在本地重放,即解析成 sql 语句逐一执行,使得其数据和 Master节点的保持一致,最后I/O
线程和SQL线程将进入睡眠状态,等待下一次被唤醒。
注:
●中继日志通常会位于 OS 缓存中,所以中继日志的开销很小。
●复制过程②有一个很重要的限制,即复制在 Slave上是串行化的,也就是说 Master上的并行更新
操作不能在 Slave上并行操作。
4、MySQL主从复制延迟
①、master服务器高并发,形成大量事务
②、网络延迟
③、主从硬件设备导致:cpu主频、内存io、硬盘io
④、本来就不是同步复制、而是异步复制
从库优化Mysql参数。比如增大innodb_buffer_pool_size,让更多操作在Mysql内存中完成,
减少磁盘操作。
从库使用高性能主机。包括cpu强悍、内存加大。避免使用虚拟云主机,使用物理主机,这样
提升了i/o方面性。
从库使用SSD磁盘
网络优化,避免跨机房实现同步
二、MySQL 主从复制架构搭建
1、前期准备
①、确认各主机名及IP地址,以下均在CentOS 7环境下配置。
主机名 | IP地址 | 主要软件 |
Master(主) | 192.168.110.128 | mysql-5.7 |
Slave1(从) | 192.168.110.129 | mysql-5.7 |
Slave2(从) | 192.168.110.130 | mysql-5.7 |
Amoeba(中继) | 192.168.110.131 | jdk1.6、Amoeba |
client(客户端) | 192.168.110.132 | mysql-5.7 |
systemctl stop firewalld
systemctl disable firewalld
setenforce 0
③、主从服务器时间同步
##############master#############
[root@master ~]# yum install -y ntp
[root@master ~]# vim /etc/ntp.conf #添加下面内容
……
server 127.127.80.0 #设置本地是时钟源,注意修改自己的网段
fudge 127.127.80.0 stratum 8 #设置时间层级为 8(限制在 15 内)
……
[root@slave2 ~]# systemctl start ntpd
##############selave1和selave2#############
[root@slave1 ~]# yum install -y ntp ntpdate
[root@slave1 ~]# systemctl start ntpd
[root@slave1 ~]# /usr/sbin/ntpdate 192.168.110.128 #设置与主服务器时间同步
2 Nov 00:28:31 ntpdate[18122]: the NTP socket is in use, exiting
[root@slave1 ~]# crontab -e #设置计划任务
*/30 * * * * /usr/sbin/ntpdate 192.168.110.128
#两台从服务器时间同步步骤一致
2、文件配置
①、主服务器的 mysql 配置
[root@master ~]# vim /etc/my.cnf
……
[mysqld]
server-id = 1 #定义 server-id,每台主机不可相同
log-bin=master-bin #主服务器开启二进制日志
binlog_format = MIXED #本次使用 MIXED 模式
log-slave-updates=true #允许从服务器更新二进制日志
[root@master ~]# systemctl restart mysqld.service #重启服务
[root@master ~]# mysql -uroot -p #进入mysql环境
Enter password:
#给从服务器授权
mysql> GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.110.%' IDENTIFIED BY '1234556';
mysql> flush privileges;
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 | 154 | | | |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
mysql> #File 列显示日志名,Position 列显示偏移量
②、从服务器的 mysql 配置
##############selave1 #############
[root@slave1 ~]# vim /etc/my.cnf #添加如下内容
……
server-id = 2 #注意 id 与其他主机都不能相同
relay-log=relay-log-bin #开启中继日志,从主服务器上同步日志文件记录到本地
relay-log-index=slave-relay-bin.index #定义中继日志文件的位置和名称
relay_log_recovery = 1 #选配项
#当 slave 从库宕机后,假如 relay-log 损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的
relay-log,并且重新从 master 上获取日志,这样就保证了relay-log 的完整性。默认情况下该功能是关闭
的,将 relay_log_recovery 的值设置为 1 时, 可在 slave 从库上开启该功能,建议开启。
#配置同步,注意 master_log_file 和 master_log_pos 的值要与 Master 查询的一致
mysql> CHANGE master to master_host='192.168.110.128',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=603;
mysql> start slave; #启动同步,如有报错执行 reset slave;
############## selave2#############
[root@slave1 ~]# vim /etc/my.cnf #添加如下内容
……
server-id = 3
relay-log=relay-log-bin
relay-log-index=slave-relay-bin.index
relay_log_recovery = 1
mysql> CHANGE master to master_host='192.168.110.128',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=603;
mysql> start slave;
############## 查看 Slave 状态 #############
mysql> show slave status\G;
#确保 IO 和 SQL 线程都是 Yes,代表同步正常。
注意:一般 Slave_IO_Running: No 的可能性
①、网络不通
②、my.cnf配置有问题
③、密码、file文件名、pos偏移量不对
④、防火墙没有关闭
3、验证主从复制
三、读写分离介绍
1、基本概念
在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求
的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此,通过
主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。有点类似于rsync,
但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据、语句做备份。
①、读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、
DELETE),即只在主服务器上写,只在从服务器上读。而从数据库处理SELECT查询操作。数据
库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
因为数据库的“写”(写10000条数据可能要3分钟)操作是比较耗时的。
但是数据库的“读”(读10000条数据可能只要5秒钟)。
所以读写分离,解决的是,数据库的写入,影响了查询的效率。
③、读写分离作用
数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用。
利用数据库主从同步,再通过读写分离可以分担数据库压力,提高性能。
2、实现方式
读写分离有两种实现方式:基于程序代码内部实现 和 基于中间代理层实现。
①、基于程序代码内部实现
在代码中根据 select、insert,进行路由分类,这类方法也是目前生产环境应用最广泛的。
优点是性能较好,因为在程序代码中实现,不需要增加额外的设备为硬件开支; 缺点是需要开发人
员来实现,运维人员无从下手。
但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的 Java 应用,如果在
程序代码中实现读写分离对代码改动就较大。
②、基于中间代理层实现
代理一般位于客户端和服务器之间, 代理服务器接到客户端请求后通过判断后转发到后端数据
库, 有以下代表性程序:
◆ MySQL-Proxy :MySQL-Proxy 为 MySOL开源项目, 通过其自带的 lua 脚本进行SOL 判断。
◆ Atlas:是由奇虎360的Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项
目。它是在mysql-proxy0.8.2版本的基础上,对其进行了优化, 增加了一些新的功能特性。360内
部使用Atlas运行的mysql业务,每天承载的读写请求数达几十亿条。支持事物以 及存储过程。
◆ Amoeba:由陈思儒开发,作者曾就职于阿里巴巴。该程序由Jaya语言进行开发,阿里巴巴将其
用于生产环境。但是它不支持事务和存储过程。
由于使用 MySQL Proxy 需要写大量的 Lua 脚本,这些 Lua 并不是现成的,而是需要自己去写。
这对于并不熟悉 MySQL Proxy 内置变量和 MySQL Protocol 的人来说是非常困难的。Amoeba是
一个非常容易使用、可移植性非常强的软件。因此它在生产环境中被广泛应用于数据库的代理层。
四、MySQL 读写分离架构搭建
1、前期准备
①、安装Java 环境。因为 amoeba 是基于 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,
高版本不建议使用。
[root@amoeba opt]# ls /opt/ #在opt目录下准备amoeba和jdk的安装包
amoeba-mysql-binary-2.2.0.tar.gz
jdk-6u14-linux-x64.bin
mysql-5.7.17
rh
[root@amoeba opt]# cp jdk-6u14-linux-x64.bin /usr/local/
[root@amoeba opt]# cd /usr/local/
[root@amoeba opt]# chmod +x jdk-6u14-linux-x64.bin #赋予执行权限
[root@amoeba opt]# ./jdk-6u14-linux-x64.bin #执行
#按住Enter,提示输入YES等待完成并回车
[root@amoeba local]# mv jdk1.6.0_14/ /usr/local/jdk1.6
#编辑全局配置文件,在最后一行添加以下配置
export JAVA_HOME=/usr/local/jdk1.6 #输出定义Java的工作目录
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib #输出指定的java类型
export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin#将java加入路径环境变量
export AMOEBA_HOME=/usr/local/amoeba #输出定义amoeba工作目录
export PATH=$PATH:$AMOEBA_HOME/bin #加入路径环境变量
[root@amoeba ~]# source /etc/profile #刷新文件
[root@amoeba ~]# java -version #查看java版本信息以检查是否安装成功
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)
[root@amoeba ~]#
②、安装 Amoeba
[root@amoeba ~]# mkdir /usr/local/amoeba
[root@amoeba ~]# tar zxvf /opt/amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
[root@amoeba ~]# chmod -R 755 /usr/local/amoeba/ #为目录赋予权限
[root@amoeba ~]# /usr/local/amoeba/bin/amoeba #开启amoeba
amoeba start|stop
[root@amoeba ~]# #如显示amoeba start|stop说明安装成功
2、配置 amoeba 读写分离,之前两个 slave做读负载均衡
#首先、在master、slave1、slave2 的 mysql上开放权限给 amoeba 访问
mysql> grant all on *.* to test@'192.168.110.%' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql>
#接着、回到amoeba 服务器配置 amoeba 服务
[root@amoeba ~]# cd /usr/local/amoeba/conf/
[root@amoeba conf]# cp amoeba.xml amoeba.xml.bak #先备份配置文件
[root@amoeba conf]# cp dbServers.xml dbServers.xml.bak
[root@amoeba conf]# cd
[root@amoeba ~]# vim /usr/local/amoeba/conf/amoeba.xml #修改amoeba配置文件
……
#--30行--
<property name="user">amoeba</property>
#--32行--
<property name="password">123456</property>
#--115行--
<property name="defaultPool">master</property>
#--117行-120行,取消注释并修改
<property name="writePool">master</property>
<property name="readPool">slaves</property>
……
[root@amoeba ~]# vim /usr/local/amoeba/conf/dbServers.xml #修改数据库配置文件
……
#--23行--注释掉 作用:默认进入 test 库,以防 mysql 中没有 test 库时会报错
<!-- <property name="schema">test</property> -->
#--26--修改,使用之前创建的授权用户
<property name="user">test</property>
#--29--去掉注释,密码为之前创建的授权用户密码
<property name="password">123456</property>
#--45--修改,设置主服务器的名 master
<dbServer name="master" parent="abstractServer">
#--48--修改,设置主服务器的地址
<property name="ipAddress">192.168.10.20</property>
#--52--修改,设置从服务器的名 slave1
<dbServer name="slave1" parent="abstractServer">
#--55--修改,设置从服务器1的地址
<property name="ipAddress">192.168.10.30</property>
#--58--复制上面 6 行粘贴,设置从服务器 2 的名 slave2 和地址
<dbServer name="slave2" parent="abstractServer">
<property name="ipAddress">192.168.10.40</property>
#--65行--修改
<dbServer name="slaves" virtual="true">
#--71行--修改
<property name="poolNames">slave1,slave2</property>
......
[root@amoeba ~]# /usr/local/amoeba/bin/amoeba start & # #启动 amoeba 软件,按 ctrl+c 返回
[root@amoeba ~]# netstat -anpt | grep java #查看 8066 端口是否开启,默认端口为 TCP 8066
3、客户端测试读写分离
###################客户端验证,修改数据库###########
[root@localhost ~]# yum install -y mariadb-server mariadb #客户端安装
[root@localhost ~]# systemctl start mariadb.service
[root@localhost ~]# mysql -u amoeba -p123456 -h 192.168.110.128 -P8066 #通过中继服务器访问数据库
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 696318316
……
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.01 sec)
MySQL [(none)]> use test;
MySQL [(none)]> create table test(id int,name char(20));
MySQL [test]> create table test001(id int,name char(20));
MySQL [test]> show tables;
+----------------+
| Tables_in_test |
+----------------+
| test |
| test001 |
+----------------+
2 rows in set (0.00 sec)
MySQL [test]>
#################master查看#####################
mysql> use test;
……
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| test |
| test001 |
+----------------+
2 rows in set (0.00 sec)
mysql> #可以看到客户端添加的表,在主服务器上面都可以看到
#################slave1和slave2查看#####################
#显示结果与主服务器相同,可以看到客户端中做出的修改,在所有服务器上都能看到。
4、关闭 slave 功能后在主从服务器插入数据
从服务器关闭从服务功能,并添加记录,
主服务器添加记录
客户端验证
结论:客户端在 slave1、slave2 中轮询查看数据。由于从服务器都关闭了 slave 功能,因此无法
查看 master 服务器中的修改。
5、关闭 slave 功能后,客户端服务器写入数据
紧接着上述做,现在在客户端进行修改数据
在master服务器查看
在两台从服务器查看表内容
结论:客户端服务器的修改操作是对于 master 服务器进行的,因此 master 服务器上可以看到客
户端服务器所做的 insert 操作。由于客户端服务器的 select 操作是对从服务器进行的,而从服务
器关闭了 slave 功能,无法获取更新,因此客户端本身以及从服务器都无法查看更新操作。
6、从服务器开启 slave 服务
首先,开启从服务,并查看数据是否同步
接着,在master主服务器查看
客户端查看表内容变化
结论:从服务器开启 slave 服务后可以从 master 服务器上获取更新,但是从服务器上的更新不会
被 master 服务器更新。客户端可以看到从服务器内容更新,但是是以轮询方式显示。
7、客户端服务器再次插入数据
客户端插入新数据
master主服务器查看表内容
两台slave从服务器
两台slave从服务器
结论:客户端服务器上的数据修改,会同步到所有服务器中。
总结
MySQL主服务器在用户增删改查提交事务的以后,会按顺序写入到binlog中,从服务器开启了 I/O
线程并请求binlog的内容,当binlog日志发生更新时,dump线程读取内容并推送给I/O线程。
从服务器连接主服务器数据库,I/O线程接收到binlog日志内容后会把数据放入本地中继日志中,
SQL线程会读取中继日志中的内容,并根据当中的内容对服务器进行操作。