mysql主从复制

一 核心概念

1、什么是主从复制

主从复制可以实现将数据从一台数据库服务器(master)复制到一台到多台数据库服务器(slave)

默认情况下,属于异步复制,所以无需维持长连接

解决问题:① 数据实时备份 ② 缓解服务器压力(读操作可以分散到slave服务器)=> MyCAT(读写分离软件)

简单来说,master将数据库的改变写入二进制日志,slave同步这些二进制日志,并根据这些二进制日志进行数据重演操作,实现数据异步同

2、MySQL复制原理

binlog 二进制日志, relaylog 重写日志(负责把主服务器的 DML 在 slave 服务器重写执行一遍)

binlog 保存了 用户对数据库的增删改事务操作 ( SQL 语句)、 relaylog 重写日志 (中继日志), 当从服务器从主服务器拉取到二进制日志数据时,会首先写入到 relaylog 重写日志中 。

mysqldump --single-transaction --master-data

详细描述:

前提:主服务器开启binlog二进制日志,从服务器开启relaylog中继日志。

① slave端的IO线程发送请求给master端的binlog dump线程

② master端binlog dump线程获取二进制日志信息(文件名和位置信息)发送给slave端的IO线程

③ salve端IO线程获取到的内容依次写到slave端relay log里,并把master端的bin-log文件名和位置记录到master.info里

④ salve端的SQL线程,检测到relay log中内容更新,就会解析relay log里更新的内容,并执行这些操作,从而达到和master数据一致

主从复制三要素:binlog、relaylog、两个线程

  1. IO 线程:从库 → 连主库 Binlog Dump → 拉 binlog → 写入本地 relaylog,记录主库位点
  2. SQL 线程:读本地 relaylog → 回放事务 → 记录 relay 执行位点
  3. binlog:主库原始变更日志;relaylog:从库中转缓存日志

|------------------------|
| master:主服务器;slave:从服务器 |

|-------------------------------------------------|
| 注:主从复制也是备份的一种,属于在线热备。到这里就学过3种备份了:逻辑备份、物理备份、在线热备 |

3、同步 vs 异步(面试常问)

方式 怎么工作 优点 缺点
同步复制 主库写数据时,从库必须马上同步完,主库才能继续下一个事务 两端数据高度一致 阻塞主库事务,主库被拖慢
异步复制 主库写完就走,从库空闲时自己来拉 不阻塞主库业务 可能出现主从延迟

4、两个关键日志(先记住这两个名字)

日志 住哪儿 干什么的
binlog(二进制日志) 主库 记录用户对数据库的增删改操作(SQL 语句)
relay log(中继日志/重写日志) 从库 从主库拉来的 binlog 先存这儿,然后在这上面"重演"一遍

大白话:binlog 是主库的"账本",relay log 是从库抄回来的"副本账本",从库照着副本再抄一遍到自己库里。

5、复制原理四步走(必背)

前提:主库开 binlog,从库开 relay log。

1、从库 IO 线程读取本地记录的位点,连接主库 Binlog Dump 线程,告知同步起点

【我上次收到主库 binlog.000004,位置 1207,接下来从这里往后给我数据】

2、Binlog Dump 定位到 binlog 对应位置,持续向后推送 binlog 事件

【如果这个位置后面已经有现成 binlog 数据:立刻读取、发送给从库 IO 线程;

如果这个位置后面暂时没有新写入的 binlog:Binlog Dump 线程阻塞等待(长连接挂着),主库一旦有新 DML/DDL 提交写入 binlog,马上把新事件推送给从库 IO 线程。】

3、IO 线程接收事件,写入 relaylog,更新 IO 接收位点

【这个 pos 之前的日志,我已经成功接收落盘到 relaylog 了,下次断连重连,就从这个 pos 继续。】

4、SQL 线程读取 relaylog,回放执行事务,更新回放位点

【从库 SQL 线程 持续监控 relaylog 有没有新增事件,SQL 线程按顺序读取 relaylog 里的事件,解析并在从库本地执行(DML/DDL 事务回放,事务回放成功后,更新 mysql.slave_relay_log_info:记录已经回放执行到哪个主库位点,已经回放完成、不需要再用的 relaylog,MySQL 自动清理】

5、循环往复,保持主从数据一致

记忆口诀:IO 线程负责" 搬" ,SQL 线程负责" 干" 。

后面排查故障你会反复看到这两个线程的状态,所以现在一定要分清:

  1. Slave_IO_Running: Yes → 成功连上主库并且下载了日志
  2. Slave_SQL_Running: Yes → 成功执行了日志里的 SQL 语句
  3. Seconds_Behind_Master: 0 → 主从延迟秒数,0 表示最佳状态,无延迟

**二、传统主从复制(**AB 复制)设计

1. MySQL****主从复制环境准备

传统AB复制架构(M-S),说明:mysql数据库,版本为8.0.40

环境说明:

|----------------|-------------|-----------|
| IP | 主机名 | 角色 |
| 192.168.30.111 | node1.it.cn | master(主) |
| 192.168.30.112 | node2.it.cn | slave(从) |

安装前准备:① 配置IP、主机名 ② 配置IP与主机映射 => /etc/hosts ③ 关闭防火墙与SELinux ④ 时间同步 ⑤ 安装必备软件,如vim、wget、rsync

复制代码
设置主机名称
hostnamectl set-hostname node1.itcast.cn
hostnamectl set-hostname node2.itcast.cn

配置IP与主机映射
vim /etc/hosts
尾部追加如下内容
192.168.88.101 node1 node1.itcast.cn
192.168.88.102 node2 node2.itcast.cn

关闭防火墙与SELinux
systemctl stop firewalld
systemctl disable firewalld

setenforce 0
vim /etc/selinux/config

安装一些依赖软件(系统必备软件)
yum install vim wget rsync -y

**2.**搭建主从复制思路

  1. master、slave必须安装相同版本的mysql数据库软件
  2. master端必须开启二进制日志;slave端必须开启relay log中继日志
  3. master端和slave端的server-id号不能一致 => my.cnf => server-id = 10
  4. 同步master端数据之前,要删除data数据目录下的auto.cnf文件 => uuid编号(每个mysql实例都是唯一的)

master => mysql(uuid编号 => 数据库初始化自动生成) => /export/server/mysql/data/auto.cnf

slave数据目录是把master中的data同步过来,导致两个MySQL公用同一个uuid编号

5.slave端配置向master来同步数据

  • master端必须创建一个复制用户
  • 保证master和slave端初始数据一致
  • 配置主从复制(slave端)

3. MySQL8****主从复制实践

3.1****配置 Master 主服务器

既要安装MySQL软件,也要初始化MySQL数据库

下面是master安装脚本 需要脚本同目录下有mysql-8.0.40-linux-glibc2.17-x86_64.tar.xz安装包

bash 复制代码
#!/bin/bash
#1.安装依赖软件
echo "正在安装依赖软件..."
yum -y install libaio &> /dev/null
if [ $? -ne 0 ];then
    echo "libaio安装失败"
    exit 1
fi
#2.判断是否有压缩包,如果有,则执行解压缩操作
echo "正在判断是否有压缩包,如果有进行解压缩操作..."
if [ -f mysql-8.0.40-linux-glibc2.17-x86_64.tar.xz ]; then
    tar -xf mysql-8.0.40-linux-glibc2.17-x86_64.tar.xz
    ls -l mysql-8.0.40-linux-glibc2.17-x86_64
fi
#3.判断系统中是否安装过mariadb软件,如果有对其进行卸载操作
echo "正在判断系统中是否安装过mariadb软件,如果有对其进行卸载操作..."
rpm -qa | grep mariadb | xargs -r yum remove -y
[ -f /etc/my.cnf ] && rm -rf /etc/my.cnf
#4.创建mysql系统账号
id mysql &> /dev/null
[ $? -ne 0 ] && useradd -r -s /sbin/nologin mysql
#5.创建/export/server目录,然后移动mysql压缩包解压后的文件到/export/server目录下
rm -rf /export/server
mkdir -p /export/server
mv mysql-8.0.40-linux-glibc2.17-x86_64 /export/server/mysql
#6.进入mysql目录,对其进行初始化操作
echo "正在进入mysql目录,对其进行初始化操作..."
cd /export/server/mysql
bin/mysqld --initialize --user=mysql --basedir=/export/server/mysql --datadir=/export/server/mysql/data 2>&1 | tee /tmp/mysqld.log | grep password | awk '{print $NF}' > /tmp/mysql_temp_password.txt
#7.设置ssl加密传输连接
bin/mysql_ssl_rsa_setup --datadir=/export/server/mysql/data &> /dev/null
#8.设置my.cnf与mysqld.service文件
echo "正在设置my.cnf与mysqld.service文件..."
cat > /etc/my.cnf <<EOF
[mysqld]
port=3306
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
character_set_server=utf8
collation-server=utf8_unicode_ci
EOF

cat > /etc/systemd/system/mysqld.service <<EOF
[Unit]
Description=MySQL Server
After=network.target

[Service]
User=mysql
Group=mysql
Type=forking

# MySQL 执行命令及路径
ExecStart=/export/server/mysql/bin/mysqld --daemonize --pid-file=/export/server/mysql/data/mysqld.pid
ExecStop=/export/server/mysql/bin/mysqladmin --defaults-file=/export/server/mysql/my.cnf shutdown

# Ensure MySQL has sufficient time to start up
TimeoutSec=600

# PID 文件路径
PIDFile=/export/server/mysql/data/mysqld.pid

# Enable these options to auto-restart the service if it crashes
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
#9.刷新后台服务,然后启动mysqld
echo "正在刷新后台服务,然后启动mysqld..."
systemctl daemon-reload
systemctl start mysqld
systemctl enable mysqld
#10.重置mysql管理员密码为123456
echo "正在重置mysql管理员密码..."
cd /export/server/mysql
temp_password=`cat /tmp/mysql_temp_password.txt`
bin/mysqladmin -uroot password '123456' -p$temp_password
#11.把mysql的bin目录添加到环境变量中
echo 'export PATH=$PATH:/export/server/mysql/bin' >> /etc/profile
source /etc/profile
#12.解决mysql客户端首次无法登录问题
[ ! -f /lib64/libncurses.so.5 ] && ln -s /lib64/libncurses.so.6 /lib64/libncurses.so.5
[ ! -f /lib64/libtinfo.so.5 ] && ln -s /lib64/libtinfo.so.6 /lib64/libtinfo.so.5
#13.弹出提示,MySQL安装成功
echo "MySQL安装成功,软件安装路径:/export/server/mysql,数据库初始密码:123456!"

3.2****配置 Slave 从服务器

只需要安装MySQL软件,不需要初始化也不需要启动MySQL软件。

bash 复制代码
#!/bin/bash
#1.安装依赖软件
echo "正在安装依赖软件..."
yum -y install libaio &> /dev/null
if [ $? -ne 0 ];then
    echo "libaio安装失败"
    exit 1
fi
#2.判断是否有压缩包,如果有,则执行解压缩操作
echo "正在判断是否有压缩包,如果有进行解压缩操作..."
if [ -f mysql-8.0.40-linux-glibc2.17-x86_64.tar.xz ]; then
    tar -xf mysql-8.0.40-linux-glibc2.17-x86_64.tar.xz
    ls -l mysql-8.0.40-linux-glibc2.17-x86_64
fi
#3.判断系统中是否安装过mariadb软件,如果有对其进行卸载操作
echo "正在判断系统中是否安装过mariadb软件,如果有对其进行卸载操作..."
rpm -qa | grep mariadb | xargs -r yum remove -y
[ -f /etc/my.cnf ] && rm -rf /etc/my.cnf
#4.创建mysql系统账号
id mysql &> /dev/null
[ $? -ne 0 ] && useradd -r -s /sbin/nologin mysql
#5.创建/export/server目录,然后移动mysql压缩包解压后的文件到/export/server目录下
rm -rf /export/server
mkdir -p /export/server
mv mysql-8.0.40-linux-glibc2.17-x86_64 /export/server/mysql
#6.进入mysql目录,对其进行初始化操作
echo "正在进入mysql目录,从服务器无需进行初始化操作..."
cd /export/server/mysql
#7.设置ssl加密传输连接
bin/mysql_ssl_rsa_setup --datadir=/export/server/mysql/data &> /dev/null
#8.设置my.cnf与mysqld.service文件
echo "正在设置my.cnf与mysqld.service文件..."
cat > /etc/my.cnf <<EOF
[mysqld]
port=3306
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
character_set_server=utf8
collation-server=utf8_unicode_ci
EOF

cat > /etc/systemd/system/mysqld.service <<EOF
[Unit]
Description=MySQL Server
After=network.target

[Service]
User=mysql
Group=mysql
Type=forking

# MySQL 执行命令及路径
ExecStart=/export/server/mysql/bin/mysqld --daemonize --pid-file=/export/server/mysql/data/mysqld.pid
ExecStop=/export/server/mysql/bin/mysqladmin --defaults-file=/export/server/mysql/my.cnf shutdown

# Ensure MySQL has sufficient time to start up
TimeoutSec=600

# PID 文件路径
PIDFile=/export/server/mysql/data/mysqld.pid

# Enable these options to auto-restart the service if it crashes
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
#9.刷新后台服务,然后启动mysqld
echo "正在刷新后台服务,暂不启动mysqld..."
systemctl daemon-reload
systemctl enable mysqld
#10.把mysql的bin目录添加到环境变量中
echo 'export PATH=$PATH:/export/server/mysql/bin' >> /etc/profile
source /etc/profile
#11.解决mysql客户端首次无法登录问题
[ ! -f /lib64/libncurses.so.5 ] && ln -s /lib64/libncurses.so.6 /lib64/libncurses.so.5
[ ! -f /lib64/libtinfo.so.5 ] && ln -s /lib64/libtinfo.so.6 /lib64/libtinfo.so.5
#13.弹出提示,MySQL安装成功
echo "MySQL安装成功,软件安装路径:/export/server/mysql,数据库暂未初始化,数据库暂未启动!"

注意:暂时不需要初始化数据库文件,只是安装好了和master相同版本的mysql数据库软件;后面向master来同步所有数据。

master:从0-1安装MySQL,需要初始化,需要start启动服务!

slave:从0-1安装,但是又和master有所不同,不需要初始化,因为数据来源于master,不需要启动mysqld,没有数据目录本身也无法启动!

注意:以上两个脚本执行都必须通过++source mysql8.sh++

问题:./mysql8.sh、sh/bash mysql8.sh 与 source mysql8.sh执行有何不同?

./mysql8.sh、sh/bash mysql8.sh都代表直接运行脚本,这两种方式运行脚本都会产生一个子进程,程序所在终端(主进程),两者之间相互独立,子进程环境变量无法影响父进程中的环境变量。

source执行脚本时,虽然也会产生一个子进程,但是不仅会影响子进程中的环境变量,也会影响父进程中的环境变量。

3.3****修改 MySQL 主从配置(核心)

master服务器 => my.cnf

实际添加了两行

log-error=/export/server/mysql/master.err

log-bin=/export/server/mysql/data/binlog

bash 复制代码
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
port=3306
log-error=/export/server/mysql/master.err
log-bin=/export/server/mysql/data/binlog
server-id=10
character_set_server=utf8mb4
collation-server=utf8mb4_unicode_ci
EOF
配置项 中文说明
basedir=/export/server/mysql MySQL 软件安装目录
datadir=/export/server/mysql/data 数据存放目录
socket=/tmp/mysql.sock 本地通信的 socket 文件路径
port=3306 监听端口
log-error=/export/server/mysql/master.err 错误日志路径(排错全靠它)
log-bin=/export/server/mysql/data/binlog 开启二进制日志,并指定存放路径和文件名前缀
server-id=10 服务器唯一 ID,主从必须不同(主=10)
character_set_server=utf8mb4 服务端默认字符集
collation-server=utf8mb4_unicode_ci 排序规则

master主服务器配置完成后,先在主服务器端为其创建一个master.err的日志文件并授权

bash 复制代码
touch /export/server/mysql/master.err
chown mysql.mysql /export/server/mysql/master.err
systemctl restart mysqld
slave服务器 => my.cnf => 开启了relaylog(重写日志 => 把主服务器binlog重写)

主要是添加

log-error=/export/server/mysql/slave.err

log-bin=/export/server/mysql/data/binlog

relay-log=/export/server/mysql/data/relaylog 【slave特有的中继日志】

bash 复制代码
cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/export/server/mysql
datadir=/export/server/mysql/data
socket=/tmp/mysql.sock
port=3306
log-error=/export/server/mysql/slave.err
log-bin=/export/server/mysql/data/binlog
relay-log=/export/server/mysql/data/relaylog
server-id=20
character_set_server=utf8mb4
collation-server=utf8mb4_unicode_ci
EOF

从服务器配置完成后,也需要提前创建slave.err错误日志文件

bash 复制代码
touch /export/server/mysql/slave.err
chown mysql.mysql /export/server/mysql/slave.err

3.4****启动 Master 主服务器并创建同步账号

从库要来主库拉数据,得有个账号。这个账号是在 master 上创建、给 slave 用的。

命令 中文说明
CREATE USER 'slave'@'%' IDENTIFIED WITH mysql_native_password BY '123'; 创建用户 slave;'%' 表示允许从任意 IP 连接;IDENTIFIED WITH mysql_native_password 是用旧版密码认证方式,为了兼容 Navicat、DataGrip 等第三方工具(MySQL 8 默认用新的 caching_sha2_password,老工具连不上)
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%'; 授予复制权限。REPLICATION SLAVE 让从库能读取主库二进制日志;REPLICATION CLIENT 让从库能查看主库状态;ON *.* 表示所有库所有表 replication slave replication sclient

为什么要这两个权限? 目的就是让从库能读取二进制日志、并拉取日志中的数据信息。

3.5 查看主库状态(记下这两个值)

命令 中文说明
show master status; 查看主库当前 binlog 写到哪儿了

返回结果长这样:

File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set
binlog.000003 682

务必记录下 File 和 Position 的值 ,并且记下之后不要再对主库做任何写操作------因为任何写操作都会让 Position 往后跑,你记下的值就失效了

3.6 把 master 的数据目录同步到 slave

这一步的目标是让两边初始数据一致。流程是:停主库 → 删 auto.cnf → rsync 同步 → 两端启动。

命令 中文说明
systemctl stop mysqld 先在 master 上停掉 MySQL(保证同步时数据不再变动)
cd /export/server/mysql 进入 MySQL 安装目录
rm -f data/auto.cnf 删除 auto.cnf(里面存着数据库实例的 UUID,是唯一标识,不删会导致主从 UUID 冲突)
rsync -av /export/server/mysql/data node2:/export/server/mysql/ 把主库 data 目录同步到 node2。-a 表示归档模式(保留权限、时间等所有属性),-v 表示显示详细过程
chown -R mysql.mysql /export/server/mysql 在 slave 上执行,-R 递归修改整个目录属主为 mysql(同步过来的文件属主可能不对,不改启动会报权限错)

启动master和slave数据库

bash 复制代码
systemctl start mysqld

注意:我们启动master和slave机器上的mysqld时,都容易出现启动不了的情况,遇到这种情况不要急,一定先要.err错误日志 => 只要环境问题,通过日志几乎100%可以解决

新知识点 :早期都用 scp 传文件(底层是 SSH 协议),但 scp 效率较低。现在推荐用 rsync 做 Linux 之间的数据同步,rsync -av 是标配用法,而且支持增量同步。

3.7 在 slave 端建立主从关系(最关键的一段)

先在主库加锁,防止配置过程中又有新数据写进来导致两边不一致:

bash 复制代码
mysql> flush tables with read lock;

查看当前数据库的二进制日志写到什么位置

sql 复制代码
mysql> show master status;
+---------------+----------+--------------+------------------+-------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000004 |      1207|              |                  |                   |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

slave服务器执行以下操作:配置主从信息,但还未开始真正同步。

slave实现同步到master => change replication source

sql 复制代码
mysql> CHANGE REPLICATION SOURCE TO 
  SOURCE_HOST='192.168.88.101',
  SOURCE_USER='slave',
  SOURCE_PASSWORD='123',
  SOURCE_LOG_FILE='binlog.000004',
  SOURCE_LOG_POS=1207;

在slave服务器上启动同步

sql 复制代码
mysql> start slave;
mysql> show slave status\G
        ......
        Slave_IO_Running: Yes       代表成功连接到master并且下载日志
        Slave_SQL_Running: Yes      代表成功执行日志中的SQL语句
        Seconds_Behind_Master: 0    代表主从延迟的秒数,如果为0,代表状态最佳,主从没有延迟
        
;与\G都代表SQL的结尾,有所不同在于分号是横向展示,而\G把每一列纵向显示,适合大数据展示!

回到master主服务器,在mysql里面,进行解锁操作

sql 复制代码
mysql> unlock tables;

3.8 测试主从复制结果

master中创建数据库、数据表并插入数据

sql 复制代码
create database db_itheima;
use db_itheima;

create table students1(
        id int primary key,
        name varchar(20)
) default charset=utf8;

insert into students values (1, 'Tom');
insert into students values (2, 'Rose');

在slave端验证:

sql 复制代码
mysql> show databases;
mysql> use db_itheima;
mysql> show tables;
mysql> select * from students;

特别注意:一旦两者配置为主从以后,主master节点既可以读数据也可以写数据,但是一定一定要注意slave服务器只能读数据,不能写数据,一旦写入,集群主从马上报错!!!

相关推荐
车载中间件通信2 小时前
[TC8测试]TCP_FLAGS_PROCESSING_06
数据库·网络协议·tcp/ip·中间件·tc8
呆萌很2 小时前
达梦数据库(DM)和MySQL核心区别
数据库
l1t2 小时前
DeepSeek总结的pgrust以光速编程:构建 Gravity 查询引擎
数据库·人工智能·postgresql
木木_Q4057733492 小时前
Oracle 增量同步配置指南:只搬变了的数据,不重复也不漏数
数据库·oracle·数据同步·数据库同步·增量同步
升鲜宝供应链及收银系统源代码服务2 小时前
Java Spring Boot 商业版 升鲜宝供应链管理系统--商品模块表与字段分析(商品管理页面业务逻辑与数据表关系分析)
数据库·生鲜配送系统排名·生鲜配送系统架构设计·b2b订货商城解决方案·生鲜配送系统源代码出售·生鲜配送系统设计源代码
灵晔君2 小时前
【MySQL】库的操作
数据库·mysql
2603_969694853 小时前
【MySQL学习笔记】DQL(基础查询/条件/聚合/分组/排序/分页)与 DCL(用户权限)全解析
数据库·笔记·学习·mysql·学习方法
Paper数据分析-经管学术3 小时前
1985-2025年地级市绿色发明专利被引量
数据库
念何架构之路3 小时前
Go 标准库 database/sql 详解
数据库·sql·golang