一文带你搞懂MySQL主从复制与读写分离(原理分析和暴力实战!)

Posted 李志宽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文带你搞懂MySQL主从复制与读写分离(原理分析和暴力实战!)相关的知识,希望对你有一定的参考价值。

前言:

我们在平时工作中,使用最多的数据库就是 mysql 了,随着业务的增加,如果单单靠一台服务器的话,负载过重,就容易造成宕机

这样我们保存在 MySQL 数据库的数据就会丢失,那么该怎么解决呢?

在很多项目,特别是互联网项目,在使用MySQL时都会采用主从复制、读写分离的架构。

其实在 MySQL 本身就自带有一个主从复制的功能,可以帮助我们实现负载均衡和读写分离

对于主服务器(Master)来说,主要负责写,从服务器(Slave)主要负责读,这样的话,就会大大减轻压力,从而提高效率。

为什么要采用主从复制读写分离的架构?如何实现?有什么缺点?

接下来,跟着俺一起来看看它都有哪些核心知识点呢:

简介

随着业务的增长,一台数据服务器已经满足不了需求了,负载过重。这个时候就需要减压了,实现负载均衡读写分离,一主一丛或一主多从。

主服务器只负责写,而从服务器只负责读,从而提高了效率减轻压力。

主从复制可以分为:

  • 主从同步:当用户写数据主服务器必须和从服务器同步了才告诉用户写入成功,等待时间比较长。

  • 主从异步:只要用户访问写数据主服务器,立即返回给用户。

  • 主从半同步:当用户访问写数据主服务器写入并同步其中一个从服务器就返回给用户成功。

思维导图:

为什么使用主从复制、读写分离

主从复制、读写分离一般是一起使用的。目的很简单,就是为了提高数据库的并发性能。你想,假设是单机,读写都在一台MySQL上面完成,性能肯定不高。如果有三台MySQL,一台mater只负责写操作,两台salve只负责读操作,性能不就能大大提高了吗?

所以主从复制、读写分离就是为了数据库能支持更大的并发。

随着业务量的扩展、如果是单机部署的MySQL,会导致I/O频率过高。采用主从复制、读写分离可以提高数据库的可用性。

主从复制的原理

①当Master节点进行insert、update、delete操作时,会按顺序写入到binlog中。

②salve从库连接master主库,Master有多少个slave就会创建多少个binlog dump线程。

③当Master节点的binlog发生变化时,binlog dump 线程会通知所有的salve节点,并将相应的binlog内容推送给slave节点。

④I/O线程接收到 binlog 内容后,将内容写入到本地的 relay-log。

⑤SQL线程读取I/O线程写入的relay-log,并且根据 relay-log 的内容对从数据库做对应的操作。

 

如何实现主从复制

我这里用三台虚拟机(Linux)演示,IP分别是104(Master),106(Slave),107(Slave)。

预期的效果是一主二从,如下图所示:

形式

一主一从

图片

一主一从

一主多从

图片

一主多从

一主一从和一主多从是我们现在见的最多的主从架构,使用起来简单有效,不仅可以实现 HA,而且还能读写分离,进而提升集群的并发能力

多主一从

图片

多主一从

多主一从可以将多个 MySQL 数据库备份到一台存储性能比较好的服务器上。

双主复制

图片

双主复制

双主复制,也就是可以互做主从复制,每个 master 既是 master,又是另外一台服务器的 salve。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。

级联复制

图片

级联复制

级联复制模式下,部分 slave 的数据同步不连接主节点,而是连接从节点

因为如果主节点有太多的从节点,就会损耗一部分性能用于 replication ,那么我们可以让 3~5 个从节点连接主节点,其它从节点作为二级或者三级与从节点连接,这样不仅可以缓解主节点的压力,并且对数据一致性没有负面影响。

原理

MySQL 主从复制是基于主服务器在二进制日志跟踪所有对数据库的更改。因此,要进行复制,必须在主服务器上启用二进制日志。

每个从服务器从主服务器接收已经记录到日志的数据。当一个从服务器连接到主服务器时,它通知主服务器从服务器日志中读取最后一个更新成功的位置。

从服务器接收从那时发生起的任何更新,并在主机上执行相同的更新。然后封锁等待主服务器通知的更新。

从服务器执行备份不会干扰主服务器,在备份过程中主服务器可以继续处理更新。

Master配置

使用命令行进入mysql:

mysql -u root -p

接着输入root用户的密码(密码忘记的话就网上查一下重置密码吧~),然后创建用户:

//192.168.0.106是slave从机的IP
GRANT REPLICATION SLAVE ON *.* to 'root'@'192.168.0.106' identified by 'Java@1234';
//192.168.0.107是slave从机的IP
GRANT REPLICATION SLAVE ON *.* to 'root'@'192.168.0.107' identified by 'Java@1234';
//刷新系统权限表的配置
FLUSH PRIVILEGES;

 创建的这两个用户在配置slave从机时要用到。

接下来在找到mysql的配置文件/etc/my.cnf,增加以下配置:

# 开启binlog
log-bin=mysql-bin
server-id=104
# 需要同步的数据库,如果不配置则同步全部数据库
binlog-do-db=test_db
# binlog日志保留的天数,清除超过10天的日志
# 防止日志文件过大,导致磁盘空间不足
expire-logs-days=10 

 Slave配置

Slave配置相对简单一点。从机肯定也是一台MySQL服务器,所以和Master一样,找到/etc/my.cnf配置文件,增加以下配置:

# 不要和其他mysql服务id重复即可
server-id=106

接着使用命令行登录到mysql服务器:

mysql -u root -p

 然后输入密码登录进去。

进入到mysql后,再输入以下命令:

CHANGE MASTER TO 
MASTER_HOST='192.168.0.104',//主机IP
MASTER_USER='root',//之前创建的用户账号
MASTER_PASSWORD='Java@1234',//之前创建的用户密码
MASTER_LOG_FILE='mysql-bin.000001',//master主机的binlog日志名称
MASTER_LOG_POS=862,//binlog日志偏移量
master_port=3306;//端口

 还没完,设置完之后需要启动:

# 启动slave服务
start slave;

 启动完之后怎么校验是否启动成功呢?使用以下命令:

show slave status\\G;

 可以看到如下信息(摘取部分关键信息):

*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.0.104
                  Master_User: root
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 619
               Relay_Log_File: mysqld-relay-bin.000001
                Relay_Log_Pos: 782
        Relay_Master_Log_File: mysql-bin.000001 //binlog日志文件名称
             Slave_IO_Running: Yes //Slave_IO线程、SQL线程都在运行
            Slave_SQL_Running: Yes
             Master_Server_Id: 104 //master主机的服务id
                  Master_UUID: 0ab6b3a6-e21d-11ea-aaa3-080027f8d623
             Master_Info_File: /var/lib/mysql/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
                Auto_Position: 0

 另一台slave从机配置一样,不再赘述。

测试主从复制

在master主机执行sql:

CREATE TABLE `tb_commodity_info` (
  `id` varchar(32) NOT NULL,
  `commodity_name` varchar(512) DEFAULT NULL COMMENT '商品名称',
  `commodity_price` varchar(36) DEFAULT '0' COMMENT '商品价格',
  `number` int(10) DEFAULT '0' COMMENT '商品数量',
  `description` varchar(2048) DEFAULT '' COMMENT '商品描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

 接着我们可以看到两台slave从机同步也创建了商品信息表:
在这里插入图片描述

主从复制就完成了!

读写分离

主从复制完成后,我们还需要实现读写分离,master负责写入数据,两台slave负责读取数据。怎么实现呢?

实现的方式有很多,以前我公司是采用AOP的方式,通过方法名判断,方法名中有get、select、query开头的则连接slave,其他的则连接master数据库。

但是通过AOP的方式实现起来代码有点繁琐,有没有什么现成的框架呢,答案是有的。

Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy两部分组成。

ShardingSphere-JDBC定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

读写分离就可以使用ShardingSphere-JDBC实现。

在这里插入图片描述

过程

工作过程

MySQL 的主从复制工作过程大致如下:

  1. 从库生成两个线程,一个 I/O 线程,一个 SQL 线程;

  2. I/O 线程去请求主库的 binlog,并将得到的 binlog 日志写到 relay log(中继日志) 文件中;

  3. 主库会生成一个 log dump 线程,用来给从库 I/O 线程传 binlog;

  4. SQL 线程会读取 relay log 文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致;

图片

工作过程

请求流程

MySQL 建立请求的主从的详细流程如下:

  1. 当从服务器连接主服务器时,主服务器会创建一个 log dump 线程,用于发送 binlog 的内容。在读取 binlog 的内容的操作中,会对象主节点上的 binlog 加锁,当读取完成并发送给从服务器后解锁。

  2. 当从节点上执行 start slave 命令之后,从节点会创建一个 IO 线程用来连接主节点,请求主库中更新 binlog。IO 线程接收主节点 binlog dump 进程发来的更新之后,保存到 relay-log 中。

  3. 从节点 SQL 线程负责读取 realy-log 中的内容,解析成具体的操作执行,最终保证主从数据的一致性。

类型

异步复制

一个主库,一个或多个从库,数据异步同步到从库。

图片

异步复制

这种模式下,主节点不会主动推送数据到从节点,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理。

这样就会有一个问题,主节点如果崩溃掉了,此时主节点上已经提交的事务可能并没有传到从节点上,如果此时,强行将从提升为主,可能导致新主节点上的数据不完整。

同步复制

在 MySQL cluster 中特有的复制方式。

当主库执行完一个事务,然后所有的从库都复制了该事务并成功执行完才返回成功信息给客户端。

因为需要等待所有从库执行完该事务才能返回成功信息,所以全同步复制的性能必然会收到严重的影响。

半同步复制

在异步复制的基础上,确保任何一个主库上的事物在提交之前至少有一个从库已经收到该事物并日志记录下来。

图片

半同步复制

介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到 relay log 中才返回成功信息给客户端(只能保证主库的 Binlog 至少传输到了一个从节点上),否则需要等待直到超时时间然后切换成异步模式再提交。

相对于异步复制,半同步复制提高了数据的安全性,一定程度的保证了数据能成功备份到从库,同时它也造成了一定程度的延迟,但是比全同步模式延迟要低,这个延迟最少是一个 TCP/IP 往返的时间。所以,半同步复制最好在低延时的网络中使用。

半同步模式不是 MySQL 内置的,从 MySQL 5.5 开始集成,需要 master 和 slave 安装插件开启半同步模式。

延迟复制

在异步复制的基础上,人为设定主库和从库的数据同步延迟时间,即保证数据延迟至少是这个参数。

方式

MySQL 主从复制支持两种不同的日志格式,这两种日志格式也对应了各自的复制方式。当然也有二者相结合的混合类型复制。

语句复制

基于语句的复制相当于逻辑复制,即二进制日志中记录了操作的语句,通过这些语句在从数据库中重放来实现复制。

这种方式简单,二进制文件小,传输带宽占用小。但是基于语句更新依赖于其它因素,比如插入数据时利用了时间戳。

因此在开发当中,我们应该尽量将业务逻辑逻辑放在代码层,而不应该放在 MySQL 中,不易拓展。

特点

  • 传输效率高,减少延迟。

  • 在从库更新不存在的记录时,语句赋值不会失败。而行复制会导致失败,从而更早发现主从之间的不一致。

  • 设表里有一百万条数据,一条sql更新了所有表,基于语句的复制仅需要发送一条sql,而基于行的复制需要发送一百万条更新记录

行数据复制

基于行的复制相当于物理复制,即二进制日志中记录的实际更新数据的每一行。

这样导致复制的压力比较大,日志占用的空间大,传输带宽占用大。但是这种方式比基于语句的复制要更加精确

特点

  • 不需要执行查询计划。

  • 不知道执行的到底是什么语句。

  • 例如一条更新用户总积分的语句,需要统计用户的所有积分再写入用户表。如果是基于语句复制的话,从库需要再一次统计用户的积分,而基于行复制就直接更新记录,无需再统计用户积分。

混合类型的复制

一般情况下,默认采用基于语句的复制,一旦发现基于语句无法精确复制时,就会采用基于行的复制。

配置

配置主要要点如下:

# 如果在双主复制结构中没有设置ID的话就会导致循环同步问题
server_id=1

# 即日志中记录的是语句还是行更新或者是混合
binlog_format=mixed

# 在进行n次事务提交以后,Mysql将执行一次fsync的磁盘同步指令。将缓冲区数据刷新到磁盘。
# 为0的话由Mysql自己控制频率。
sync_binlog=n

# 为0的话,log buffer将每秒一次地写入log file中并且刷新到磁盘。
# mysqld进程崩溃会丢失一秒内的所有事务。
# 为1的话,每次事务log buffer会写入log file并刷新到磁盘。(较为安全)
# 在崩溃的时候,仅会丢失一个事务。
# 为2的话,每次事务log buffer会写入log file,但一秒一次刷新到磁盘
innodb_flush_logs_at_trx_commit=0

# 阻止从库崩溃后自动启动复制,给一些时间来修复可能的问题,
# 崩溃后再自动复制可能会导致更多的问题。并且本身就是不一致的
skip_slave_start=1 

# 是否将从库同步的事件也记录到从库自身的bin-log中
# 允许备库将重放的事件也记录到自身的二进制日志中去,可以将备库当做另外一台主库的从库
log_slave_update 

# 日志过期删除时间,延迟严重的话会导致日志文件占用磁盘
expire_logs_days=7

问题

延迟

当主库的 TPS 并发较高的时候,由于主库上面是多线程写入的,而从库的SQL线程是单线程的,导致从库SQL可能会跟不上主库的处理速度

解决方法

  • 网络方面:尽量保证主库和从库之间的网络稳定,延迟较小;

  • 硬件方面:从库配置更好的硬件,提升随机写的性能;

  • 配置方面:尽量使 MySQL 的操作在内存中完成,减少磁盘操作。或升级 MySQL5.7 版本使用并行复制;

  • 建构方面:在事务中尽量对主库读写,其它非事务的读在从库。消除一部分延迟带来的数据库不一致。增加缓存降低一些从库的负载。

数据丢失

当主库宕机后,数据可能丢失。

解决方法

使用半同步复制,可以解决数据丢失的问题。

注意事项

MySQL 需要注意以下事项:

  • MySQL 主从复制是 MySQL 高可用性,高性能(负载均衡)的基础;

  • 简单,灵活,部署方式多样,可以根据不同业务场景部署不同复制结构;

  • 复制过程中应该时刻监控复制状态,复制出错或延时可能给系统造成影响;

  • MySQL 主从复制目前也存在一些问题,可以根据需要部署复制增强功能。

作用

主从复制带来了很多好处,当我们的主服务器出现问题,可以切换到从服务器;可以进行数据库层面的读写分离;可以在从数据库上进行日常备份。还可以保证:

  1. 数据更安全:做了数据冗余,不会因为单台服务器的宕机而丢失数据;

  2. 性能大大提升:一主多从,不同用户从不同数据库读取,性能提升

  3. 扩展性更优:流量增大时,可以方便的增加从服务器,不影响系统使用;

  4. 负载均衡:一主多从相当于分担了主机任务,做了负载均衡

应用场景

MySQL 主从复制集群功能使得 MySQL 数据库支持大规模高并发读写成为可能,同时有效地保护了物理服务器宕机场景的数据备份

横向扩展

将工作负载分发到各 Slave 节点上,从而提高系统性能。

在这个场景下,所有的写(write)和更新(update)操作都在 Master 节点上完成;所有的读( read)操作都在 Slave 节点上完成。通过增加更多的 Slave 节点,便能提高系统的读取速度。

数据安全

数据从 Master 节点复制到 Slave 节点上,在 Slave 节点上可以暂停复制进程。可以在 Slave 节点上备份与 Master 节点对应的数据,而不用影响 Master 节点的运行。

数据分析

实时数据可以在 Master 节点上创建,而分析这些数据可以在 Slave 节点上进行,并且不会对 Master 节点的性能产生影响。

远距离数据分布

可以利用复制在远程主机上创建一份本地数据的副本,而不用持久的与Master节点连接。

拆分访问

可以把几个不同的从服务器,根据公司的业务进行拆分。通过拆分可以帮助减轻主服务器的压力,还可以使数据库对外部用户浏览、内部用户业务处理及 DBA 人员的备份等互不影响

福利:

你的点赞是我创作的最大动力~

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了

为了感谢读者们,我想把我收藏的一些学习干货贡献给大家,回馈每一个读者,希望能帮到你们。

各位朋友们可以关注+评论一波 然后扫描下方二维码  备注:CSDN 即可免费获取资料

拒绝做一条咸鱼,我是一个努力让大家记住的死肥仔。我们下期再见!!!

 

以上是关于一文带你搞懂MySQL主从复制与读写分离(原理分析和暴力实战!)的主要内容,如果未能解决你的问题,请参考以下文章

一文搞懂│mysql 中的备份恢复分区分表主从复制读写分离

一文搞懂│mysql 中的备份恢复分区分表主从复制读写分离

新手编程之旅:MySQL中的关键点!一文带你搞懂它

一直搞不清楚什么是读写分离,主从复制的原理,今天总算搞懂了

一文带你搞懂“缓存策略”

一文带你搞懂RPC核心原理