目录
前言
MySQL使用主从复制与读写分离的原因:
- 在企业应用中,成熟的业务通常数据量都比较大
- 单台MySQL在安全性、高可用性和高并发方面都无法满足实际的需求
(myisam基于表级锁定;innodb基于行级锁定 --存储引擎不能满足并发读、写的需求) - 配置多台主从数据库服务器以实现读写分离
一、MySQL主从复制原理
1.1 MySQL的复制类型
- 基于语句的复制
- 基于行的复制
- 混合类型的复制(语句、行、日志文件)
1.2 MySQL主从复制的工作过程
二、MySQL读写分离原理
-
只在主服务器上写,只在从服务器上读
-
主数据库处理事务性查询,从数据库处理select查询
-
数据库复制用于将事务性查询的变更同步到集群中的从数据库
读写分离原理示意图
-
复制的基本过程如下
- 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读写分离环境
三台装有MySQL5.7版本的服务器,角色分配如下:
master:10.0.0.40
slave-1:10.0.0.41
slave-2:10.0.0.42
一台Amoeba代理服务器
Amoeba:10.0.0.49
四、MySQL主从同步以及读写分离步骤
先把服务器的防火墙和核心防护全部关闭。
[root@localhost ~]# systemctl stop firewalld.service
[root@localhost ~]# setenforce 0
4.1 主从同步部分
- 主服务器:
- 时间同步
[root@master ~]# yum -y install ntp ntpdate
[root@master ~]# ntpdate ntp.aliyun.com
[root@master ~]# vi /etc/ntp.conf
server 127.127.1.0 #本地是时钟源
fudge 127.127.1.0 stratum 8 #设置时间层级为8
[root@master ~]# systemctl start ntpd
- mysql主服务器配置
[root@master ~]# vi /etc/my.cnf
server-id = 1 #指定id号,服务器的唯一标识,不能相同
log-bin=master-bin #主服务器日志文件
log-slave-updates=true #从服务器更新二进制日志
[root@master ~]# systemctl restart mysqld
[root@master ~]# mysql -u root -pqwe123
mysql> GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'10.0.0.%' IDENTIFIED BY 'abc123';
mysql> flush privileges;
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000006 | 120 | | | |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
# 特别注意File和Position对应的master-bin.000006以及120两个参数,从服务器同步时需要用到
- 从服务器
- 时间同步
[root@slave-1 ~]# yum -y install ntpdate
[root@slave-1 ~]# ntpdate 10.0.0.40
- mysql主服务器配置
[root@slave-1 ~]# vi /etc/my.cnf
server-id = 12
relay-log=relay-log-bin #从主服务器上同步日志文件记录到本地
relay-log-index=slave-relay-bin.index #定义relay-log的位置和名称
[root@slave-1 ~]# systemctl restart mysqld
[root@slave-1 ~]# mysql -u root -pqwe123
mysql> change master to master_host='10.0.0.40',master_user='myslave',master_password='abc123',master_log_file='master-bin.000006',master_log_pos=120;
mysql> start slave; #开启从服务
mysql> show slave status\G; #查看状态
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.40
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-bin.000004
Read_Master_Log_Pos: 120
Relay_Log_File: localhost-relay-bin.000002
Relay_Log_Pos: 284
Relay_Master_Log_File: master-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 120
Relay_Log_Space: 461
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: d67a9e7a-189b-11eb-8dfe-000c29302649
Master_Info_File: /usr/local/mysql/data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)
实验验证
- 主服务器
mysql> create database follow;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| follow |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)
- 从服务器
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| follow |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.01 sec)
4.2 读写分离部分
- 安装jdk环境
[root@amoeba packages]# tar zxf jdk-8u91-linux-x64.tar.gz
[root@amoeba packages]# mv jdk1.8.0_91/ /usr/local/java
[root@amoeba packages]# vi /etc/profile
#末尾添加
export JAVA_HOME=/usr/local/java
export JRE_HOME=/usr/local/java/jre
export AMOEBA_HOME=/usr/local/amoeba
export PATH=$PATH:$JAVA_HOME/bin:$AMOEBA_HOME/bin
[root@amoeba packages]# source /etc/profile
- 安装amoeba代理
[root@amoeba packages]# unzip amoeba-mysql-3.0.5-RC-distribution.zip
[root@amoeba packages]# 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:MaxPe rmSize=96m"
33 JVM_OPTIONS="-server -Xms1024m -Xmx1024m -Xss256k"
- 在三台MySQL上添加权限开放给amoeba访问
mysql> grant all on *.* to 'test'@'10.0.0.%' identified by 'abc123';
- 修改amoeba主配置文件
[root@amoeba ~]# vi /usr/local/amoeba/conf/amoeba.xml
#26,28行修改如下 修改客户端连接amoeba前端服务器时使用的用户名和密码
<property name="user">amoeba</property>
<property name="password">123123</property>
#83行修改如下(去掉注释)
<property name="defaultPool">master</property>
<property name="writePool">master</property>
<property name="readPool">slaves</property>
- 修改dbServers.xml 文件
[root@amoeba ~]# vi /usr/local/amoeba/conf/dbServers.xml
指定为amoeba创建的允许读取数据库的用户名和密码
22 <!-- mysql schema -->
23 <property name="schema">mysql</property> ###5.7版本没有默认的test数据库
24 <!-- mysql user -->
25 <property name="user">test</property>
26 <!-- mysql password -->
27 <property name="password">abc123</property>
配置三个服务器主机名和地址
<dbServer name="master" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">10.0.0.40</property>
</factoryConfig>
</dbServer>
<dbServer name="slave1" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">10.0.0.41</property>
</factoryConfig>
</dbServer>
<dbServer name="slave2" parent="abstractServer">
<factoryConfig>
<property name="ipAddress">10.0.0.42</property>
</factoryConfig>
</dbServer>
指定名为slaves的poolNames中pools的主机名
<dbServer name="slaves" virtual="true">
<property name="poolNames">slave1,slave2</property>
</dbServer>
额外需要注意!!
阿米巴做读写分离的时候:5.7版本没有test默认数据库
#23行需要注意!!!mysql5.7版本,默认没有test数据库所以修改为mysql数据库,5.5版本可忽略
- 启动amoeba服务器准备测试
[root@amoeba ~]# /usr/local/amoeba/bin/launcher
[root@amoeba ~]# netstat -anpt | grep 8066
tcp6 0 0 :::8066 :::* LISTEN 3226/java
- 验证
- 在master新建库和表
mysql> create database amoeba;
mysql> use amoeba;
mysql> create table test(id int(2),name varchar(64));
- 在两台从服务器上停止服务
mysql> stop slave;
- 在主服务上插入内容不会同步(通过amoeba操作)
主服务器
mysql> insert into test values(1,'alice');
从服务器
mysql> select * from amoeba.test;
Empty set (0.00 sec)
- 在从服务器1上直接插入内容
mysql> insert into amoeba.test values(2,'ben');
mysql> select * from amoeba.test;
+------+------+
| id | name |
+------+------+
| 2 | ben |
+------+------+
- 在从服务器2上直接插入内容
mysql> insert into amoeba.test values(3,'chalice');
mysql> select * from amoeba.test;
+------+---------+
| id | name |
+------+---------+
| 3 | chalice |
+------+---------+
- 在客户端amoeba上测试读操作
[root@localhost ~]# mysql -uamoeba -p123123 -h 10.0.0.49 -P 8066
mysql> use amoeba;
mysql> select * from test;
+------+------+
| id | name |
+------+------+
| 2 | ben |
+------+------+
1 row in set (0.01 sec)
mysql> select * from test;
+------+---------+
| id | name |
+------+---------+
| 3 | chalice |
+------+---------+
1 row in set (0.00 sec)
第一次向从服务器1读数据,第二次向从服务器2读数据,不会读取到主服务器的数据
- 在客户端测试写操作
客户端
mysql> insert into test values(4,'david');
Query OK, 1 row affected (0.01 sec)
mysql> select * from test;
+------+------+
| id | name |
+------+------+
| 2 | ben |
+------+------+
1 row in set (0.00 sec)
mysql> select * from test;
+------+---------+
| id | name |
+------+---------+
| 3 | chalice |
+------+---------+
1 row in set (0.00 sec)
主:
mysql> select * from amoeba.test;
+------+-------+
| id | name |
+------+-------+
| 1 | alice |
| 4 | david |
+------+-------+
2 rows in set (0.00 sec)
在客户端上看不到新插入的数据,只能在主服务器端看到,说明写操作在主服务器上,证明已经实现读写分离。
- 在客户端amoeba上看不到新插入的数据,因为同步没有开启,只有主服务器上可以看到数据
mysql> select * from test;
+------+------+
| id | name |
+------+------+
| 2 | ben |
+------+------+
mysql> select * from test;
+------+---------+
| id | name |
+------+---------+
| 3 | chalice |
+------+---------+
1 row in set (0.01 sec)
- 开启同步
从1:
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from amoeba.test;
+------+-------+
| id | name |
+------+-------+
| 2 | ben |
| 1 | alice |
| 4 | david |
+------+-------+
3 rows in set (0.00 sec)
从2:
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from amoeba.test;
+------+---------+
| id | name |
+------+---------+
| 3 | chalice |
| 1 | alice |
| 4 | david |
+------+---------+
3 rows in set (0.00 sec)
主:
mysql> select * from test;
+------+-------+
| id | name |
+------+-------+
| 1 | alice |
| 4 | david |
+------+-------+
2 rows in set (0.00 sec)
客户端:
mysql> select * from test;
+------+-------+
| id | name |
+------+-------+
| 2 | ben |
| 1 | alice |
| 4 | david |
+------+-------+
3 rows in set (0.00 sec)
mysql> select * from test;
+------+---------+
| id | name |
+------+---------+
| 3 | chalice |
| 1 | alice |
| 4 | david |
+------+---------+
3 rows in set (0.01 sec)
开启同步后,主服务器上的数据会同步到各从服务器上中,但从服务器上的自己增加的数据不会同步,只有本地保存