MySQL使用主从复制与读写分离的原因
1、在企业应用中,成熟的业务通常数据量都比较大
2、单台MySQL在安全性、高可用性和高并发方面都无法满足实际的需求
(myisam基于表级锁定;innodb基于行级锁定 --存储引擎不能满足并发读、写的需求)
3、配置多台主从数据库服务器以实现读写分离
MySQL主从复制原理
MySQL的复制类型
基于语句的复制
基于行的复制
混合类型的复制(语句、行、日志文件)
复制的基本过程
-
Master将用户对数据库更新的操作以二进制格式保存到Binary Log日志文件中
-
Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容;
IO进程:从数据库复制主数据库上二进制日志的进程
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的哪个位置开始往后的日志内容,请发给我”
- Slave的Sql进程检测到relay-log中新增加了内容后,会马上解析relay-log的内容成为在Master端真实执行时候的那些可执行的内容,并在自身执行.
Sql进程:将二进制日志内容翻译成SQL语句写进从数据库
MySQL读写分离原理
1、只在主服务器上写,只在从服务器上读
2、主数据库处理事务性查询,从数据库处理select查询
3、数据库复制用于将事务性查询的变更同步到集群中的从数据库
4、复制的基本过程如下:
Master将用户对数据库更新的操作以二进制格式保存到Binary Log日志文件中
Slave上面的IO进程连接上Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容
Master接收来自Slave的IO进程的请求后,通过负责复制的IO进程根据请求信息读取制定日志指定位置之后的日志信息,返回给Slave的IO进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到Master端的bin-log文件的名称以及bin-log的位置
Slave的IO进程接收到信息后,将接收到的日志内容依次添加到Slave端的relay-log文件的最末端,并将读取到的Master端的bin-log的文件名和位置记录到master-info文件中,以便在下一次读取的时候能够清除的告诉Master“我需要从某个bin-log的哪个位置开始往后的日志内容,请发给我”
SlaveSql进程检测到relay-log中新增了内容后,会马上解析relay-log的内容称为在Master端真实执行时候的那些可执行的内容,并在自身执行
项目
环境
mysql主数据:192.168.188.10
mysql从数据库:192.168.188.20 192.168.188.30
Amoeba:192.168.188.40
client:192.168.188.50
注:一定要关闭防火墙和核心防护
1、关闭防火墙和核心防护(所有设备)
[root@master ~]# systemctl stop firewalld
[root@master ~]# setenforce 0
setenforce: SELinux is disabled
[root@master ~]# sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
[root@master ~]# grep -v "#" /etc/selinux/config
SELINUX=disabled
SELINUXTYPE=targeted
2、设备主从时间同步 (主数据库)
[root@master ~]# yum -y install ntpdate ntp //安装ntp软件
[root@master ~]# ntpdate ntp.aliyun.com //时间同步
29 Dec 08:59:36 ntpdate[15322]: adjust time server 203.107.6.88 offset -0.003530 sec
[root@master ~]# date //查看当前时间
2020年 12月 29日 星期二 09:00:58 CST
[root@master ~]# vim /etc/ntp.conf
8 restrict default nomodify #定义默认访问规则,nomodify禁止远程主机修改本地服务器配置
17 #restrict 192.168.188.0 mask 255.255.255.0 nomodify notrap #从192.168.188.1到192.168.188.254主机都可以设用ntp时间同步
21 fudge 127.127.1.0 stratum 10 #时间同步为10级 ,是向其他服务器提供时间同步的同步源 ,最好不要设置0级
22 server 127.127.1.0 #设置本机为同步源
[root@master ~]# systemctl restart ntpd //重启ntp服务
[root@master ~]# netstat -anpu | grep ntpd //查看ntp服务状态
udp 0 0 192.168.188.10:123 0.0.0.0:* 15919/ntpd
2、部署时间同步的任务计划(主数据库)
[root@master ~]# crontab -e
*/30 * * * * /usr/sbin/ntpdate ntp.aliyun.com #每隔30秒跟阿里云时间同步
从数据库1、2上面安装
[root@slave1 ~]# yum -y install ntpdate //安装ntp全歼
[root@slave1 ~]# ntpdate 192.168.188.10 //设置192.168.188.10为时间同步源
29 Dec 09:19:28 ntpdate[61580]: adjust time server 192.168.188.10 offset -0.006004 sec
[root@slave1 ~]# date //查看当前时间
2020年 12月 29日 星期二 09:19:46 CST
[root@slave1 ~]# crontab -e //设置时间同步源
*/5 * * * * /usr/sbin/ntpdate 192.168.188.10 #每隔5分钟进行一次同步对象为192.168.188.10(主数据库)的时间同步
[root@slave1 ~]# date //查看当前时间
2020年 12月 29日 星期二 09:31:20 CST
3、配置主从复制(主数据库)
[root@master ~]# vim /etc/my.cnf
检索server-id = 1下面添加
log_bin=master_bin #同步二进制日志前缀
log_slave_updates=true #允许日志同步到从数据库
[root@master ~]# systemctl restart mysqld //重启数据库服务
[root@master ~]# mysql -uroot -p123456 //登录数据库
mysql> grant replication slave on *.* to 'myslave'@'192.168.188.%' identified by '123456'; //授权从数据库复制权限,可复制所有表所有库,
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> flush privileges; //刷新授权
Query OK, 0 rows affected (0.00 sec)
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)
从数据库1上
[root@slave1 ~]# vim /etc/my.cnf
server-id = 2 #修改 服务id为2
relay_log=relay-log-bin #二进制日志中继日志前缀
relay_log_index=slave-relay-bin-bin.index #中继日志索引
[root@slave1 ~]# systemctl restart mysqld
[root@slave1 ~]# mysql -uroot -p123123
mysql> change master to master_host='192.168.188.10',master_user='myslave',master_password='123456',master_log_file='master_bin.000001',master_log_pos=864; //设置主数据库的ip地址密码 日志文件
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start slave; //开启从状态
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G //查看从数据库的状态信息
从数据库2上
[root@slave2 ~]# vi /etc/my.cnf
server-id = 3 # 服务id为2
relay_log=relay-log-bin #二进制日志的中继日志前缀
relay_log_index=slave-relay-bin.index #中继日志索引
[root@slave2 ~]# systemctl restart mysqld
[root@slave2 ~]# mysql -uroot -p123123
mysql> change master to master_host='192.168.188.10',master_user='myslave',master_password='123456',master_log_file='master_bin.000001',master_log_pos=864;
Query OK, 0 rows affected, 2 warnings (0.00 sec)
mysql> start slave; //开启从数据库
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G //查看从数据库的状态
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.188.10
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master_bin.000001
Read_Master_Log_Pos: 864
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
4、主从测试
主数据库上
mysql> show databases; //查看所有库
+--------------------+
| Database |
+--------------------+
| information_schema |
| aaa |
| auth |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.00 sec)
mysql> create database song; //创建一个song数据库
Query OK, 1 row affected (0.00 sec)
mysql> show databases; //查看是否有song库
+--------------------+
| Database |
+--------------------+
| information_schema |
| aaa |
| auth |
| mysql |
| performance_schema |
| song |
| sys |
+--------------------+
7 rows in set (0.00 sec)
从数据库1上
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| song |
| sys |
+--------------------+
5 rows in set (0.00 sec)
从数据库2上
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| song |
| sys |
+--------------------+
5 rows in set (0.00 sec)
5、设置读写分离
因为ameoba用户授权给用户时默认的数据库为test
所以在主数据库时创建数据库test
主数据库
mysql> create database test; //创建test数据库
Query OK, 1 row affected (0.01 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| aaa |
| auth |
| mysql |
| performance_schema |
| song |
| sys |
| test |
+--------------------+
8 rows in set (0.00 sec)
在amoeba上
设置时间同步
[root@amoeba ~]# yum -y install ntpdate
[root@amoeba ~]# ntpdate 192.168.188.10
29 Dec 10:19:44 ntpdate[43430]: adjust time server 192.168.188.10 offset -0.071035 sec
[root@amoeba ~]# crontab -e
*/5 * * * * /usr/sbin/ntpdate 192.168.188.10
添加jdk-8u91-linux-x64.tar.gz
[root@amoeba ~]# tar zxvf jdk-8u91-linux-x64.tar.gz
[root@amoeba ~]# cp -rv jdk1.8.0_91/ /usr/local/java //-rv 递归显示进程
[root@amoeba ~]# vim /etc/profile
export JAVA_HOME=/usr/local/java # 设置JAVA根目录为全局变量
export JRE_HOME=/usr/local/java/jre
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin #环境变量,在PATH环境变量中添加JAVA根目录下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:/usr/local/java/bin:/usr/local/java/jre/bin
[root@amoeba ~]# echo $CLASSPATH //输出类路径信息
./:/usr/local/java/lib:/usr/local/java/jre/lib
[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)
安装管理软件
添加 amoeba-mysql-3.0.5-RC-distribution.zip
[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行下面添加 斌且注释32行
32 #JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss196k -XX:PermSize=16m -XX:MaxPermS ize=96m"
33 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 "Staring $NAME..." #提示
$AMOEBA_BIN #启动
echo "done" #输出信息
;;
stop)
echo -n "Stopping $NAME..."
$SHUTDOWN_BIN
rm -rf $PIDFILE #删除进程文件
echo "done"
;;
restart) #重启
$SHUTDOWN_BIN #先执行关闭
sleep 1 #延时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
[root@amoeba ~]# netstat -anpt | grep 8066
tcp6 0 0 :::8066 :::* LISTEN 44150/java
在主从数据库上给amoeba授权
在主从数据库上
mysql> grant all privileges on *.* to 'test'@'192.168.188.%' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
在前端上授权客户端登录时用户名和密码
[root@amoeba ~]# vi /usr/local/amoeba/conf/amoeba.xml
设置客户端连接amoeba前端服务器时使用的用户名和密码
检索password
<property name="user">amoeba</property>
<property name="password">123456</property>
设置读与写数据库的池
检索1500
删除掉 <!--
<property name="defaultPool">master</property> #默认池
<property name="writePool">master</property> #主数据库负责写
<property name="readPool">slaves</property> #从数据库负责读
删除掉 -->
[root@amoeba ~]# vim /usr/local/amoeba/conf/dbServers.xml
定义amoeba登录上数据库的授权用户名与密码
26 <property name="user">test</property>
27
28 <property name="password">123456</property>
设置主服务,地址
主数据库
43 <dbServer name="master" parent="abstractServer">
46 <property name="ipAddress">192.168.188.10</property>
从1
50 <dbServer name="slave1" parent="abstractServer">
51 <factoryConfig>
52 <!-- mysql ip -->
53 <property name="ipAddress">192.168.188.20</property>
54 </factoryConfig>
55 </dbServer>
从2复制从1 6行
57 <dbServer name="slave2" parent="abstractServer">
58 <factoryConfig>
59 <!-- mysql ip --> 60 <property name="ipAddress">192.168.188.30</property>
61 </factoryConfig>
62 </dbServer>
设置定义池
检索Separated
63 <dbServer name="slaves" virtual="true">
69 <property name="poolNames">slave1,slave2</property>
设置客户机
[root@client ~]# yum -y install mariadb*
[root@client ~]# systemctl start mariadb.service //重启服务
[root@client ~]# mysql
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 2
Server version: 5.5.56-MariaDB MariaDB Server
Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> exit
[root@client ~]# mysql -uamoeba -p123456 -h 192.168.188.40 -P8066 //登录前端数据库,用户名amoeba,密码123456,ip地址192.168.188.40,端口号8066
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 65950793
Server version: 5.1.45-mysql-amoeba-proxy-3.0.4-BETA
Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]> exit
MySQL [(none)]> use test;
Database changed
MySQL [test]> create table song(id int(10),name varchar(128),guanli varchar(128));
Query OK, 0 rows affected (0.03 sec)
MySQL [test]> insert into song values(1,'shufeng','this is master');
Query OK, 1 row affected (0.02 sec)
在主数据库上
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| song |
+----------------+
1 row in set (0.00 sec)
mysql> select * from song;
+------+---------+----------------+
| id | name | guanli |
+------+---------+----------------+
| 1 | shufeng | this is master |
+------+---------+----------------+
1 row in set (0.00 sec)
从数据库1上
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from song;
+------+---------+----------------+
| id | name | guanli |
+------+---------+----------------+
| 1 | shufeng | this is master |
+------+---------+----------------+
1 row in set (0.00 sec)
从数据库2上
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from song;
+------+---------+----------------+
| id | name | guanli |
+------+---------+----------------+
| 1 | shufeng | this is master |
+------+---------+----------------+
1 row in set (0.00 sec)
关闭主从同步复制,查看情况
主从复制关闭,再次添加数据,此时因为主数据库负责写操作,从数据库负责读操作,所以此时应只有主数据库上可以查看到新增的数据,从数据库上无变化
在客户端添加数据记录
客户机
MySQL [test]> insert into song values(2,'aaa','ccc');
Query OK, 1 row affected (0.00 sec)
从数据库
mysql> stop slave; //关闭主从同步
Query OK, 0 rows affected (0.00 sec)
客户机
MySQL [test]> insert into song values(2,'bbb','ddd');
Query OK, 1 row affected (0.01 sec)
从数据库
mysql> select * from song;
+------+---------+----------------+
| id | name | guanli |
+------+---------+----------------+
| 1 | shufeng | this is master |
| 2 | aaa | ccc |
+------+---------+----------------+
2 rows in set (0.00 sec)
主数据库
mysql> select * from song;
+------+---------+----------------+
| id | name | guanli |
+------+---------+----------------+
| 1 | shufeng | this is master |
| 2 | aaa | ccc |
| 2 | bbb | ddd |
+------+---------+----------------+
3 rows in set (0.00 sec)
写入不同的数据,查看客户端读取时情况
从数据库1
mysql> delete from test.song; //删除
Query OK, 2 rows affected (0.00 sec)
mysql> insert into test.song values(10011,'wangwu','qwe');
Query OK, 1 row affected (0.00 sec)
mysql> select * from song;
+-------+--------+--------+
| id | name | guanli |
+-------+--------+--------+
| 10011 | wangwu | qwe |
+-------+--------+--------+
1 row in set (0.00 sec)
从数据库2
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)
mysql> delete from test.song;
Query OK, 3 rows affected (0.00 sec)
mysql> select * from test.song;
Empty set (0.00 sec)
mysql> insert into test.song values(10012,'lisi','ddd');
Query OK, 1 row affected (0.00 sec)
mysql> select * from song;
+-------+------+--------+
| id | name | guanli |
+-------+------+--------+
| 10012 | lisi | ddd |
+-------+------+--------+
1 row in set (0.00 sec)
客户机
轮询的方式
MySQL [test]> select * from song;
+-------+--------+--------+
| id | name | guanli |
+-------+--------+--------+
| 10011 | wangwu | qwe |
+-------+--------+--------+
1 row in set (0.02 sec)
MySQL [test]>
MySQL [test]>
MySQL [test]> select * from song;
+-------+------+--------+
| id | name | guanli |
+-------+------+--------+
| 10012 | lisi | ddd |
+-------+------+--------+
1 row in set (0.01 sec)
说明客户机提供Amoeba写入数据,写在主数据库上,从Amoeba中读取数据库信息,是从从数据库中读取的
主数据库
mysql> select * from song;
+------+---------+----------------+
| id | name | guanli |
+------+---------+----------------+
| 1 | shufeng | this is master |
| 2 | aaa | ccc |
| 2 | bbb | ddd |
+------+---------+----------------+
3 rows in set (0.00 sec)
mysql> show master status; //查看数据库的状态信息
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master_bin.000001 | 2660 | | | |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
恢复从数据库1与主数据库的主从同步
从数据库1
mysql> change master to master_host='192.168.188.10',master_user='myslave',master_password='123456',master_log_file='master_bin.000001',master_log_pos=2660;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start slave; //开启从状态
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G //查看从数据库的状态信息
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.188.10
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master_bin.000001
Read_Master_Log_Pos: 2660
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
从数据库2
mysql> show slave status\G //查看从数据库的状态
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 192.168.188.10
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master_bin.000001
Read_Master_Log_Pos: 2660
Relay_Log_File: relay-log-bin.000002
Relay_Log_Pos: 2117
Relay_Master_Log_File: master_bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No