MySQL主从复制原理

MySQL的主从复制和MySQL的读写分离两者有着紧密联系,首先要部署主从复制,只有主从复制完成了,才能在此基础上进行数据的读写分离。

(1)MySQL支持复制的类型。

1)基于语句的复制。MySQL默认采用基于语句的复制,效率比较高。

2)基于行的复制。把改变的内容复制过去,而不是把命令在从服务器上执行一遍。

3)混合类型的复制。默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。

(2)MySQL复制的工作过程如图所示。
在CentOS7上搭建MySQL主从复制与读写分离
1)在每个事务更新数据完成之前,Master在二进制日志记录这些改变。写入二进制日志完成后,Master通知存储引擎提交事务。

2)Slave将Master的Binary log复制到其中继日志。首先,Slave开始一个工作线程——I/O线程,I/O线程在Master上打开一个普通的链接,然后开始Binlog dump process。Binlog dump process从Master的二进制日志中读取事件,如果已经跟上Master,它会睡眠并等待Master产生新的事件。I/O线程将这些事件写入中继日志。

3)SQL slave thred(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新Slave的数据,使其与Master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。

复制过程中有一个很重要的限制,即复制在Slave上是串行化的,也就是说Master上的并行更新操作不能在Slave上并行操作。

MySQL读写分离原理

简单来说,读写分离(见图所示)就是只在主服务器上写,只在从服务器上读。基本原理是让主数据库处理事务性查询,而从数据库处理select查询。数据库复制被用来把事务性查询导致的变更同步到群集中的从数据库。
在CentOS7上搭建MySQL主从复制与读写分离
基于中间代理层实现:代理一般位于客户端和服务器之间,代理服务器接到客户端请求通过判断后转发到后端数据库。

实验环境

使用五台服务器模拟搭建,具体的拓扑如图所示:
在CentOS7上搭建MySQL主从复制与读写分离

实验环境表:
在CentOS7上搭建MySQL主从复制与读写分离
实施步骤

1.搭建MySQL主从复制。

(1)建立时间同步环境,在主节点上搭建时间同步服务器。

1)安装NTP。

[root@localhost ~]# yum install ntp -y

2)配置NTP。

[root@localhost ~]# vim /etc/ntp.conf 
server 127.127.126.0                    //本地是时钟源//
fudge 127.127.126.0 stratum 8          //设置时间层级为8(限制在15内)//

3)重启服务。

[root@localhost ~]# systemctl restart ntpd.service

(2)在从节点服务器上进行时间同步。

[root@localhost ~]# yum install ntpdate -y
[root@localhost ~]# /usr/sbin/ntpdate 192.168.126.138  //同步主服务器的时间//

(3)在每台服务器上关闭firewalld防火墙。

[root@localhost ~]# systemctl stop firewalld.service   //关闭防火墙//
[root@localhost ~]# setenforce 0

(4)安装MySQL数据库。在Master、Slave1、Slave2上安装,我用的数据库是MySQL5.7.17这里安装完毕不再演示。

(5)配置MySQL Master主服务器。

1)在/etc/my.cnf中修改或者增加以下内容。

[root@localhost mysql]# vim /etc/my.cnf
server-id = 11
log-bin=master-bin             //主服务器日志文件//
log-slave-updates=true      //从服务器更新二进制日志//

2)重启MySQL服务。

[root@localhost ~]# systemctl restart mysqld.service

3)登录MySQL程序,给服务器授权。

[root@localhost ~]# mysql -uroot -p
mysql> GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.126.%' IDENTIFIED BY '123456';      //授权//
mysql> FLUSH PRIVILEGES;
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 |      604 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
其中File列显示日志名,position列显示偏移量。

(6)配置从服务器。
1)在/etc/my.cnf中修改或者增加以下内容。

[root@localhost ~]# vim /etc/my.cnf
server-id = 22
relay-log=relay-log-bin             //从主服务器上同步日志文件记录到本地//
relay-log-index=slave-relay-bin.index   //定义relay-log的位置和名称//

这里要注意server-id不能与主服务器相同。

2)重启mysql服务。

[root@localhost ~]# systemctl restart mysqld.service 

3)登录mysql,配置同步。

按主服务器结果更改下面命令中的master_log_file和master_log_pos的参数。

[root@localhost ~]# mysql -u root -p
mysql> change master to master_host='192.168.126.138',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=604;

4)启动同步。

mysql> start slave;

5)查看Slave状态,确保以下两个值为YES。

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.126.138
                  Master_User: myslave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000001
          Read_Master_Log_Pos: 604
               Relay_Log_File: relay-log-bin.000002
                Relay_Log_Pos: 321
        Relay_Master_Log_File: master-bin.000001
             Slave_IO_Running: Yes           
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 

(7)验证主从复制效果。

1)在主服务器上新建数据库 db_test。

mysql>  create database db_test;

2)在主、从服务器上分别查看数据库,显示数据库相同,则主从复制成功。

mysql> show databases;      //主服务器//
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db_test            |
| mysql              |
| performance_schema |
| school             |
| sys                |
+--------------------+
6 rows in set (0.03 sec)

mysql> show databases;    //从服务器//
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db_test            |
| kgc                |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
6 rows in set (0.05 sec)

搭建MySQL读写分离

Amoeba(变形虫),这个软件致力于MySQL的分布式数据库前端代理层,它主要为应用层访问MySQL时充当SQL路由,并具有负载均衡、高可用性、SQL过滤、读写分离、可路由相关到目标数据库、可并发请求多台数据库。通过Amoeba能够完成多数据源的高可用、负载均衡、数据切片的功能。

(1)在主机Amoeba上安装Java环境。

Amoeba是基于jdk1.5开发的,所以官方推荐使用jdk1.5或1.6版本,高版本不建议使用。

[root@localhost ~]# systemctl stop firewalld.service 
[root@localhost ~]# setenforce 0
[root@localhost tomcat]# cp jdk-6u14-linux-x64.bin /usr/local/
[root@localhost local]# ./jdk-6u14-linux-x64.bin    //根据提示按Enter键完成即可//
[root@localhost local]# mv jdk1.6.0_14/ /usr/local/jdk1.6
[root@localhost local]# 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

[root@localhost local]# source /etc/profile   //启动//

Java环境已配置成功。

(2)安装并配置Amoeba软件

[root@localhost local]# mkdir /usr/local/amoeba  创建工作路径//
[root@localhost tomcat]# tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba   //解压安装包//
[root@localhost tomcat]# chmod -R 755 /usr/local/amoeba/  //提示amoeba权限//
[root@localhost tomcat]# /usr/local/amoeba/bin/amoeba
amoeba start|stop   //显示此内容说明Amoeba安装成功//

(3)配置Amoeba读写分离,两个Slave读负载均衡。
1)Master、Slave1、Slave2中开放权限给Amoeba访问。

grant all on *.* to test@'192.168.126.%' identified by '123.com';

2)编辑amoeba.xml配置文件。

[root@localhost tomcat]# cd /usr/local/amoeba/
[root@localhost amoeba]# vim conf/amoeba.xml 
---30行--

 <property name="user">amoeba</property>
----32行---------
 <property name="password">123456</property>

---117-去掉注释-
 <property name="defaultPool">master</property>
 <property name="writePool">master</property>
 <property name="readPool">slaves</property>

3)编辑dbServers.xml配置文件。

vi conf/dbServers.xml

--26-29--去掉注释--      //行//
 <property name="user">test</property>    

 <property name="password">123.com</property>

-----42-主服务器地址---
<dbServer name="master"  parent="abstractServer">
 <property name="ipAddress">192.168.126.138</property>
--52-从服务器主机名-
<dbServer name="slave1"  parent="abstractServer">
--55-从服务器地址-
 <property name="ipAddress">192.168.126.162</property>
 从服务器slave2      
<dbServer name="slave2"  parent="abstractServer">      //添加//
 <property name="ipAddress">192.168.126.232</property>
 <dbServer name="slaves" virtual="true">
 <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">

--末尾--
<property name="poolNames">slave1,slave2</property>    //修改//
 </poolConfig>

4)配置无误后,可用启动Amoeba软件,其默认端口为TCP 8066。

[root@localhost amoeba]# /usr/local/amoeba/bin/amoeba start&
[root@localhost amoeba]#  netstat -anpt | grep java
tcp6       0      0 127.0.0.1:28750         :::*                    LISTEN      3370/java           
tcp6       0      0 :::8066                 :::*                    LISTEN      3370/java 
.....//省略//

(4) 测试
1)在client主机上。

[root@localhost ~]# yum install mysql -y

可以通过代理访问MySQL:

[root@localhost ~]# mysql -u amoeba -p123456 -h 192.168.126.132 -P8066
..... //省略//
MySQL [(none)]>

2)在Master上创建一个表,同步到各从服务器上,然后关闭各从服务器的Slave功能,再插入区别语句。

mysql> use db_test;
Database changed
mysql> create table zang (id int(10),name varchar(10),address varchar(20)); //创建表//
Query OK, 0 rows affected (0.06 sec)

分别在两台服务器上关闭slave功能:

mysql> stop slave;

然后在主服务器上插入区别语句:

mysql> insert into zang values('1','zhang','this_is_master');  //插入数据//

3)从服务器上手动插入其他内容。

slave1:
mysql> use db_test;
mysql> insert into zang values('2','zhang','this_is_slave1');   //插入数据//
Query OK, 1 row affected (0.03 sec)

slave2:
mysql> use db_test;
mysql> insert into zang values('3','zhang','this_is_slave2'); //插入数据//
Query OK, 1 row affected (0.03 sec)

4)测试读操作
在client主机上第一次查询的结果如下:

MySQL [db_test]> select * from zang;
+------+-------+----------------+
| id   | name  | address        |
+------+-------+----------------+
|    3 | zhang | this_is_slave2 |
+------+-------+----------------+
1 row in set (0.01 sec)

第二次查询的结果如下:

MySQL [db_test]> select * from zang;
+------+-------+----------------+
| id   | name  | address        |
+------+-------+----------------+
|    2 | zhang | this_is_slave1 |
+------+-------+----------------+
1 row in set (0.01 sec)

第三次查询结果:

MySQL [db_test]> select * from zang;
+------+-------+----------------+
| id   | name  | address        |
+------+-------+----------------+
|    3 | zhang | this_is_slave2 |
+------+-------+----------------+
1 row in set (0.01 sec)

5)测试写操作。
在client主机上插入一条语句:

MySQL [db_test]> insert into zang values('5','zhang','write_test');
Query OK, 1 row affected (0.02 sec)

但在client上查询不到,最终只有在Master上才能查看到这条语句内容,说明写的操作在Master服务器上。

mysql> select * from zang;
+------+-------+----------------+
| id   | name  | address        |
+------+-------+----------------+
|    1 | zhang | this_is_master |
|    5 | zhang | write_test     |
+------+-------+----------------+
2 rows in set (0.01 sec)

由此验证,已经实现了MySQL读写分离,目前所有的写操作全部在Master主服务器上,用来避免数据 的不同步;所有的读操作都分摊给了Slave从服务器,用来分担数据库的压力。