数据库---MySQL主从复制与读写分离如何部署

1.案例分析

1.1 案例概述

  • 在企业应用中,成熟的业务通常数据量都比较大
  • 单台MySQL在安全性、高可用性和高并发方面都无法满足实际的需求
  • 配置多台主从数据库服务器以实现读写分离

1.2 案例前置知识点

1.2.1 MySQL主从复制原理

  • MySQL的复制类型:
基于语句的复制:在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。

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

混合类型的复制:默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。
  • MySQL主从复制的工作过程

在这里插入图片描述

复制的基本过程如下:
1.Master将用户对数据库更新的操作以二进制格式保存到 Binary Log 日志文件中;

2.Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;

3.Master接收到来自Slave的IO进程的请求后,通过负责复制的IO进程根据请求信息读取制定日志指定位置之后的日志信息,
返回给Slave的IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的
名称以及bin-log的位置;

4.Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master
端的bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清楚的告诉Master "我需要从某个
bin-log的哪个位置开始往后的日志内容,请发给我";

5.Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些
可执行的内容,并在自身执行。

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

1.2.2 MySQL读写分离原理

  • 只在主服务器上写,只在从服务器上读
  • 主数据库处理事务性查询,从数据库处理SELECT查询
  • 数据库复制用于将事务性查询的变更同步到集群中的从数据库
  • 读写分离方案
基于程序代码内部实现

基于中间代理层实现:代理一般位于客户端与服务器之间,代理服务器接到客户端请求后通过判断转发到后端数据库,
有两个代表性程序 "MySQL-Proxy  Amoeba"
  • 读写分离过程
    在这里插入图片描述

1.3 案例环境

主机 操作系统 IP地址 主要软件
Master CentOS-7-x86_64 192.168.140.20 cmake-2.8.6.tar/mysql-boost-5.7.20.tar
Slaverl CentOS-7-x86_64 192.168.140.21 cmake-2.8.6.tar/mysql-boost-5.7.20.tar
Slaver2 CentOS-7-x86_64 192.168.140.22 cmake-2.8.6.tar/mysql-boost-5.7.20.tar
Amoeba CentOS-7-x86_64 192.168.140.14 jdk-8u91-linux-x64.tar/amoeba-mysql-3.0.5-RC-distribution
客户端 CentOS-7-x86_64 192.168.140.13

2.案例实施

2.1 搭建MySQL主从复制

2.1.1 建立时间同步环境

  • 确保在能上网的环境下
    在这里插入图片描述
  • 主节点上搭建时间同步服务器
[root@master ~]# rpm -qa | grep ntpdate		'//查询软件包是否安装'
ntpdate-4.2.6p5-25.el7.centos.2.x86_64

[root@master ~]# yum -y install ntp ntpdate		'//确认已安装'
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: 
软件包 ntp-4.2.6p5-25.el7.centos.2.x86_64 已安装并且是最新版本
软件包 ntpdate-4.2.6p5-25.el7.centos.2.x86_64 已安装并且是最新版本
无须任何处理

[root@master ~]# ntpdate ntp.aliyun.com
28 Dec 14:01:33 ntpdate[73315]: adjust time server 203.107.6.88 offset 0.391783 sec
[root@master ~]# vi /etc/ntp.conf
...
restrict 192.168.140.0 mask 255.255.255. 0 nomodify notrap	//修改第17行设置自己的网段
//删除21-24行,添加以下配置
server 127.127.1.0
fudge 127.127.1.0 stratum 8 //设置时间服务器的层级为8级,顶级是0

[root@master ~]# systemctl restart ntpd
[root@master ~]# netstat -anptu | grep ntpd
udp        0      0 192.168.122.1:123       0.0.0.0:*                           73375/ntpd          
udp        0      0 192.168.140.20:123      0.0.0.0:*                           73375/ntpd          
udp        0      0 127.0.0.1:123           0.0.0.0:*                           73375/ntpd          
udp        0      0 0.0.0.0:123             0.0.0.0:*                           73375/ntpd          
udp6       0      0 ::1:123                 :::*                                73375/ntpd          
udp6       0      0 fe80::a5ca:bfa5:47e:123 :::*                                73375/ntpd          
udp6       0      0 :::123                  :::*                                73375/ntpd 
  • 从服务器上
[root@slaver1 ~]# rpm -qa | grep ntpdate
ntpdate-4.2.6p5-25.el7.centos.2.x86_64
[root@slaver1 ~]# ntpdate 192.168.140.20
28 Dec 14:09:06 ntpdate[74804]: adjust time server 192.168.140.20 offset 0.223987 sec

[root@slaver2 ~]# rpm -qa | grep ntpdate
ntpdate-4.2.6p5-25.el7.centos.2.x86_64
[root@slaver2 ~]# ntpdate 192.168.140.20
28 Dec 14:09:13 ntpdate[72436]: step time server 192.168.140.20 offset 1245572.722417 sec
  • 为了保证ntp时间同步时刻保持同步,需要使用crontab计划任务
[root@master ~]# which ntpdate
/usr/sbin/ntpdate
[root@master ~]# crontab -e
*/30 * * * * /usr/sbin/ntpdate ntp.aliyun.com

[root@slaver1 ~]# crontab -e
*/3 * * * * /usr/sbin/ntpdate 192.168.140.20

[root@slaver2 ~]# crontab -e
*/3 * * * * /usr/sbin/ntpdate 192.168.140.20

发送键入所有会话
先关闭防火墙
systemctl stop firewalld
setenforce 0
[root@master ~]# date
20201228日 星期一 14:15:03 CST
[root@slaver1 ~]# date
20201228日 星期一 14:15:03 CST
[root@slaver2 ~]# date
20201228日 星期一 14:15:03 CST
  • 此刻表示时间同步搭建完成

2.1.2 配置master主服务器

[root@master ~]# vi /etc/my.cnf 	'//在mysqld下面配置'
...
server_id =1
log_bin=master_bin		'//设置二进制日志的抬头'
log_slave_updates=true	'//允许从库更新'

[root@master ~]# systemctl restart mysqld.service
[root@master ~]# mysqladmin -uroot password 'abc123'
[root@master ~]# mysql -uroot -pabc123
...
mysql> grant replication slave on *.* to 'myslave'@'192.168.140.%' identified by 'abc123';
mysql> flush privileges;
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master_bin.000001 |      864 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

在这里插入图片描述

2.1.3 配置slaver从服务器

  • 从slaver1上的配置
[root@slaver1 ~]# vi /etc/my.cnf 
...
server_id = 2				'//该值不能有重复'
relay_log=relay-log-bin		'//设置中继日志的抬头'
relay_log_index=slave-relay-bin.index		'//设置索引'

[root@master ~]# systemctl restart mysqld.service
[root@slaver1 ~]# mysqladmin -uroot password 'abc123'
[root@slaver1 ~]# mysql -uroot -pabc123
...
mysql> change master to master_host='192.168.140.20',master_user='myslave',master_password='abc123',master_log_file='master_bin.000001',master_log_pos=864;
mysql> start slave;
mysql> show slave status \G

在这里插入图片描述

  • 从slaver2上的配置
修改server-id其余配置与1上一样
最后查看IO和SQL线程是否为YES

[root@slaver2 ~]# vi /etc/my.cnf 
...
server_id = 3

在这里插入图片描述

2.1.4 验证主从复制

  • 在主服务器上
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

mysql> create database test;		'//主服务器上创建好数据库,从服务器上查看'
  • 从服务器上
[root@slave1 ~]# mysql -uroot -pabc123
...
mysql> show databases;

在这里插入图片描述
在这里插入图片描述

2.2 搭建MySQL读写分离

2.2.1 在前端服务器上搭建NTP时间同步

[root@amoeba ~]# ntpdate 192.168.140.20
28 Dec 15:23:42 ntpdate[55032]: adjust time server 192.168.140.20 offset 0.454341 sec
[root@amoeba ~]# crontab -e
*/3 * * * * /usr/sbin/ntpdate 192.168.140.20

2.2.2 安装JDK环境

  • 导入软件包
    在这里插入图片描述
[root@amoeba ~]# tar zxvf jdk-8u91-linux-x64.tar.gz		//解压
[root@amoeba ~]# cp -rv jdk1.8.0_91/ /usr/local/java	//将加压后的文件夹复制到java目录下
[root@amoeba ~]# vi /etc/profile		//设置环境变量
...//末尾添加以下配置
export JAVA_HOME=/usr/local/java
export JRE_HOME=/usr/local/java/jre
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export CLASSPATH=./:$JAVA_HOME/lib:$JRE_HOME/lib
[root@amoeba ~]# source /etc/profile		//让环境变量立即生效
[root@amoeba ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/java/bin:/usr/local/java/jre/bin
[root@amoeba ~]# java -version
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-b12)
OpenJDK 64-Bit Server VM (build 25.131-b12, mixed mode)

2.2.3 配置amoeba代理服务器

  • 导入软件包
    在这里插入图片描述
[root@amoeba ~]# unzip amoeba-mysql-3.0.5-RC-distribution.zip
[root@amoeba ~]# mv amoeba-mysql-3.0.5-RC/ /usr/local/amoeba
[root@amoeba ~]# chmod -R 755 /usr/local/amoeba/
[root@amoeba ~]# vi /usr/local/amoeba/jvm.properties
//注释掉第32行,并添加以下配置
#JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss196k -XX:PermSize=16m -XX:MaxPermSize=96m"
JVM_OPTIONS="-server -Xms1024m -Xmx1024m -Xss256k"
  • 设置启动脚本
[root@amoeba ~]# vi /etc/init.d/amoeba
#!/bin/bash
#chkconfig: 35 20 90
export JAVA_HOME=/usr/local/java
export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin
NAME=Amoeba
AMOEBA_BIN=/usr/local/amoeba/bin/launcher
SHUTDOWN_BIN=/usr/local/amoeba/bin/shutdown
PIDFILE=/usr/local/amoeba/Amoeba-MySQL.pid
SCRIPTNAME=/etc/init.d/amoeba
case "$1" in
start)
echo -n "Starting $NAME..."
$AMOEBA_BIN
echo "done"
;;
stop)
echo -n "Stopping $NAME..."
$SHUTDOWN_BIN
echo "done"
;;
restart)
$SHUTDOWN_BIN
sleep 1
$AMOEBA_BIN
;;
*)
echo "Usage:$SCRIPTNAME {start|stop|restart}"
exit 1
;;
esac
  • 设置启动权限,并启动服务
[root@amoeba ~]# chmod 755 /etc/init.d/amoeba
[root@amoeba ~]# chkconfig --add amoeba
[root@amoeba ~]# chkconfig --list

注:该输出结果只显示 SysV 服务,并不包含
原生 systemd 服务。SysV 配置数据
可能被原生 systemd 配置覆盖。 

      要列出 systemd 服务,请执行 'systemctl list-unit-files'。
      查看在具体 target 启用的服务请执行
      'systemctl list-dependencies [target]'。

amoeba         	0:1:2:3:4:5:6:关
netconsole     	0:1:2:3:4:5:6:关
network        	0:1:2:3:4:5:6:[root@amoeba ~]# systemctl start amoeba		//若启动失败可用脚本启动,service amoeba start
[root@amoeba ~]# netstat -anpt | grep 8066
tcp6       0      0 :::8066                 :::*                    LISTEN      56062/java
  • 在三台服务器上为amoeba授权(master和slave服务器上)
mysql> grant all privileges on *.* to 'test'@'192.168.140.%' identified by '123.com';
mysql> flush privileges; 
  • 修改amoeba配置文件
[root@amoeba ~]# cd /usr/local/amoeba
[root@amoeba amoeba]# vi conf/amoeba.xml
...//第28行设置客户端连接amoeba
<property name="user">amoeba</property>
<property name="password">123456</property>
...
//去掉83行注释内容,修改以下配置。将默认的server1改成master,把默认的servers改成slaves
<property name="defaultPool">master</property>
<property name="writePool">master</property>
<property name="readPool">slaves</property>

[root@amoeba amoeba]# vi conf/dbServers.xml
...//修改第26行开始的两行配置,设置授权账户和密码
<property name="user">test</property>
<property name="password">123.com</property>
...
<dbServer name="master"  parent="abstractServer">		//第43行,设置为主服务器主机名
<property name="ipAddress">192.168.140.20</property>	//第46行,设置主服务器的IP
<dbServer name="slave1"  parent="abstractServer">		//第50行,设置为从服务器主机名
<property name="ipAddress">192.168.140.21</property>	//第53行,设置从服务器地址
//接着复制slave1模块,修改为slave2的主机名和地址
<dbServer name="slaves" virtual="true">		//第63行,设置修改为slaves
<property name="poolNames">slave1,slave2</property>		//第69行,设置为从服务器名

在这里插入图片描述

[root@amoeba ~]# service amoeba restart		//若启动失败可用脚本启动
[root@amoeba ~]# netstat -anpt | grep 8066
用脚本命令启动amoeba服务
[root@amoeba ~]# cd /usr/local/amoeba/
[root@amoeba amoeba]# cd bin/
[root@amoeba bin]# ll
总用量 32
-rwxr-xr-x. 1 root root 2071 75 2013 benchmark
-rwxr-xr-x. 1 root root 1452 1229 2012 benchmark.bat
-rwxr-xr-x. 1 root root  145 1227 2012 benchmark.classpath
-rwxr-xr-x. 1 root root 2114 75 2013 launcher
-rwxr-xr-x. 1 root root 1579 1227 2012 launcher.bat
-rwxr-xr-x. 1 root root  134 1223 2012 launcher.classpath
-rwxr-xr-x. 1 root root 1032 75 2013 mkdirhier
-rwxr-xr-x. 1 root root 1615 75 2013 shutdown
[root@amoeba bin]# ./shutdown		//或者用/usr/local/amoeba/bin/shutdown停止服务
[root@amoeba bin]# ./launcher

在这里插入图片描述

  • 客户机上
[root@client ~]# systemctl stop firewalld
[root@client ~]# setenforce 0
[root@client ~]# yum -y install mariadb*
[root@client ~]# systemctl start mariadb.service
[root@client ~]# mysql -uamoeba -p123456 -h 192.168.140.14 -P8066		//通过代理访问mysql

在这里插入图片描述

2.2.4 验证结果

  • 在客户机上写入数据
MySQL [(none)]> use test;
Database changed
MySQL [test]> create table t1(id int(10),name varchar(64),address varchar(20));
Query OK, 0 rows affected (0.02 sec)

MySQL [test]> insert into t1 values(1,'luchen','This is Master');
Query OK, 1 row affected (0.01 sec)
  • 在主从服务器上查看结果
    在这里插入图片描述
    在这里插入图片描述
  • 当停掉两台slave从服务器后
mysql> stop slave;
在从slave1上删除原有数据,写入新的数据

mysql> delete from test.t1;		//或者用 truncate test.t1 也能删除
mysql> use test;
mysql> insert into t1 values(5,'zuoyou','This is Slave1');
mysql> select * from test.t1;
+------+--------+----------------+
| id   | name   | address        |
+------+--------+----------------+
|    5 | zuoyou | This is Slave1 |
+------+--------+----------------+
1 row in set (0.00 sec)
在从slave2上删除原有数据,写入新的数据

mysql> truncate test.t1;
mysql> use test;
mysql> insert into t1 values(10,'aliang','This is Slave2');
Query OK, 1 row affected (0.01 sec)

mysql> select * from test.t1;
+------+--------+----------------+
| id   | name   | address        |
+------+--------+----------------+
|   10 | aliang | This is Slave2 |
+------+--------+----------------+
1 row in set (0.00 sec)
在客户机上查询test.t1表,可以轮询查看slave1和slave2上表的内容

MySQL [test]> select * from test.t1;
+------+--------+----------------+
| id   | name   | address        |
+------+--------+----------------+
|    5 | zuoyou | This is Slave1 |
+------+--------+----------------+
1 row in set (0.01 sec)

MySQL [test]> select * from test.t1;
+------+--------+----------------+
| id   | name   | address        |
+------+--------+----------------+
|   10 | aliang | This is Slave2 |
+------+--------+----------------+
1 row in set (0.00 sec)

结论:amoeba能读取slave从服务器上写入的数据,具有读的功能

在这里插入图片描述

  • 当在amoeba代理服务器上写入数据时
MySQL [test]> insert into t1 values(17,'forlo','This is Master2');

在master主服务器上查看发现有数据写入,从slave服务器上并没有数据写入
mysql> select * from test.t1;
+------+--------+-----------------+
| id   | name   | address         |
+------+--------+-----------------+
|    1 | luchen | This is Master  |
|   17 | forlo  | This is Master2 |
+------+--------+-----------------+
2 rows in set (0.00 sec)

结论:ameoba代理服务器写入的内容会同步到master主服务器,但不会同步到slave从服务器上;
同时master写入的数据,ameoba代理服务器也能读取到。
  • 此时表示,MySQL读写分离搭建完成。

补充

在主服务器上查看状态发现position改变

mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master_bin.000001 |     2232 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

结论,当我们在写入数据时,发现位置会更改,再次设置主从复制时,需要修改pos的值为当前的值。

猜你喜欢

转载自blog.csdn.net/weixin_42449832/article/details/112167241
今日推荐