数据库主从复制实战和读写分离
主从复制 核心原理
必须记住 3 个核心组件
binlog(二进制日志) ------ 主库
. IO 线程 ------ 从库
SQL 线程 ------ 从库
完整流程(一步一步,逐句理解)
-
主库执行增删改(insert/update/drop)
-
MySQL 把这些操作记录到 binlog 日志文件
-
从库的 IO 线程连接主库,请求 binlog
-
主库的 dump 线程把 binlog 发给从库
-
从库 IO 线程将内容写到本地 relay log(中继日志)
-
从库 SQL 线程读取中继日志,重放 SQL 语句
-
两个 Yes 代表正常:IO_Running、SQL_Running
-
从库数据和主库完全一致
MySQL读写分离原理
只在主服务器上写,只在从服务器上读
主数据库处理事务性查询,从数据库处理SELECT查询
数据库复制用于将事务性查询的变更同步到集群中的从数据库
读写分离方案
基于程序代码内部实现
基于中间代理层实现
MySQL-Proxy
Amoeba
在每个事务更新数据完成之前,Master将这些改变记录进二进制日志。写入二进制日志完成后,
Master通知存储引擎提交事务
Slave将Master 的 Binary log复制到其中继日志(Relay log)。首先,Slave开始一个工作线程-I/0线
程,I/0线程在Master上打开一个普通的连接,然后开始Binlog dump process。Binlog dump
process从Master 的二进制日志中读取事件,如果已经跟上Master,它会睡眠并等待Master产生
新的事件。I/0线程将这些事件写入中继日志。
SQL sl3ave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其
中的事件而更新Slave数据,使其与Master中的数据保持一致。只要该线程与I/0线程保持一致,中
继日志通常会位于0S的缓存中,所以中继日志的开销很小。复制过程有一个很重要的限制,即复制
在Slave上是串行化的,也就是说Master上的并行更新操作不能在Slave上并行操作。
实战准备
主机名 IP地址 作用
mysql-master 192.168.108.101 mysql主服务器
mysql-slave01 192.168.108.102 mysql从节点1
mysql-slave02 192.168.108.103 mysql从节点2
amoeba 192.168.108.110 amoeba
mysql-client 192.168.108.111 应用客户端
时间同步
过时间戳实现业务的一致性
# 所有节点
ntpdate ntp.aliyun.com
date -R
systemctl disable firewalld --now
setenforce 0
mysql主从服务器配置
mysql主服务器配置
[root@mysql-master ~]# vim /etc/my.cnf
server-id = 11
log-bin = master-bin #主服务器日志文件
log-slave-updates = true #从服务器更新二进制日志
[root@mysql-master ~]# systemctl restart mysqld
[root@mysql-master ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.7.17-log Source distribution
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.108.%' 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 | 604 | | |
+-------------------+----------+--------------+------------------+---------------
----+
1 row in set (0.00 sec)
#检查有没有master-bin.000001
[root@mysql-master ~]# ls /usr/local/mysql/data
auto.cnf ibdata1 ib_logfile1 master-bin.000001 mysql
sys
ib_buffer_pool ib_logfile0 ibtmp1 master-bin.index performance_schema
mysql从服务器配置
mysql-slave01,mysql-slave02都要做如下操
#主从是克隆的要做这个操作,否则UUID一致
[root@mysql-slave01 ~]# systemctl stop mysqld
[root@mysql-slave01 ~]# rm -f /usr/local/mysql/data/auto.cnf
[root@mysql-slave01 ~]# systemctl start mysqld
[root@mysql-slave01 ~]# vim /etc/my.cnf
server-id = 22 #另外一台为23
relay-log = relay-log-bin #从主服务器上同步日志文件记录到本地
relay-log-index = slave-relay-bin.index #定义relay-log的位置和名称
[root@mysql-slave01 ~]# systemctl restart mysqld
[root@mysql-slave01 ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.17 Source distribution
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> change master to
master_host='192.168.108.101',master_user='myslave',master_password='123456',mast
er_log_file='master-bin.000001',master_log_pos=604;
#master_log_file,master_log_pos与前面查询的相同
Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> start slave;
Query OK, 0 rows affected (0.02 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.108.101
Master_User: myslave
Master_Port: 3306
......
#mysql-slave02
[root@mysql-slave02 ~]# systemctl stop mysqld
[root@mysql-slave02 ~]# rm -f /usr/local/mysql/data/auto.cnf
[root@mysql-slave02 ~]# systemctl start mysqld
[root@mysql-slave02 ~]# vim /etc/my.cnf
server-id = 23
relay-log = relay-log-bin
relay-log-index = slave-relay-bin.index
[root@mysql-slave02 ~]# systemctl restart mysqld
[root@mysql-slave02 ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.17 Source distribution
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> change master to
master_host='192.168.108.101',master_user='myslave',master_password='123456',mast
er_log_file='master-bin.000001',master_log_pos=604;
Query OK, 0 rows affected, 2 warnings (0.04 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.108.101
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-bin.000001
Read_Master_Log_Pos: 604
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:
验证主从同步
# 主服务器上:
[root@mysql-master ~]# mysql -uroot -phuawei
mysql> create database school;
Query OK, 1 row affected (0.01 sec)
mysql> use school;
CREATE TABLE student (
id int UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
age tinyint UNSIGNED,
#height DECIMAL(5,2),
gender ENUM('M','F') default 'M'
)ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4;
mysql> insert student (name,age)values('路飞',20);
# 去从服务器上 show databases;
[root@mysql-slave01 ~]# mysql -uroot -phuawei
mysql> select * from school.student;
+----+--------+------+--------+
| id | name | age | gender |
+----+--------+------+--------+
| 10 | 路飞 | 20 | M |
+----+--------+------+--------+
以上就完成了MySQL的主从复制
在mysql-slave01和mysql-slave02上查看
# mysql-slave01
mysql> select * from student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 31 | M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
#mysql-slave02
mysql> select * from student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 32| M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
并没有将客户端写入的insert student (name,age)values('卡卡西',30);同步
在mysq-master上查看内容发现写入成功:
# mysql-master
mysql> select * from student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 30 | M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
验证主从复制
读写分离 核心原理
基于 主从复制:
主库写入数据 → 自动同步到从库
应用程序收到 SQL:
主从复制保证数据一致
读写分离保证性能扩容
验证读写分离
在客户端上测试,第一次会向从服务器1读数,据-第二次会向从2读取
#mysql-client
MySQL [(none)]> select * from school.student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 31 | M |
+----+-----------+------+--------+
3 rows in set (0.02 sec)
MySQL [(none)]> select * from school.student;
+----+-----------+------+--------+
| id | name | age | gender |
+----+-----------+------+--------+
| 10 | 路飞 | 20 | M |
| 11 | 鸣人 | 20 | M |
| 12 | 卡卡西 | 32 | M |
+----+-----------+------+--------+
3 rows in set (0.00 sec)
#都是从从节点读取的,读写分离,由实验结果可知:客户端的读取内容会从mysql-slave01和mysqlslave02上轮询得到