MySQL 主从同步与读写分离原理及案例实施

文章目录

  • 概述
    • MySQL主从复制原理
    • MySQL读写分离原理
    • 案例拓扑图
  • 搭建 MySQL 主从复制
    • 一:时间同步配置
    • 二:安装 MySQL 数据库
    • 三:配置主、从 服务器
  • 搭建 MySQL 读写分离

概述

1:在企业应用中,成熟的业务通常数据量都比较大

2:单台MySQL在安全性、高可用性和高并发方面都无法满足实际的需求

3:配置多台主从数据库服务器以实现读写分离

MySQL主从复制原理

1:MySQL的复制类型

  • 基于语句的复制:在主服务器上执行的 SQL 语句,在服务器上执行同样的语句。MySQL 默认采用基本语句的复制,效率比较高
  • 基于行的复制:把改变的内容复制过去,而不是把命令在服务器上执行一遍
  • 混合类型的复制:默认采用基本语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制

2:MySQL主从复制的工作过程
MySQL 主从同步与读写分离原理及案例实施_第1张图片

  • 1.Master将用户对数据库更新的操作以二进制格式保存到 Binary Log日志文件中

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

  • 3.Master接收到来自Slave的IO进程的请求后,通过负责复制的TO进程根据请求信息读取制定日志指定位置之后的日志信息,返回给Slave的I0进程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到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读写分离原理

1:只在主服务器上写,只在从服务器上读

2:主数据库处理事务性查询,从数据库处理SELECT查询

3:数据库复制用于将事务性查询的变更同步到集群中的从数据库

4:读写分离方案

  • 基于程序代码内部实现
  • 基于中间代理层实现
    (1)MySQL-Proxy
    (2)Amoeba
  • MySQL-Proxy:MySQL-Proxy 为 MySQL 开源项目,通过其自带的 lua 脚本进行 SQL 判断,虽然是 MySQL
    官方产品,但是 MySQL 官方并不建议将 MySQL-Proxy 用到生产环境
  • Amoeba:由陈思儒开发,作者曾就职与阿里巴巴。该程序由 Java 语言进行开发,阿里巴巴将其用于生产环境,它不支持事务和存储过程
    MySQL 主从同步与读写分离原理及案例实施_第2张图片

案例拓扑图

MySQL 主从同步与读写分离原理及案例实施_第3张图片

搭建 MySQL 主从复制

一:时间同步配置

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

  • (1)主节点安装 ntp 和 ntpdate
[root@master ~]# yum -y install ntp ntpdate
  • (2)从节点安装 ntodate
[root@slave1 ~]# yum -y install ntpdate
[root@slave2 ~]# yum -y install ntpdate
  • (3)主节点同步阿里云时间
[root@master ~]# ntpdate ntp.aliyun.com
  • (4)配置 ntp
[root@master ~]# vi /etc/ntp.conf
restrict default nomodify                                # 8
restrict 20.0.0.0 mask 255.255.255.0 nomodify notrap     # 17 同步网段
21-24 行删除,添加两句
fudge 127.127.1.0 stratum 8
server 127.127.1.0
  • (5)重启服务
[root@master ~]# systemctl restart ntpd
[root@master ~]# netstat -anptu | grep ntpd
udp        0      0 20.0.0.13:123           0.0.0.0:*                           77692/ntpd 
udp        0      0 0.0.0.0:123             0.0.0.0:*                           77692/ntpd

2:从节点同步时间

[root@slave1 ~]# ntpdate 20.0.0.13
[root@slave2 ~]# ntpdate 20.0.0.13

3:主、从节点创建计划任务

[root@master ~]# crontab -e
*/30 *  *  *  *  /usr/sbin/ntpdate   ntp.aliyun.com          # 每半个小时执行一次时间同步
[root@slave1 ~]# crontab -e
*/3  *  *  *  *  /usr/sbin/ntpdate  20.0.0.13
[root@slave2 ~]# crontab -e
*/3  *  *  *  *  /usr/sbin/ntpdate  20.0.0.13

二:安装 MySQL 数据库

三台都需安装

三:配置主、从 服务器

1:配置 MySQL master 主服务器

  • (1)在 /etc/my.conf 中修改或者增加以下内容
server-id = 1
log_bin=master_bin
log_slave_updates=true
  • (2)重启服务
[root@master ~]# systemctl restart mysqld
  • (3)登录 MySQL 数据库,给从服务器授权

其中 File 列显示日志名,Position 列显示偏移量,这两个值在后面配置从服务器的时候会用到

slave 应从该点在 master 上进行新的跟新

[root@master ~]# mysql -uroot -p123123

mysql> grant replication slave on *.* to 'myslave'@'20.0.0.%' identified by '123123';
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 |      599 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

2:配置 MySQL slave 从1 服务器

  • (1)配置从服务器
[root@slave1 ~]# vi /etc/my.cnf
server-id = 2                         # 注意:server-id 不能与主服务器相同
relay_log=relay-log-bin
relay_log_index=slave-relay-bin.index
  • (2)重启服务
[root@slave1 ~]# systemctl restart mysqld
  • (3)登录 MySQL 数据库,配置同步

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

[root@slave1 ~]# mysql -uroot -p123123

mysql> change master to master_host='20.0.0.13',master_user='myslave',master_password='123123',
master_log_file='master_bin.000001',master_log_pos=599;
Query OK, 0 rows affected, 2 warnings (0.00 sec)
  • (4)启动同步
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
  • (5)查看 slave 状态,确保以下两个值为 YES
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 20.0.0.13
                  Master_User: myslave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master_bin.000001
          Read_Master_Log_Pos: 599
               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:             

3:配置 MySQL slave 从2 服务器

  • (1)配置从服务器
[root@slave2 ~]# vi /etc/my.cnf
server-id = 3                            # 注意:server-id 不能与主服务器相同
relay_log=relay-log-bin
relay_log_index=slave-relay-bin.index
  • (2)重启 MySQL 服务
[root@slave2 ~]# systemctl restart mysqld
  • (3)登录 MySQL 数据库,配置同步

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

[root@slave2 ~]# mysql -uroot -p123123

mysql> change master to master_host='20.0.0.13',master_user='myslave',master_password='123123',
master_log_file='master_bin.000001',master_log_pos=599;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
  • (4)启动同步
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
  • (5)查看 slave 状态,确保以下两个值为 YES
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 20.0.0.13
                  Master_User: myslave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master_bin.000001
          Read_Master_Log_Pos: 599
               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: 

4:验证主从复制效果

  • (1)主、从服务器登录 MySQL 数据库

三台数据库执行结果应该相同

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)
  • (2)在主服务器上新建数据库 txt
mysql> create database txt;
Query OK, 1 row affected (0.15 sec)
  • (3)在主、从服务器上分别查看数据库,显示数据库相同,则主从复制成功
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| txt                |
+--------------------+
5 rows in set (0.01 sec)

5:新建数据表 test ,客户机登录 Amoeba 时用

mysql> create database test;
Query OK, 1 row affected (0.00 sec)

搭建 MySQL 读写分离

1:时间同步

  • (1)同步 20.0.0.13
[root@amoeba ~]# ntpdate 20.0.0.13
  • (2)创建计划任务
[root@amoeba ~]# crontab -e
*/3  *  *  *  *  /usr/sbin/ntpdate  20.0.0.13

2:在主机 amoeba 上安装 JDK

  • (1)解压缩包
[root@amoeba ~]# tar zxvf jdk-8u91-linux-x64.tar.gz 
  • (2)拷贝文件
[root@amoeba ~]# cp -rv jdk1.8.0_91/ /usr/local/java
  • (3)更改环境变量
[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 ~]# echo $CLASSPATH
./:/usr/local/java/lib:/usr/local/java/jre/lib
  • (4)查看 Java 环境
[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)

3:安装并配置 Amoeba 软件

  • (1)解压缩软件包
[root@amoeba ~]# unzip amoeba-mysql-3.0.5-RC-distribution.zip 
  • (2)移动目录生成 amoeba 文件
[root@amoeba ~]# mv amoeba-mysql-3.0.5-RC/ /usr/local/amoeba
  • (3)对目录文件进行授权
[root@amoeba ~]# chmod -R 755 /usr/local/amoeba/
  • (4)更改 Amoeba 的参数值
[root@amoeba ~]# vi /usr/local/amoeba/jvm.properties
#JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss196k -XX:PermSize=16m -XX:MaxPermSize=96m"#32行,注释
JVM_OPTIONS="-server -Xms1024m -Xmx1024m -Xss256k"    # 接 32 行下面添加
  • (5)制作 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
rm -rf $PIDFILE
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

4:进入三台 MySQL 数据库开放权限给 Amoeba 访问

mysql> grant all privileges on *.* to 'test'@'20.0.0.%' identified by '123.com';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

5:编辑 amoeba 配置文件

  • (1)设置客户端连接 amoeba 前端服务器时使用的用户名和密码
[root@amoeba ~]# vi /usr/local/amoeba/conf/amoeba.xml 
<property name="user">amoeba</property>                 # 28
<property name="password">123456</property>             # 30
  • (2)配置读和写的池
<property name="defaultPool">master</property>           # 83
8588 去掉 
<property name="writePool">master</property>             # 86
<property name="readPool">slaves</property>              # 87

6:编辑 dbServers.xml 配置文件

[root@amoeba ~]# vi /usr/local/amoeba/conf/dbServers.xml
<property name="user">test</property>                   # 26
<property name="password">123.com</property>            # 28

<dbServer name="master"  parent="abstractServer">       # 43
 <property name="ipAddress">20.0.0.13</property>        # 46

<dbServer name="slave1"  parent="abstractServer">       # 50
 <property name="ipAddress">20.0.0.14</property>        # 5353 行后面复制添加一组 slave2
<dbServer name="slave2"  parent="abstractServer">
                 <factoryConfig>
                           <!-- mysql ip -->
                      <property name="ipAddress">20.0.0.15</property>
              </factoryConfig>
      </dbServer>

<dbServer name="slaves" virtual="true">                 # 64
<property name="poolNames">slave1,slave2</property>     # 70

7:启动 amoeba 服务

[root@amoeba ~]# cd /usr/local/amoeba/bin/
[root@amoeba bin]# ./launcher

8:查看 8066 号端口是否监听

[root@amoeba bin]# netstat -anpt | grep java
tcp6       0      0 :::8066              :::*                  LISTEN      52878/java          
tcp6       0      0 20.0.0.12:57802      20.0.0.15:3306        ESTABLISHED 52878/java          
tcp6       0      0 20.0.0.12:52758      20.0.0.13:3306        ESTABLISHED 52878/java          
tcp6       0      0 20.0.0.12:33636      20.0.0.14:3306        ESTABLISHED 52878/java 

9:客户机登录测试

需安装软件

[root@client ~]# yum -y install mariadb*

开启服务

[root@client ~]# systemctl start mariadb

登录

[root@client ~]# mysql -uamoeba -p123456 -h 20.0.0.12 -P8066
  • (1)查看数据库
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
| txt                |
+--------------------+
6 rows in set (0.01 sec)
  • (2)新创建一个表并写入数据
MySQL [(none)]> create table a1(id int(10),name varchar(64),address varchar(20));
Query OK, 0 rows affected (0.01 sec)

MySQL [(none)]> insert into a1 values(1,'lisi','this is master');
Query OK, 1 row affected (0.01 sec)
  • (3)主、从服务器查看

三台服务器查询结果一样

mysql> select * from test.a1;
+------+------+----------------+
| id   | name | address        |
+------+------+----------------+
|    1 | lisi | this is master |
+------+------+----------------+
1 row in set (0.00 sec)
  • (4)将从1、从2 服务器断开,客户机重新写入数据测试

断开

mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

客户机写入数据

MySQL [(none)]> insert into a1 values(2,'wangwu','this is master2');
Query OK, 1 row affected (0.00 sec)

主服务器查询测试

mysql> select * from test.a1;
+------+--------+-----------------+
| id   | name   | address         |
+------+--------+-----------------+
|    1 | lisi   | this is master  |
|    2 | wangwu | this is master2 |
+------+--------+-----------------+
2 rows in set (0.00 sec)

从服务器查询,没有写入的数据

mysql> select * from test.a1;
+------+------+----------------+
| id   | name | address        |
+------+------+----------------+
|    1 | lisi | this is master |
+------+------+----------------+
1 row in set (0.00 sec)
  • (5)从服务器单独写入数据,客户据查询数据(slave 关闭状态)

删除从1、从2 服务器表里的数据

mysql> delete from test.a1;
Query OK, 1 row affected (0.01 sec)

从1、从2 单独写入数据

mysql> insert into test.a1 values(100,'zhangsan','slave1');
Query OK, 1 row affected (0.00 sec)
mysql> insert into test.a1 values(200,'zhaoliu','slave2');
Query OK, 1 row affected (0.00 sec)

客户机多次查询

多次查询后依旧是这两个数据,

MySQL [(none)]> select * from test.a1;
+------+---------+---------+
| id   | name    | address |
+------+---------+---------+
|  200 | zhaoliu | slave2  |
+------+---------+---------+
1 row in set (0.00 sec)

MySQL [(none)]> select * from test.a1;
+------+----------+---------+
| id   | name     | address |
+------+----------+---------+
|  100 | zhangsan | slave1  |
+------+----------+---------+
1 row in set (0.01 sec)

结论:主从同步关闭时,客户机通过 amoeba 写入数据是写在主服务器里的,客户机通过 amoeba 读出数据第一次是从从1 里读的,第二次是从从2 里度的,默认轮询

10:重新主从同步

  • (1)重新查看主服务器的信息
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master_bin.000001 |     2843 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
  • (2)从服务器重新同步
mysql> change master to master_host='20.0.0.13',master_user='myslave',master_password='123123',
master_log_file='master_bin.000001',master_log_pos=2843;
Query OK, 0 rows affected, 2 warnings (0.00 sec)
  • (3)启动同步
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
  • (4)查看 slave 状态,确保以下两个值为 YES
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 20.0.0.13
                  Master_User: myslave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master_bin.000001
          Read_Master_Log_Pos: 2843
               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: 

你可能感兴趣的:(mysql,amoeba)