50.Mysql主从复制与读写分离
Mysql主从复制与读写分离
主从复制的作用
1.实时灾备,用于故障切换
2.读写分离,提供查询服务
3.数据备份,避免影响业务
主从形式
一主一从,主主复制,一主多从,(扩展系统读取性能),多主一从(MySQL 5.7+支持),级联复制
主从复制原理
主从复制步骤:
- 主库将所有写操作记录到binlog日志,并通过log dump线程传送给从库的I/O线程
- 从库生成I/O线程和SQL线程
- I/O线程请求主库的binlog,并将日志写入relay log(中继日志)文件
- SQL线程读取relay log文件中的日志,解析成具体操作,实现主从数据一致
关闭防火墙和selinux
确保主从数据一致
先在主库新建一个数据库,然后新建一个表
mysql> create database xyh;
Query OK, 1 row affected (0.00 sec)mysql> use xyh;
Database changed
mysql> create table student(id int primary key,-> name varchar(10), -> age int);
mysql> insert into student values (1,"zhangsan",23),-> (2,"lisi",21),-> (3,"wangwu",20),-> (4,"zhaoliu",22),-> (5,"xiaohao",24);
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0mysql> select * from student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
+----+----------+------+
5 rows in set (0.00 sec)
在备份主库时需要给主库上锁,避免在备份期间有其他人在写入导致数据不一致,在备份完成后才能退出,因此需要另开一个终端来备份
mysql> FLUSH TABLES WITH READ LOCK;
备份主库
[root@xieyuhui2 ~]# mysqldump -uroot -predhat --all-databases > /opt/all-20250921.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.
将主库文件传输给从库
[root@xieyuhui2 ~]# scp /opt/all-20250921.sql root@192.168.100.10:/opt/
将文件导入从库确保数据一致
[root@xieyuhui ~]# mysql -uroot -predhat < /opt/all-20250921.sql
mysql> select * from xyh.student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
+----+----------+------+
5 rows in set (0.00 sec)
创建主从同步账号
在主库里创建同步账号授权给从数据库使用
mysql> create user 'root'@'192.168.100.10' identified by 'redhat';
Query OK, 0 rows affected (0.00 sec)mysql> grant replication slave on *.* to 'root'@'192.168.100.10';
Query OK, 0 rows affected (0.01 sec) #授权mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
配置主数据库
[root@xieyuhui2 ~]# vim /etc/my.cnf[mysqld]
basedir = /usr/local/mysql
datadir = /opt/data
socket = /tmp/mysql.sock
port = 3306
pid-file = /opt/data/mysql.pid
user = mysql
skip-name-resolve
log-bin=mysql-bin #启用binlog日志
server-id=1 #数据库服务器的唯一标识,从库的id值必须比主库大
symbolic-links=0
log-error=/var/log/mysqld.log
重启mysql服务
[root@xieyuhui2 ~]# systemctl restart mysqld
查看主库状态
[root@xieyuhui2 ~]# mysql -uroot -predhat -e 'show master status';
mysql: [Warning] Using a password on the command line interface can be insecure.
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 154 | | | |
+------------------+----------+--------------+------------------+-------------------+
配置从数据库
[root@xieyuhui ~]# vim /etc/my.cnf[mysqld]
basedir = /usr/local/mysql
datadir = /opt/data
socket = /tmp/mysql.sock
port = 3306
pid-file = /opt/data/mysql.pid
user = mysql
skip-name-resolve
server-id=2
relay-log=mysql-relay-bin #启用中继日志
symbolic-links=0
log-error=/var/log/mysqld.log
重启从库mysql服务
[root@xieyuhui ~]# systemctl restart mysqld
配置并启动主从复制
mysql> change master to-> master_host='192.168.100.20',-> master_user='root',-> master_password='redhat',-> master_log_file='mysql-bin.000001',-> master_log_pos=154;
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: Connecting to masterMaster_Host: 192.168.100.20Master_User: rootMaster_Port: 3306Connect_Retry: 60Master_Log_File: mysql-bin.000001Read_Master_Log_Pos: 154Relay_Log_File: mysql-relay-bin.000001Relay_Log_Pos: 4Relay_Master_Log_File: mysql-bin.000001Slave_IO_Running: Yes #必须为yesSlave_SQL_Running: Yes #必须为yesReplicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0Last_Error: Skip_Counter: 0Exec_Master_Log_Pos: 154Relay_Log_Space: 154Until_Condition: NoneUntil_Log_File: Until_Log_Pos: 0Master_SSL_Allowed: NoMaster_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: NoLast_IO_Errno: 2003Last_IO_Error: error connecting to master 'root@192.168.100.20:3306' - retry-time: 60 retries: 1Last_SQL_Errno: 0Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 0Master_UUID: Master_Info_File: /opt/data/master.infoSQL_Delay: 0SQL_Remaining_Delay: NULLSlave_SQL_Running_State: Slave has read all relay log; waiting for more updatesMaster_Retry_Count: 86400Master_Bind: Last_IO_Error_Timestamp: 250921 22:13:00Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version:
1 row in set (0.00 sec)
测试验证
在主服务器的xyh库的student表中插入数据
mysql> select * from student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
+----+----------+------+
5 rows in set (0.00 sec)mysql> insert into student values(6,"haoshuai",19);
Query OK, 1 row affected (0.02 sec)mysql> select * from student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
| 6 | haoshuai | 19 |
+----+----------+------+
6 rows in set (0.00 sec)
在从库查看
mysql> select * from student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
| 6 | haoshuai | 19 |
+----+----------+------+
6 rows in set (0.00 sec)
读写分离
什么是读写分离?
读写分离是一种数据库架构模式,基本原理是:
主数据库(Master)处理所有写操作(INSERT、UPDATE、DELETE)
从数据库(Slave)处理SELECT查询操作
通过数据库复制将写操作的变更同步到从数据库
为什么要读写分离?
写操作相对耗时,可能影响查询性能
分离读写操作可提升数据库并发负载能力
优化数据库性能,提高系统响应速度
适用场景
应用程序读多写少的情况
需要提升数据库查询性能的场景
数据库负载较高,需要分担压力的情况
基于中间代理层实现
常见中间件:
- MySQL-Proxy:MySQL开源项目,使用Lua脚本进行SQL判断
- Atlas:奇虎360基于MySQL-Proxy优化开发
- Amoeba:Java开发,阿里巴巴用于生产环境(不支持事务和存储过程)
- Mycat:Java编写的数据库中间件,支持分库分表和读写分离
本次使用mycat作为中间代理
前置条件
关闭防火墙和SELinux
配置时间同步
主从数据库已完成主从复制配置
配置/etc/hosts文件:
192.168.100.10 xieyuhui.example.com slave
192.168.100.20 xieyuhui2.example.com master
192.168.100.30 xieyuhui3.example.com mycat
发送给其他主机
[root@xieyuhui2 ~]# scp /etc/hosts root@192.168.100.10:/etc/hosts[root@xieyuhui2 ~]# scp /etc/hosts root@192.168.100.30:/etc/hosts
在mycat节点安装java环境
[root@xieyuhui3 ~]# yum -y install java java-devel
部署mycat服务,将Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz上传到mycat的/root目录下,并解压到/usr/local
cd /root
[root@xieyuhui3 ~]# tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local/
定义环境变量
[root@xieyuhui3 ~]# echo export MYCAT_HOME=/usr/local/mycat/ >> /etc/profile
[root@xieyuhui3 ~]# source /etc/profile
编辑mycat服务读写分离的schema.xml,设置数据库写入节点为master,读取节点slave,注意IP要修改
清除全部内容,并且写入新内容
[root@xieyuhui3 ~]# vim /usr/local/mycat/conf/schema.xml<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="USERDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1"></schema>
<dataNode name="dn1" dataHost="localhost1" database="xyh" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" dbType="mysql" dbDriver="native" writeType="0" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat><writeHost host="hostM1" url="192.168.100.20:3306" user="root" password="redhat"> <readHost host="hostS1" url="192.168.100.10:3306" user="root" password="redhat" /> </writeHost></dataHost></mycat:schema>
编辑mycat用户的/usr/local/mycat/conf/目录下的server.xml,修改root用户的密码redhat,访问mycat的逻辑库为USERDB
[root@xieyuhui3 ~]# vim /usr/local/mycat/conf/server.xml
在文件末尾处删除如下<user name="root"><property name="password">user</property><property name="schemas">TESTDB</property><property name="readOnly">true</property></user><user name="root"><property name="password">redhat</property><property name="schemas">USERDB</property>
启动mycat服务
[root@xieyuhui3 ~]# cd /usr/local/mycat/bin
[root@xieyuhui3 bin]# ./mycat start
查看8066,9066端口是否开启
[root@xieyuhui3 bin]# ss -anlt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:111 *:*
LISTEN 0 128 *:6000 *:*
LISTEN 0 5 192.168.122.1:53 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 128 127.0.0.1:631 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 127.0.0.1:6010 *:*
LISTEN 0 1 127.0.0.1:32000 *:*
LISTEN 0 100 :::9066 :::*
LISTEN 0 50 :::44749 :::*
LISTEN 0 128 :::111 :::*
LISTEN 0 128 :::6000 :::*
LISTEN 0 128 :::22 :::*
LISTEN 0 128 ::1:631 :::*
LISTEN 0 50 :::36697 :::*
LISTEN 0 100 ::1:25 :::*
LISTEN 0 128 ::1:6010 :::*
LISTEN 0 50 :::1984 :::*
LISTEN 0 100 :::8066 :::*
8066为数据端口,9066为数据端口,用于管理mycat集群状态
在master和slave节点分别给root数据库用户授权,因为默认情况下,数据库用户root不允许远程登录
mysql> grant all privileges on *.* to 'root'@'%' identified by 'redhat' with grant option;
Query OK, 0 rows affected, 1 warning (0.01 sec)
在mycat节点安装[root@xieyuhui3 ~]# yum install mariadb mariadb-server让其能够使用mysql命令
[root@xieyuhui3 ~]# yum install mariadb mariadb-server -y
在本地使用数据端口登录到mysql数据中,查询和插入数据,然后使用管理端口查看读写分离效果
[root@xieyuhui3 ~]# mysql -uroot -predhat -P8066 -h127.0.0.1
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server (OpenCloundDB)Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MySQL [(none)]>MySQL [(none)]> show databases;
+----------+
| DATABASE |
+----------+
| USERDB |
+----------+
1 row in set (0.00 sec)MySQL [USERDB]> show tables;
+---------------+
| Tables_in_xyh |
+---------------+
| student |
+---------------+
1 row in set (0.00 sec)MySQL [USERDB]> select * from student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
| 6 | haoshuai | 19 |
+----+----------+------+
6 rows in set (0.03 sec)插入数据
MySQL [USERDB]> insert into student values(7,"guoqiang",55);
Query OK, 1 row affected (0.00 sec)MySQL [USERDB]> select * from student;
+----+----------+------+
| id | name | age |
+----+----------+------+
| 1 | zhangsan | 23 |
| 2 | lisi | 21 |
| 3 | wangwu | 20 |
| 4 | zhaoliu | 22 |
| 5 | xiaohao | 24 |
| 6 | haoshuai | 19 |
| 7 | guoqiang | 55 |
+----+----------+------+
7 rows in set (0.00 sec)
通过管理接口9066来查看读写分离的情况,看写入的次数和查看的次数
[root@xieyuhui3 ~]# mysql -uroot -predhat -P9066 -h127.0.0.1MySQL [(none)]> show @@datasource;
主库写了1次,从库读了5次