Mysql 主从复制和读写分离实战

文章目录


前言

MySQL 主从复制与读写分离是高可用架构核心方案,通过数据同步与请求分流,解决单库性能瓶颈、提升数据安全性,是企业级应用必备的数据库优化手段。


一、mysql主从复制和读写分离背景

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

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

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

二、主从复制

1、主从复制原理

2、两日志和三进程介绍

日志:二进制日志 、中继日志

线程:dump线程 、I/O 线程 、sql线程

1、在每个事务更新数据完成之前,Master 在二进制日志(Binary log)记录这些改变。写入二进制

日志完成后,Master 通知存储引擎提交事务。

2、Slave 将 Master 的复制到其中继日志(Relay log)。首先slave 开始一个工作线程(I/O),I/O 线程在 Master 上打开一个普通的连接,然后开始 Binlog dump process。Binlog dump process 从Master 的二进制日志中读取事件,如果已经跟上 Master,它会睡眠并等待 Master 产生新的事件,I/O线程将这些事件写入中继日志。

3、SQL slave thread(SQL从线程)处理该过程的最后一步,SQL线程从中继日志读取事件,并重放其中的事件而更新 Slave 数据,使其与 Master 中的数据一致,只要该线程与 I/O 线程保持一致,中继日志通常会位于 OS 缓存中,所以中继日志的开销很小。

复制过程有一个很重要的限制,即复制在 Slave 上是串行化的,也就是说 Master 上的并行更新操作不能在 Slave 上并行操作。

3、mysql复制类型

基于语句的复制(STATEMENT, MySQL默认类型)

基于行的复制(ROW)

混合类型的复制(MIXED)

4、MySQL主从复制延迟

1、master服务器高并发,形成大量事务

2、网络延迟

3、主从硬件设备导致 。cpu主频、内存io、硬盘io

优化:

1、从库使用高性能主机。包括cpu强悍、内存加大。避免使用虚拟云主机,使用物理主机,这样提升了i/o方面性。

2、从库使用SSD磁盘

3、网络优化,避免跨机房实现同步

解决方法

1、半同步复制- 解决数据丢失的问题

2、并行复制---解决从库复制延迟的问题

5、Mysql应用场景

mysql 数据库主要的性能是读和写,一般场景来说读请求更多。

根据主从复制可以演变成读写分离,因为读写分离基于主从复制,使用读写分离从而解决高并发的问题。

6、MySQL 同步方式

1、异步复制(Async Replication)

2、同步复制(sync Replication)

3、半同步复制(Async Replication)

4、增强半同步复制(lossless Semi-Sync Replication)、无损复制

6.1、异步复制(Async Replication)

主库将更新写入Binlog日志文件后,不需要等待数据更新是否已经复制到从库中,就可以继续处理更多的请求。Master将事件写入binlog,但并不知道Slave是否或何时已经接收且已处理。在异步复制的机制的情况下,如果Master宕机,事务在Master上已提交,但很可能这些事务没有传到任何的Slave上。假设有Master->Salve故障转移的机制,此时Slave也可能会丢失事务。MySQL复制默认是异步复制,异步复制提供了最佳性能。

6.2、同步复制(Sync Replication)

主库将更新写入Binlog日志文件后,需要等待数据更新已经复制到从库中,并且已经在从库执行成功,然后才能返回继续处理其它的请求。同步复制提供了最佳安全性,保证数据安全,数据不会丢失,但对性能有一定的影响。

6.3、半同步复制(Semi-Sync Replication)

库提交更新写入二进制日志文件后,等待数据更新写入了从服务器中继日志中,然后才能再继续处理其它请求。该功能确保至少有1个从库接收完主库传递过来的binlog内容已经写入到自己的relay log里面了,才会通知主库上面的等待线程,该操作完毕。半同步复制,是最佳安全性与最佳性能之间的一个折中。

MySQL 5.5版本之后引入了半同步复制功能,主从服务器必须安装半同步复制插件,才能开启该复制功能。如果等待超时,超过rpl_semi_sync_master_timeout参数设置时间(默认值为10000,表示10秒),则关闭半同步复制,并自动转换为异步复制模式。当master dump线程发送完一个事务的所有事件之后,如果在rpl_semi_sync_master_timeout内,收到了从库的响应,则主从又重新恢复为增强半同步复制。

ACK (Acknowledge character)即是确认字符。

6.4、增强半同步复制(lossless Semi-Sync Replication、无损复制)

增强半同步是在MySQL 5.7引入,其实半同步可以看成是一个过渡功能,因为默认的配置就是增强半同步,所以,大家一般说的半同步复制其实就是增强的半同步复制,也就是无损复制。 增强半同步和半同步不同的是,等待ACK时间不同rpl_semi_sync_master_wait_point = AFTER_SYNC(默认)

半同步的问题是因为等待ACK的点是Commit之后,此时Master已经完成数据变更,用户已经可以看到最新数据,当Binlog还未同步到Slave时,发生主从切换,那么此时从库是没有这个最新数据的,用户看到的是老数据。

增强半同步将等待ACK的点放在提交Commit之前,此时数据还未被提交,外界看不到数据变更,此时如果发送主从切换,新库依然还是老数据,不存在数据不一致的问题。

三、mysql主从复制实战

1、服务规划

环境部署:cetos7.9

服务器:

master服务器:192.168.10.105

slave1服务器:192.168.10.106

slave2服务器:192.168.10.107

2、主从服务器时间同步

2.1、外网环境同步阿里云

master节点

bash 复制代码
#1关闭防火墙和增强服务
systemctl stop firewalld.service
setenforce 0
#2下载依赖包和同步阿里云时间
yum -y install ntpdate ntp
ntpdate ntp.aliyun.com
#3开启ntpd服务(会自动周期性同步阿里云时间)
systemctl start ntpd

slave节点

bash 复制代码
#1关闭防火墙和增强服务
systemctl stop firewalld.service
setenforce 0
#2下载依赖包和同步阿里云时间
yum -y install ntpdate ntp
ntpdate ntp.aliyun.com
#3开启ntpd服务(会自动周期性同步阿里云时间)
systemctl start ntpd

2.2、内网环境slave同步master

master节点

bash 复制代码
#1关闭防火墙和增强服务
systemctl stop firewalld.service
setenforce 0
#2下载依赖包和同步阿里云时间
yum -y install ntpdate ntp
ntpdate ntp.aliyun.com
#3开启ntpd服务(会自动周期性同步阿里云时间)
systemctl start ntpd
#4配置本机为ntpd服务的时间同步源
viM /etc/ntp.conf
server 127.127.1.0 #设置本机为时间同步源
#设置本机的时间层级为10级
fudge 127.127.1.0 stratum 10

为什么设置本机的时间层级为10级?

NTP 服务有个 "自我校验逻辑":如果一个源的层级设置得 "名不副实"(比如本地时钟设为 1 级),服务会觉得 "你明明是个不准的本地源,还敢冒充顶级源",直接拒绝启动,或者启动后报警告。

等级越大,时间偏移越大,越不可信。

slave节点

bash 复制代码
#1关闭防火墙和增强服务
systemctl stop firewalld.service
setenforce 0
#2下载依赖包
yum -y install ntpdate ntp
#3同步master节点的时间
ntpdate 192.168.10.105
#4设置周期同步任务
#yum install cronie -y
crontab -e
*/10 * * * * /usr/sbin/ntpdate 192.168.10.105

验证内网slave同步master的结果:

失败:

原因:开启了ntpd服务,占用了端口。

解决:关闭ntpd服务,再用slave同步master

成功同步:

最后,编译安装mysql,见以前文章

3、配置主从同步

3.1、master服务器配置

bash 复制代码
vi /etc/my.cnf
#1、配置文件,进行主从复制
server-id = 1 #主服务器id为1(不可重复)
log_bin=master-bin #开启二进制日志文件(之后生成的日志名为master-bin)
log_slave-updates=true #开启从服务器日志同步
#MIXED 表示「混合模式」,兼顾性能和数据一致性
binlog-format=MIXED # MySQL 控制「二进制日志(binlog)记录格式」的参数
#2、重启服务
systemctl restart mysqld
#3、登入数据库
mysql -uroot -p123456
#4、给从服务器提权,允许使用slave的身份复制master的所有数据库的所有表,并指定密码为123456
#如果 'myslave'@'192.168.10.%' 这个用户不存在,创建。存在即更新
#REPLICATION SLAVE主从复制场景的「专用权限」
#仅用于从节点连接主节点、读取主节点的 binlog 日志(实现数据同步),无其他操作权限。
GRANT REPLICATION SLAVE ON *.* TO 'myslave'@'192.168.10.%' IDENTIFIED BY
'123456';
#5刷新权限表
flush privileges;
#6查看数据库状态
#产生了master-bin.000001日志文件,定位为603
#slave需要定位到此处进行复制(重要)
show master status;

日志文件名称和定位后续配置slave节点都需要用到

3.2、slave服务器配置

bash 复制代码
#1设置数据库配置文件
vi /etc/my.cnf
#写入
log_bin=master-bin #开启二进制日志文件
server_id = 22 #设置server_id。slave1为22,slave2 为23
relay-log=relay-log-bin # #从主服务器上同步日志文件记录到本地
relay-log-index=slave-relay-bin.index #定义relay-log的位置和名称(index索引)
relay_log_recovery = 1 #保证了relay-log 的完整性
#2重启mysqld和登入mysql
systemctl restart mysqld
mysql -uroot -p123456
#3给「从节点」绑定「主节点」的同步信息
change master to 
master_host='192.168.10.105',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=603;
#4开启从节点
start slave;
#5、查看从服务器状态
show slave status\G;

注意:

1、解析「从节点」绑定「主节点」的同步信息的sql语句。

相当于给从节点配置 "主节点的联系方式和同步起点",执行后才能通过 START SLAVE 启动同步。

bash 复制代码
CHANGE MASTER TO
master_host='192.168.10.16',    -- 主节点IP(你要同步的主服务器地址)
master_user='myslave',          -- 连接主节点的用户名(之前授权的复制专用用户)
master_password='123456',       -- 连接主节点的密码(该用户的登录密码)
master_log_file='master-bin.000001',  -- 从主节点的哪个binlog日志文件开始同步
master_log_pos=603;             -- 从该binlog文件的哪个位置(偏移量)开始同步

master_log_file='master-bin.000001'

master_log_pos=603;

从主节点的show master status;查看。

2、解析relay_log_recovery = 1

当 slave 从库宕机后,假如 relay-log 损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的 relay-log,并且重新从 master 上获取日志,这样就保证了relay-log 的完整性。默认情况下该功能是关闭的,将 relay_log_recovery 的值设置为 1 时, 可在 slave 从库上开启该功能,建议开启。

验证配置生效:

bash 复制代码
-- 1. 验证 relay_log_recovery 配置是否生效
show variables like 'relay_log_recovery';
-- 预期输出:Value = 1(表示已启用)

-- 2. 验证主从同步状态(核心看 2 个 Yes + 延迟 0)
show slave status\G;

若 Slave_IO_Running=Yes、Slave_SQL_Running=Yes、Seconds_Behind_Master=0,则配置生效且同步正常。

查看从节点状态:

3.3、测试数据同步

bash 复制代码
master:
create database sjj_yjs;
show databases;
slave1:
show databases;
slave2:
show databases;


四、mysql读写分离

1、读写分离原理

读写分离就是只在主服务器上写,只在从服务器上读。基本的原理是让主数据库处理事务性操作,而从数据库处理 select 查询。数据库复制被用来把主数据库上事务性操作导致的变更同步到集群中的从数据库。

2、什么是读写分离

读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。

3、为什么要读写分离呢?

因为数据库的"写"(写10000条数据可能要3分钟)操作是比较耗时的。

但是数据库的"读"(读10000条数据可能只要5秒钟)。

所以读写分离,解决的是,数据库的写入,影响了查询的效率。

4、什么时候要读写分离?

数据库不一定要读写分离,如果程序使用数据库较多时,而更新少,查询多的情况下会考虑使用。利用数据库主从同步,再通过读写分离可以分担数据库压力,提高性能。

5、主从复制与读写分离

在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。有点类似于rsync,但是不同的是rsync是对磁盘文件做备份,而mysql主从复制是对数据库中的数据、语句做备份。

6、企业 使用MySQL 读写分离场景

基于程序代码内部实现

基于中间代理层实现

Amoeba

mycat

五、Mysql读写分离实战

1、实验环境和服务器信息

环境部署 cetos7.9

虚拟机服务环境

Master服务器:192.168.10.105

slave1服务器:192.168.10.106

Slave2服务器:192.168.10.107

Amoeba服务器:192.168.10.104 jdk1.6、Amoeba

客户端服务器:192.168.10.103 mysql 测试

注意:读写分离必须在主从复制做好后进行

2、Amoeba服务器配置

192.168.10.104 配置

bash 复制代码
#1 安装java环境
#因为 Amoeba 基于是 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,高版本不建议使用。
cd /opt/
#上传jdk的bin文件并复制。上传文件下载相关依赖yum install -y lrzsz
cp jdk-6u14-linux-x64.bin /usr/local/
cd /usr/local/
chmod +x jdk-6u14-linux-x64.bin # 设置文件可执行权限
./jdk-6u14-linux-x64.bin # 一直按回车,最后输入yes,完成安装
# mv a/ /b/c 表示a目录下的所有内容,移动到c目录
# mv a /b/c  表示a目录替换成c目录
mv jdk1.6.0_14/ /usr/local/jdk1.6 
# 打开配置文件,写入jdk环境变量,注意amoeba的环境变量要正确
vim /etc/profile 
export JAVA_HOME=/usr/local/jdk1.6
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin
export AMOEBA_HOME=/usr/local/amoeba # 后续安装的amoeba会在此路径下
export PATH=$PATH:$AMOEBA_HOME/bin
source /etc/profile # 生效配置文件
# 查看jdk是否安装成功
java -version

#2 安装amoeba软件
mkdir /usr/local/amoeba
cd /opt/
#上传amoeba文件并解压
tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
chmod -R 755 /usr/local/amoeba/
# 执行amoeba程序。如显示amoeba start|stop说明安装成功
/usr/local/amoeba/bin/amoeba

#3 配置 Amoeba读写分离,两个 Slave 读负载均衡
#先在Master、Slave1、Slave2 的mysql上开放权限给 Amoeba 访问
#即192.168.10.105、192.168.10.106、192.168.10.107的mysql上创建test用户
grant all on *.* to test@'192.168.10.%' identified by '123456';
#再回到amoeba服务器(192.168.10.104)配置amoeba服务:
cd /usr/local/amoeba/conf/
#3.1配置amoeba配置文件。备份配置文件
cp amoeba.xml amoeba.xml.bak

#设置配置文件的内容,输入:set nu
vim amoeba.xml
# 修改配置
--30行--
<property name="user">amoeba</property>
--32行--
<property name="password">123456</property>
--115行-进行修改--
<property name="defaultPool">master</property>
# 去注释
--117-去掉注释-
#写请求池
<property name="writePool">master</property>
#读请求池
<property name="readPool">slaves</property>

#4 配置数据库文件.备份
cp dbServers.xml dbServers.xml.bak
vim dbServers.xml #修改数据库配置文件
--23行--注释掉 作用:默认进入test库 以防mysql中没有test库时,会报错
<!-- <property name="schema">test</property> -->
--26--修改,使用mysql用户
<property name="user">test</property>
--28-30--去掉注释
<property name="password">123456</property>
--45--修改,设置主服务器的名Master
<dbServer name="master" parent="abstractServer">
--48--修改,设置主服务器的地址
<property name="ipAddress">192.168.10.16</property>
--52--修改,设置从服务器的名slave1
<dbServer name="slave1" parent="abstractServer">
--55--修改,设置从服务器1的地址
<property name="ipAddress">192.168.10.14</property>
--58--复制上面6行粘贴,设置从服务器2的名slave2和地址
<dbServer name="slave2" parent="abstractServer">
<property name="ipAddress">192.168.10.15</property>
--65行--修改
<dbServer name="slaves" virtual="true">
--71行--修改
<property name="poolNames">slave1,slave2</property>

/usr/local/amoeba/bin/amoeba start& #启动Amoeba软件,按ctrl+c 返回
netstat -anpt | grep java #查看8066端口是否开启,默认端口为TCP 8066

验证结果:

1、jdk安装验证。输入java -version

2、启动amoeba

3、启动amoeba验证结果

成功!!!!

3、测试读写分离

客户端192.168.10.103下载mariadb并启用

注意:这里只是用mariadb的远程连接功能,与Navicat 类似

bash 复制代码
yum install -y mariadb-server mariadb
systemctl start mariadb.service
# 在客户端服务器上测试
#通过amoeba服务器代理访问mysql ,在通过客户端连接mysql后写入的数据只有主服务会记录,然后同步给从服务器
mysql -u amoeba -p123456 -h 192.168.10.104 -P8066

验证如下:

第一步:在主服务器创建库sjj_db和表test

create database sjj_db;

use sjj_db;

create table test(id int(10),name varchar(10),address varchar(20));

第二步:停止slave(stop slave;), 在sjj_db库插入数据

在slave1上:

use sjj_db;

insert into test values('1','sjj1','is_slave1');

在slave2上:

use sjj_db;

insert into test values('2','sjj2','is_slave2');

在主服务器上:

use sjj_db;

insert into test values('3','sjj3','is_master');

第三步:验证slave1和slave2

在客户端服务器上

use sjj_db

select * from test;

结果:每次查询结果都进行了轮询

第四步:客户端上写入数据,只有主服务器显示

insert into test values('4','qianqi','is_client');

客户端无法查询到之前写入的数据

直接去master服务器查询,可以查到

第五步:重新启用slave

在两个从服务器上执行 start slave; 即可实现同步在主服务器上添加的数据。

在客户端进行查询,结果如图所示:


总结

本文从原理、类型、同步方式深入解析主从复制与读写分离,结合外网 / 内网实战场景落地配置,验证数据同步与请求分流效果,为企业级数据库架构优化提供可直接复用的方案。

相关推荐
尘埃落定wf12 分钟前
Python 数据库迁移:Alembic 太重?自己动手搭一套轻量版
数据库·python·mysql
Bert.Cai31 分钟前
MySQL IFNULL()函数详解
数据库·mysql
eggrall35 分钟前
MySQL数据类型
数据库·mysql
HalvmånEver1 小时前
MySQL事务(二)
数据库·mysql
会编程的土豆1 小时前
mysql数据类型
数据库·mysql
南境十里·墨染春水1 小时前
linux 学习进展 mysql 事务详解
linux·学习·mysql
南境十里·墨染春水1 小时前
linux学习进展 mysql视图详解
linux·学习·mysql
思麟呀2 小时前
MySQL的库和表的操作
数据库·mysql
思麟呀2 小时前
初始MySQL数据库
服务器·数据库·mysql
HalvmånEver2 小时前
MySQL视图
linux·数据库·学习·mysql·视图