MySQL主从复制与读写分离

125 阅读10分钟

一、读写分离概述

读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

1.1、为何需要读写分离

因为数据库的“写”(写10000条数据可能要3分钟)操作是比较耗时的。 但是数据库的“读”(读10000条数据可能只要5秒钟)。 所以读写分离,解决的是,数据库的写入,影响了查询的效率。

1.2、什么时候要读写分离?

数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用。利用数据库主从同步,再通过读写分离可以分担数据库压力,提高性能。

1.3、主从复制与读写分离

在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。有点类似于rsync,但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据、语句做备份。

1.4、mysql支持的复制类型

  1. STATEMENT:基于语句的复制。在服务器上执行sql语句,在从服务器上执行同样的语句,mysql默认采用基于语句的复制,执行效率高。
  2. ROW:基于行的复制。把改变的内容复制过去,而不是把命令在从服务器上执行一遍。
  3. MIXED:混合类型的复制。默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。

1.5、主从复制的工作过程

  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上并行操作。

1.6、MySQL 读写分离原理

读写分离就是只在主服务器上写,只在从服务器上读。基本的原理是让主数据库处理事务性操作,而从数据库处理 select 查询。数据库复制被用来把主数据库上事务性操作导致的变更同步到集群中的从数据库。

1.7、目前较为常见的 MySQL 读写分离分为以下两种:

1.7.1、基于程序代码内部实现

在代码中根据 select、insert 进行路由分类,这类方法也是目前生产环境应用最广泛的。 优点是性能较好,因为在程序代码中实现,不需要增加额外的设备为硬件开支;缺点是需要开发人员来实现,运维人员无从下手。 但是并不是所有的应用都适合在程序代码中实现读写分离,像一些大型复杂的Java应用,如果在程序代码中实现读写分离对代码改动就较大。

1.7.2、基于中间代理层实现

代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有以下代表性程序。

  1. MySQL-Proxy。MySQL-Proxy 为 MySQL 开源项目,通过其自带的 lua 脚本进行SQL 判断。
  2. Atlas。是由奇虎360的Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它是在mysql-proxy 0.8.2版本的基础上,对其进行了优化,增加了一些新的功能特性。360内部使用Atlas运行的mysql业务,每天承载的读写请求数达几十亿条。支持事物以及存储过程。
  3. Amoeba。由陈思儒开发,作者曾就职于阿里巴巴。该程序由Java语言进行开发,阿里巴巴将其用于生产环境。但是它不支持事务和存储过程。
  4. Mycat。是一款流行的基于Java语言编写的数据库中间件,是一个实现了MySql协议的服务器,其核心功能是分库分表。配合数据库的主从模式还可以实现读写分离。

由于使用MySQL Proxy 需要写大量的Lua脚本,这些Lua并不是现成的,而是需要自己去写。这对于并不熟悉MySQL Proxy 内置变量和MySQL Protocol 的人来说是非常困难的。 Amoeba是一个非常容易使用、可移植性非常强的软件。因此它在生产环境中被广泛应用于数据库的代理层。

二、搭建 MySQL 主从复制

环境搭建:
Master 服务器:192.168.80.10		mysql5.7
Slave1 服务器:192.168.80.11		mysql5.7
Slave2 服务器:192.168.80.12		mysql5.7
Amoeba 服务器:192.168.80.20		jdk1.6、Amoeba
客户端 服务器:192.168.80.30	        mysql

systemctl stop firewalld
systemctl disable firewalld
setenforce 0

2.1、MySQL主从服务器时间同步

## 主服务器设置 ##
yum install -y ntp
vim /etc/ntp.conf
  ## 在末尾添加下面两行 ##
  server 127.127.80.0           ##设置本地时钟源;127.127是固定格式,后面80是虚拟机网段,注意修改!
  fudge 127.127.80.0 stratum 8  ##设置时间层级为 8 (限制范围在15以内)

systemctl start ntpd    ##启动服务
## 从服务器设置 (两个从服务器执行相同操作)##
yum install -y ntp ntpdate

systemctl start ntpd
ntpdate 192.168.80.10   ##进行时间同步,ip是主服务器地址

crontab -e
 */30 * * * * /usr/sbin/ntpdate 192.168.80.10

2.2、主服务器的MySQL配置

vim /etc/my.cnf
 server-id = 1                ## 相当于集群id,此id唯一,不能跟其他MySQL服务器id冲突
 log-bin = mysql-bin          ## 主服务器开启二进制日志
 binlog_format = MIXED        ## 二进制日志保存格式:混合
 log-slave-updates = true     ## 允许slave从master复制数据时可以写入自己的二进制日志
 expire_logs_days = 7         ## 设置二进制日志文件过期时间,默认值为0,表示logs不过期
 max_binlog_size = 500M       ## 设置二进制日志限制大小,如果超出给定值,日志就会发生滚动,默认为1GB
 
systemctl restart mysqld

## 如果是yum安装的mysql,登录MySQL前,需要如下操作流程:
## grep password /var/log/mysqld.log    #查看默认密码
## mysql -u root -p'34/Jeoiq0fZb'       #登录MySQL,默认密码需使用单引号括起来
## set global validate_password_policy=low;  ## 降低密码等级
## set global validate_password_length=6;    ## 更改密码长度
## alter user  'root'@'localhost' identified by 'abc123';  ## 修改自己想要的root密码
## 然后重新登录,就可以正常授权了!
mysql -uroot -pabc123         ## 进入数据库
grant replication slave on *.* to 'myslave'@'192.168.80.%' identified by '123456';  ## 给从服务器授权

## replication slave:主从同步的权限
## *.* :所有库的所有表
## 'myslave'@'192.168.80.%':可以从"192.168.80.%" 这些主机登录的一个用户,名叫:myslave
## '123456':myslave用户的密码,从服务器可以使用myslave用户验证,并且只能从192.168.80网段上登录的才行

flush privileges;             ## 刷新权限

show master status;           ## 查看主服务器状态
## Position :偏移量,主从复制的依据
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 |      847 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

2.3、从服务器的MySQL配置(两个从服务器都需要配置,server-id 不能一样!)

vim /etc/my.cnf
 server-id = 2                ## 用于区分主从复制集群中的每个服务器,不同服务器id不能相同
 relay-log=relay-log-bin      ## 开启中继日志,从主服务器上同步日志文件到本地
 relay-log-index=slave-relay-bin.index  ## 定义中继日志文件的位置和名称,一般和relay-log在同一目录
 relay_log_recovery = 1       ## 当从库宕机后,假如relay-log损坏,则自动放弃所有未执行的relay-log,
                              ## 并重新从master上获取日志,保证relay-log的完整性,值=1,表示开启。
systemctl restart mysqld

## 如果是yum安装的mysql,登录MySQL前,需要如下操作流程:
## grep password /var/log/mysqld.log    #查看默认密码
## mysql -u root -p'34/Jeoiq0fZb'       #登录MySQL,默认密码需使用单引号括起来
## set global validate_password_policy=low;  ## 降低密码等级
## set global validate_password_length=6;    ## 更改密码长度
## alter user  'root'@'localhost' identified by 'abc123';  ## 修改自己想要的root密码
## 退出,重新登录

mysql -uroot -pabc123

change master to              ## 换行编写的话,to后面需要有空格
master_host='192.168.80.10',master_user='myslave',master_password='123456',master_log_file='mysql-bin.000001',
master_log_pos=847;              
                              ## 配置同步,注意 'master_log_file'、'master_log_pos'的值是自己
                              ## master 主服务器上查询的结果; master_user是主服务器授权时的用户名

start slave;                  ## 启动同步,如有报错执行 reset slave;
show slave status\G           ## 查看 slave 状态
## Slave_IO_Running:yes      ## 负责与主机的io通信
## Slave_SQL_Running:yes     ## 负责自己的slave mysql进程

三、搭建 MySQL读写分离

3.1、Amoeba服务器配置

##安装 Java 环境##
因为 Amoeba 基于是 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,高版本不建议使用。
cd /opt/            ## 将jdk-6u14-linux-x64.bin 软件包拖入此目录下
cp jdk-6u14-linux-x64.bin /usr/local/
cd /usr/local/
chmod +x jdk-6u14-linux-x64.bin
./jdk-6u14-linux-x64.bin
### 按空格键  一直回车,然后输入yes,最后回车

### 修改jdk的环境变量 ##
### mv jdk1.6.0_14/ /usr/local/jdk1.6

vim /etc/profile
 export JAVA_HOME=/usr/local/jdk1.6.0_14
 export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib   ## 编译环境
 export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
 export AMOEBA_HOME=/usr/local/amoeba
 export PATH=$PATH:$AMOEBA_HOME/bin

source /etc/profile
java -version

##安装 Amoeba软件##
cd /opt/         ## 将amoeba-mysql-binary-2.2.0.tar.gz 软件包拖入/opt目录下
mkdir /usr/local/amoeba
tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
chmod -R 755 /usr/local/amoeba/    ## 递归修改权限755
/usr/local/amoeba/bin/amoeba       ## 如显示amoeba start|stop说明安装成功

3.2、配置 Amoeba读写分离,两个 Slave 读的权限

## 先在Master、Slave1、Slave2 的mysql上开放权限给 Amoeba 访问 ##
grant all on *.* to 'amoeba'@'192.168.80.%' identified by 'abc123';

## 再回到amoeba服务器配置amoeba服务 ##
cd /usr/local/amoeba/conf/          ## amoeba配置文件

cp amoeba.xml amoeba.xml.bak        ## 备份
vim amoeba.xml									#修改amoeba服务配置文件
##30行##  修改登录amoeba的用户
<property name="user">zhangsan</property>
##32行##  修改登录用户密码
<property name="password">123123</property>
##115行## 设置默认服务器池
<property name="defaultPool">master</property>
##117-去掉注释- 并修改如下:用于写的服务器池,用于读的服务器池
<property name="writePool">master</property>
<property name="readPool">slaves</property>

cp dbServers.xml dbServers.xml.bak ## 备份
vim dbServers.xml                  ## 修改数据库服务配置

## 23行--注释掉  作用:默认进入test库 以防mysql中没有test库时,会报错 ##
<!-- <property name="schema">test</property> -->
## 26--修改 ##
<property name="user">amoeba</property>          ## 此用户就是之前在3台主从服务器上授权的用户
## 28-30--去掉注释 ##
<property name="password">abc123</property>      ## 授权用户的密码
## 45--修改,设置主服务器的名:Master ##
<dbServer name="master"  parent="abstractServer">
## 48--修改,设置主服务器的ip地址     ##
<property name="ipAddress">192.168.80.10</property>
## 52--修改,设置从服务器的名slave1 ##
<dbServer name="slave1"  parent="abstractServer">
## 55--修改,设置从服务器1的地址 ##
<property name="ipAddress">192.168.80.11</property>
## 52--6yy 复制6行,在57行p粘贴,设置从服务器2的名slave2和ip地址 ##
<dbServer name="slave2"  parent="abstractServer">
<property name="ipAddress">192.168.80.12</property>
## 66行--修改 ##  
<dbServer name="slaves" virtual="true">
## 72行--修改 ##
<property name="poolNames">slave1,slave2</property>

/usr/local/amoeba/bin/amoeba start &		  #启动Amoeba软件,按回车
netstat -anpt | grep java					  #查看8066端口是否开启,默认端口为TCP 8066