MySQL主从复制与读写分离

一、概述

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

二、MySQL主从复制原理

  • 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端真实执行时候的那些可执行的内容,并在自身执行。

三、MySQL读写分离原理

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

四、案例部署

4.1 案例环境

amoeba服务器:192.168.10.30
三台MySQL服务器,一主两从
master:192.168.10.40
slave1:192.168.10.50
slave2:192.168.10.60
client:192.168.10.20

4.2时间同步

master服务器

[root@master ~]# ntpdate ntp.aliyun.com
29 Dec 14:10:29 ntpdate[25523]: step time server 203.107.6.88 offset 0.779129 sec
[root@master ~]# crontab -e
*/30	*	*	*	* /usr/sbin/ntpdate ntp.aliyun.com
[root@master ~]# yum -y install ntp
[root@master ~]# systemctl restart ntpd

slave1和slave2同步:

root@slave1 ~]# ntpdate 192.168.10.40
29 Dec 14:18:48 ntpdate[25578]: step time server 192.168.10.40 offset 0.779788 sec
[root@slave1 ~]# crontab -e
*/30    *       *       *       * /usr/sbin/ntpdate 192.168.10.40

4.3主从同步

4.3.1 master服务器

1)修改主配置文件/etc/my.cnf,在mysqld下发添加

[root@master ~]# vi /etc/my.cnf
server-id=1  	##mysql服务器id,每个服务器不能相同
log_bin=master-bin 	##主服务器日志文件
log_slave_updates=true 	##允许中继日志读取主服务器的二进制日志
[root@master ~]# systemctl restart mysqld


2)为slave授权

[root@master ~]# mysql -uroot -p123456
mysql> grant replication slave on *.* to 'myslave'@'192.168.10.%' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.01 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 |      603 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

4.3.2 从服务器,slave1和2保持同步

1)修改主配置文件/etc/my.cnf,在mysqld下发添加

[root@slave1 ~]# vi /etc/my.cnf
server-id = 2		##slave1的server-id为2,slave2的server-id修改为3
relay_log=relay-log-bin		##从主服务器上同步日志文件记录到本地中继日志
relay_log_index=slave-relay-bin.index		##定义中继日志的索引

2)启动mysql,与主库保持同步,添加主库的账号,二进制日志文件,开始同步的位置

[root@slave1 ~]# systemctl restart mysqld
[root@slave1 ~]# mysql -uroot -p123456
mysql> change master to master_host='192.168.10.40',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=603;
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> start slave;   ## 启动从服务器
Query OK, 0 rows affected (0.00 sec)


mysql> show slave status \G    ## IO线程和SQL线程需要都处于yes状态
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.10.40
                  Master_User: myslave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000001
          Read_Master_Log_Pos: 603
               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.4 读写分离

4.4.1 amoeba服务端安装jdk

[root@amoeba ~]tar zxvf jdk-8u91-linux-x64.tar.gz
[root@amoeba ~]# mv jdk1.8.0_91/ /usr/local/java
[root@amoeba ~]# cd /etc/profile.d/
[root@amoeba profile.d]# vim java.sh   ## 指定java的环境变量
export JAVA_HOME=/usr/local/java 
export PATH=$PATH:$JAVA_HOME/bin 
[root@amoeba profile.d]# source java.sh   ##将脚本导入到环境变量中
[root@amoeba profile.d]# java -version   ## 查看java的版本信息
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)

4.4.2 部署amoeba代理

[root@amoeba ~]# unzip amoeba-mysql-3.0.5-RC-distribution.zip -d /usr/local/
[root@amoeba ~]# mv /usr/local/amoeba-mysql-3.0.5-RC/ /usr/local/amoeba
[root@amoeba ~]# chmod -R 755 /usr/local/amoeba/
[root@amoeba ~]# vim /usr/local/amoeba/jvm.properties 
32 #JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss196k -XX:PermSize=16m -XX:MaxPe    rmSize=96m"  
## 32行添加注释,新增3333 JVM_OPTIONS="-server -Xms1024m -Xmx1024m -Xss256k"

## 编写amoeba管理脚本
[root@amoeba ~]# vi /etc/init.d/amoeba
#!/bin/bash
#chkconfig: 35 62 62

export JAVA_HOME=/usr/local/java
export PATH=$JAVA_HOME/bin:$JAVE_HOME/jre/bin:$PATH
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 "Stoping $NAME..."
$SHUTDOWM_BIN
echo " done"
;;
restart)
$SHUTDOWN_BIN
sleep 1
$AMOEBA_BIN
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart}"
exit 1
;;
esac

[root@amoeba ~]# chmod +x /etc/init.d/amoeba 
[root@amoeba ~]# chkconfig --add amoeba
[root@amoeba ~]# systemctl start amoeba

4.4.3 在三台MySQL上给amoeba授权

mysql> grant all on *.* to 'test'@'192.168.10.%' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.01 sec)

4.4.4 在amoeba服务端修改主配置文件

[root@amoeba ~]# vi /usr/local/amoeba/conf/amoeba.xml 
   28<property name="user">amoeba</property>

   29<property name="password">123456</property>
   83<property name="defaultPool">master</property>
          ## 去掉这里的注释
         <property name="writePool">master</property>
         <property name="readPool">slaves</property>

         <property name="needParse">true</property>

4.4.5 修改dbServers.xml 文件

[root@amoeba ~]# vi /usr/local/amoeba/conf/dbServers.xml
## 设置数据库的用户和密码
22:		<property name="schema">mysql</property>

		<!-- mysql user -->
26:		property name="user">test</property>

28:		<property name="password">123456</property>

## 配置三台MySQL服务器
     43         <dbServer name="master"  parent="abstractServer">
     44                 <factoryConfig>
     45                         <!-- mysql ip -->
     46                         <property name="ipAddress">192.168.10.40</property>
     47                 </factoryConfig>
     48         </dbServer>
     49 
     50         <dbServer name="slave1"  parent="abstractServer">
     51                 <factoryConfig>
     52                         <!-- mysql ip -->
     53                         <property name="ipAddress">192.168.10.50</property>
     54                 </factoryConfig>
     55         </dbServer>
     56         <dbServer name="slave2"  parent="abstractServer">
     57                 <factoryConfig>
     58                         <!-- mysql ip -->
     59                         <property name="ipAddress">192.168.10.60</property>
     60                 </factoryConfig>
     61         </dbServer>
	
	 64         <dbServer name="slaves" virtual="true">

	 70         <property name="poolNames">slave1,slave2</property>

4.4.6 启动amoeba服务

两种启动方式,可以使用我们编写的管理脚本启动,也可以通过服务自带的launcher脚本启动服务

[root@amoeba ~]# systemctl start amoeba
[root@amoeba ~]# /usr/local/amoeba/bin/launcher

注意在启动的时候,如出现

2020-12-29 21:18:48,135 ERROR net.ServerableConnectionManager - Failure listening to socket on port '8066'.

表示8066端口已经被占用,通过netstat我们可以查到占用端口的进程号,再通过kill命令杀死进程即可正常启动服务

4.5 验证

4.5.1 客户端配置

[root@server2 ~]# systemctl stop firewalld
[root@server2 ~]# setenforce 0

[root@server2 ~]# yum install -y mariadb*
[root@server2 ~]# mysql -uamoeba -p123456 -h 192.168.10.30 -P 8066

4.5.2 验证同步

1)在client上面新建库test,查看三台数据库是否生成库和表

主库:

MySQL [(none)]> create database test;
Query OK, 1 row affected (0.01 sec)
MySQL [(none)]> use test;
Database changed
MySQL [test]> create table t1(id int(2) PRIMARY KEY, name varchar(48),score int(16));
Query OK, 0 rows affected (0.01 sec)
MySQL [test]> insert into t1(id,name,score)value(1,'xiaobai',100);
Query OK, 1 row affected (0.01 sec)

主库:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.00 sec)
mysql> mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| t1             |
| t2             |
+----------------+
2 rows in set (0.00 sec)

mysql> select * from t1;
+----+---------+-------+
| id | name    | score |
+----+---------+-------+
|  1 | xiaobai |   100 |
+----+---------+-------+
1 row in set (0.00 sec)


从库:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.00 sec)
mysql> select * from t1;
+----+---------+-------+
| id | name    | score |
+----+---------+-------+
|  1 | xiaobai |   100 |
+----+---------+-------+
1 row in set (0.00 sec)

成功开启同步

2)两台从服务器停止服务

mysql> stop slave;
## 在主服务器上插入的内容不会被同步-通过amoeba操作的
mysql> insert into t1 values(10,'ll',70);
Query OK, 1 row affected (0.00 sec)



## 在从服务器slave1上新建内容————直接在从上操作
mysql> insert into t1 values(3,'xiaotian',60);
Query OK, 1 row affected (0.00 sec)
## 在从服务器2上新建内容
mysql> insert into t1 values(4,'xiaoli',80);
Query OK, 1 row affected (0.01 sec)


## 客户端amoeba上测试————第一次会向从服务器slave1读取数据,第二次会向从服务器slave2读取,轮询模式
MySQL [test]> select * from t1;
+----+----------+-------+
| id | name     | score |
+----+----------+-------+
|  1 | xiaobai  |   100 |
|  3 | xiaotian |    60 |
+----+----------+-------+
2 rows in set (0.01 sec)

MySQL [test]> select * from t1;
+----+---------+-------+
| id | name    | score |
+----+---------+-------+
|  1 | xiaobai |   100 |
|  4 | xiaoli  |    80 |
+----+---------+-------+
2 rows in set (0.00 sec)

## 通过客户端连接数据库后写入的数据只有主master会有记录,然后同步给从,从服务器不会记录,从而实现了读写分离。
MySQL [test]> insert into t1(id,name,score)value(2,'xiaoming',10);
Query OK, 1 row affected (0.01 sec)


## 在客户端amoeba上看不到新插入的数据是应为没有开始主从,只有主master服务器上可以看到数据
mysql> select * from t1;
+----+----------+-------+
| id | name     | score |
+----+----------+-------+
|  1 | xiaobai  |   100 |
|  2 | xiaoming |    10 |
| 10 | ll       |    70 |
+----+----------+-------+
3 rows in set (0.00 sec)


## 在客户端开启同步后,主服务器上数据会同步到各个从服务器中,但是从服务器上自己增加的数据不会同步,只有本地会保存
## 客户端查看会看到主上面同步过来的数据,已经自己本地新增的数据,但是看不到其他从服务器上新增的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值