18-MySql主从(原理)

MySql主从(原理)

一、目的

  • 高可用:主库不可用时,切换到从库提供服务;
  • 扩展负载能力:主库写,从库提供读,对于读多写少的场景可以大幅度提高并发读能力;(需要注意主从延迟对实时性数据读取的影响)
  • 数据备份:主从模式自动备份,而且备份从库可以降低对主库的影响;

PS:因为主从同步是异步的,因此对于实时性要求高的数据,读从库可能会有数据不一致的问题,因此实时性高的数据还是需要查主库,反之可以查从库

二、原理

2.1 主从同步线程

Master会创建一个binlog dump线程(也可以叫IO线程、slave连接到master的时候创建)

  • IO线程。当slave连接到master时master会为slave开启binlog dump线程。当master 的 binlog发生变化的时候,binlog dump线程会通知slave(Push模式),并将相应的binlog内容发送给slave。(一个master 有多少个slave,就有多少个binlog dump线程)

slave上会创建2个线程(start slave的时候启动)

  • I/O线程。slave 创建IO线程负责与 master连接,接收master发过来的binlog内容并将内容写入到本地的relay log。
  • SQL线程。检测Relay Log中新增了内容后解析该 Log 文件中的内容并应有对应的语句到Slave数据库。

使用SHOW PROCESSLIST命令可以查看线程:

  • Master
//主库,去掉了无关线程的显示
mysql> show processlist\G;
*************************** 2. row ***************************
     Id: 16
   User: root
   Host: localhost
     db: NULL
Command: Query
   Time: 0
  State: starting
   Info: show processlist
*************************** 3. row ***************************
     Id: 18
   User: slave
   Host: 172.17.0.3:49218
     db: NULL
Command: Binlog Dump
   Time: 10294
  State: Master has sent all binlog to slave; waiting for more updates
   Info: NULL
  • Slave:
//从库,去掉了无关线程的显示
mysql> show processlist\G;
*************************** 1. row ***************************
     Id: 4
   User: root
   Host: localhost
     db: NULL
Command: Query
   Time: 0
  State: starting
   Info: show processlist
*************************** 2. row ***************************
     Id: 16
   User: system user
   Host: 
     db: NULL
Command: Connect
   Time: 10402
  State: Waiting for master to send event
   Info: NULL
*************************** 3. row ***************************
     Id: 17
   User: system user
   Host: 
     db: NULL
Command: Connect
   Time: 10204
  State: Slave has read all relay log; waiting for more updates
   Info: NULL
  • 注意:为了保证 slave crash 重启后够知道复制到了什么地方以便从库的io线程和sql线程仍能够知道从哪里继续复制,slave 还创建两个日志文件 master.info 和 relay-log.info 用来保存复制进度。master.info 里面会记录 master 的 binlog文件名称,IP,端口,账号密码,UUID等信息,relay-log.info 文件会记录relay log文件名、Relay_Log_Pos、Read_Master_Log_Pos等信息,如下:
mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.17.0.2
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 30
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 2425
               Relay_Log_File: edu-mysql-relay-bin.000002
                Relay_Log_Pos: 2091
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 2425
              Relay_Log_Space: 2302
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 100
                  Master_UUID: 5722550e-2319-11ea-b15e-0242ac110002
             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 more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 



root@d8a4501c94b7:/var/lib/mysql# cat master.info 
25
mysql-bin.000001
2425
172.17.0.2
slave
123456
3306
30
0





0
30.000

0
5722550e-2319-11ea-b15e-0242ac110002
86400


0


root@d8a4501c94b7:/var/lib/mysql# cat relay-log.info 
7
./edu-mysql-relay-bin.000002
2091
mysql-bin.000001
2425
0
0
1
  • 这里文件内部有些是空行;

  • 主从同步示意图:

在这里插入图片描述

1.Slave的IO线程连接Master,请求获取指定日志文件的指定位置之后的binlog日志;

2.Master的 binlog dump 线程将指定的 binlog信息发给Slave,除日志信息外,还包括本次返回的信息在 Master端的Binary Log 的日志文件名称以及在Binary Log中的位置;

3.Slave的IO线程接收到信息后将日志内容写入RelayLog 文件(mysql-relay-bin.xxxxxx)的末端,并将读取到的Master端的bin-log的文件名和位置记录到master-info,以便下一次读取时能告诉Master从何处开始同步日志。

4.Slave的SQL线程检测到Relay Log新增了内容后解析Log文件生成对应的sql语句,并应用这些sql到数据库,保证主从数据一致性。

2.2 bin-log主从同步的三种形式

  • binl-log中主从同步事件有3种记录形式:statement、row、mixed。推荐使用ROW模式,具体参考 10-MySql 日志-binlog

2.3 主从架构

一主一从:一主一从,最简单的主从架构
一主多从:扩展从节点,增加从节点的并发读取能力(从节点多,则Master上的Dump线程多,而且binlog是Master Push到Slave,过多从节点可能导致Master负载过大)
多级复制:解决从节点过多Master 的Dump线程负载高的问题
双主复制:两个MySql实例互为主从

2.4 循环复制

  • 在双Matser 模式下,两个MySql实例互为主从,切换时不需要修改主从关系,但是有一个问题,一个A上的操作记录到binlog后,binlog发给B,B执行后也会产生binlog,这个binlog再同步给A,(log_slave_updates 参数控制主备模式下Slave 执行中继日志后是否产生binlog),此时A不能再执行这个语句,否则会一直循环下去。
  • 通过server id解决该问题,不同实例server id必须不同,数据库实例日志中会记录server id,这样A生成的binlog在B执行值,B生成binlog后,在binlog里记录的server id还是A的server id,这样再到A之后,A就不会执行对应的binlog;

2.5 思考

  • 为什么Slave是双线程?
早期Slave是单线程,但是容易引起延迟的问题。 
  • binlog的推送是拉模式还是推模式?为什么?
推模式,推模式更加及时,不过会带来问题,Master的Slave太多可能造成Master负载过高(由此有了多级复制)

三、问题和挑战

  • Mysql 主从同步有两个问题,一个是数据安全,一个是同步延迟;这两个问题都源于主从同步的异步性,Master完成一个事物时不能保证这个事物的日志应用到了Slave,由此有可能引起主库执行完后机器故障导致数据丢失,延迟问题在于主库写入的数据有可能无法在从库立刻读取到,这个时差超出可忍受的范围;

3.1 主从同步延迟?

  • 主库执行完事物后写入binglog时刻记为t1,这份binlog需要传输给Slave,B收到之后记为t2,然后Slave执行完这个事物记为t3,局域网内很快一般t2-t1比较小,但是t3-t2可能会有延迟,在Slave 通过 show slave status 的 seconds_behind_master参数可以查看主从同步的延迟时间(t3 - t1),这里slave 已经扣除了主从系统的时间差异,看到的值就是主从延迟时间;这里分析主从延迟就要来自于Slave消费中继日志的速度比主库生产binlog的速度慢;
  • 主从延迟的关键在于Master的操作是并行的Slave是串行的,比如Master上面有20条针对不同数据的修改,假设有索引,这些锁是互不干扰的(行锁),但是在Slave虽然锁不干扰,但是20个得一条一条的串行执行,由此在某些场景下
    可能导致主从延迟,比如TPS比较高超出了一个Slave线程所能承受的范围,另外如果是一些读写分离的场景,可能slave上的某些查询导致锁表,导致SQL线程写入慢;
Master并发 -- Slave串行;
Slave的读可能造成锁的争用引起SQL线程写入慢;

mysql> show slave status\G; 中的 Seconds_Behind_Master 可以查看主从同步的延迟;
1.3.1 延迟原因
  • 原因1:主从机器性能不一致:Slava机器性能差
  • 原因2:从库有大量费时的读操作;
  • 原因3:大事物;比如大事物操作在主库执行了10min,但是在主库上,因为是行锁,这个事物加锁的行不影响主库上同时并发的其他操作,但是备库是单线程,这10min备库执行中级日志时候不能做其他的事情,一个是并行,一个是串行,这是最大的区别;大事物场景比如大量删除历史数据,大表的DDL
1.3.2 应对方法
  • 原因1方案:主从可能发生切换,因此建议主从对称部署
  • 原因2方案:一主多从,多个从节点分摊读压力,或者通过binlog输出到外部系统,利用专门的计算组件分析计算
  • 原因3方案:避免大事物,使用多线程复制,分库(将主库拆分为多个库,降低每个库的写并发就减少了几倍,此时主从延迟可以减少。)
  • 另外为提高Slave的写入性能,某些时候可以考虑将从库的刷盘策略调整,主库通常配置sync_binlog=1 和 innodb_flush_log_at_trx_commit = 1,从库则可以考虑
    配置为0;(这个方法在安全性高的时候不可取)

3.2 主从同步数据安全?

  • MySQL半同步模式:半同步复制,用于解决数据安全的问题
  • 半同步模式要求bing-log提交后至少传输到一个从库,通过这样的方式保证数据安全,显然对性能有一定的影响,比如在网络异常,从库卡死的时候;
  • 半同步复制在主库写入 binlog日志之后,会强制将binlog立即将同步到从库,从库将日志写入自己本地的 relay log 之后,接着会返回一个 ack 给主库,主库接收到至少一个从库的 ack 之后才会认为写操作完成了。(类似于Kafka分区的机制,MongoDB副本集的写入模式 – 性能与安全的权衡)

四、并行复制

  • 并行复制可以按照更细的粒度让多个线程来应用中继日志(单个SQL线程 -> 一组线程);

  • 单线程复制模式下SQL线程读取中继日志,多线程复制模式下,分为 coordinator 线程和 worker线程,coordinator 和之前的SQL线程类似 负责读取中继日志并分发事物,worker 线程负责接受coordinator分发的事物并应用到数据库,worker 数量由参数 slave_parallel_workers 控制;

  • 另外:coordinator分发事物的时候需要保证:同一行数据的多个事物需要分发给一个worker执行(不同线程顺序不能保证),一个事物的多个步骤需要分发给一个worker;

4.1、并行复制策略

4.1.1 按表分发
  • 按表分发事物,不同的事物操作不同的表,各表负载比较均衡的时候效果很好,但是这种策略不能解决热点表问题;
  • 原理:不同的worker用hash表保存自己需要执行的事物和事物需要修改的表的关系,coordinator 分配事物时,如果待分配事物 T 不与任意一个worker冲突,那就分配给最空闲的worker,如果与一个worker 冲突,就分配给这个worker ,如果与1个以上的worker 冲突,coordinator 就进入等待状态,直到冲突的worker 降低为一个;
4.1.2 按行分发
  • 按行分发策略粒度更细,只要不是修改同一行,则可以通过worker并行执行,要求binlog必须是ROW模式
  • 采用模式和按表分发类似,不过hash的键值对粒度更细,并且要求:binlog必须是ROW模式、表必须有主键、不能有外键,也会消耗更多计算资源

4.2、MySql并行复制

4.2.1 5.6版本
  • MySql 5.6 的并行复制策略粒度是库级别,原理和按表分发模式类似,不过粒度更大(一个库只有一个线程)。不过如果只有一个数据库,相当于单线程模型,比较适用于多数据库且数据库压力比较均衡的场景
4.2.2 5.7版本
  • 5.7并行复制支持单个库多线程,通过 slave-parallel-type 配置来切换模式,DATABASE代表库模式,和5.6一样,LOGICAL_CLOCK则支持单个库多个线程,线程数由配置slave_parallel_workers决定;
--单线程模式:
show processlist; -- 查看是单线程复制

show variables like 'slave_parallel_type'; -- 默认DATABASE 

show variables like 'slave_parallel_workers'; -- 默认0

--配置多线程模式:
stop slave;  -- 先停止Slave

set global slave_parallel_type='logical_clock'; -- 设置为 LOGICAL_CLOCK

set global slave_parallel_workers=4; -- 设置单库并行复制的线程数量

start slave; -- 启动 Slave

show processlist; -- 查看是多线程复制
  • 注意线程数量需要结合具体场景配置(业务/设备核心等);

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值