主备基本原理

备库readonly

  • 备库设置为readonly模式
  • readonly设置对超级权限用户无效,而备库用于同步更新的线程就是拥有超级权限的,所以不会影响数据同步

    好处

  • 有时候一些运营的查询会放到备库去查询,只读可以防止误操作

  • 防止切换逻辑有bug,导致双写,造成主备数据不一致
  • 通过是否是readonly判断主备库

    流程

  • 在备库B上通过change master命令,设置主库的ip、端口、用户名、密码、以及从哪个位置开始请求binlog(文件名和日志偏移量)

  • 在备库B上执行start slave命令,这个时候备库会创建两个线程:io_thread、sql_thread,其中io_thread负责和主库建立连接
  • 主库A校验完用户名密码后,按照要求的数据位置,读取binlog,发送给备库B
  • 备库B拿到后放到中转文件relay_log
  • 备库B的sql_thread读取relay_log文件,解析sql并执行

    • 由于多线程复制方案的引入,sql_thread演化为里多线程

      主从延迟

      延迟时间

      数据同步时间点

  • 主库A上执行了一个事务,写入binlog,这个时刻记为T1

  • 从库B接收到这条binlog写入relay log,这个时刻记为T2
  • 从库B读取这条binlog,并执行完这个事务,这个时刻记为T3

    延迟时间计算

  • T3-T1,就是从库B的延迟时间

  • 在从库上执行show slave status命令,可以查看到这个时间,即seconds_behind_master

    seconds_behind_master计算方式

  • 每个binlog都会有一个字段记录在主库上的写入时间

  • 从库取出这个字段后,根据当前时间计算出差值,就是延迟时间了

    主备库机器时间设置不一致怎么办

  • 从库连接上主库后,会通过select unix_timestamp()函数来获取主库系统时间

  • 如果从库发现主库的系统时间和自己的不一致,则在计算seconds_behind_master时候会减去系统时间的差值

    主从延迟的原因

    从库机器性能差

  • 如果将多个从库部署在一个机器上,会导致因争抢资源,而使备库处理能力下降

  • 备库配置低,IOPS能力不及主库,导致相同的磁盘读写压力,从库处理能力不及主库

    从库压力大

  • 通常运营后台的分析SQL会在从库执行,如果分析SQL的性能很低,会消耗大量的CPU资源,也会影响同步速度

    大事务

  • 主库上必须等事务执行完,才会写入binlog,然后发送给从库,如果事务实行时间比较久,会导致同步延迟

  • 比如:一次delete删除了太多数据

    从库的并行复制能力

  • 即sql_thread的并行处理能力,低版本的MySQL只支持单线程复制

    主备切换策略

    可靠性优先策略

    切换流程

  • 判断备库B的seconds_behind_master,如果小于某个值(2s)则进行下一步,否则重试这一步

  • 将主库A设置为只读,即readonly=true
  • 判断备库B的seconds_behind_master的值,直到等于0,进入下一步
  • 将备库B改为可读可写状态,即readonly=false
  • 将业务请求切换到库B

    缺点

  • 步骤2之后,主备库都处于只读状态,直到切换完成,这段时候数据库服务不可用

  • 步骤1则是为了降低步骤3的耗时

    可用性优先策略

    切换流程

  • 上述可靠性切换流程,将4、5步调整到最开始执行

  • 即先进行主库切换,将备库的readonly设置为false,将请求切换到新的主库

    缺点

  • 可能会出现数据不一致问题

    从库并行辅助能力优化

    问题来源

  • mysql5.6之前,辅助处理relay_log的sql_thread是单线程的,在主库并发高、TPS高,同步到备份库的redolog多

  • 由于主库是多线程并发处理事务写入binlog,而备库是单线程处理主库的binlog
  • 这就会导致备库同步延迟,很难追上主库

    优化方案

    基本线程模型

    主备一致性 - 图1

    MariaDB并行复制策略

  • 利用redo log组提交的原理,“所有处于commit状态的事务可以并行”,表示已通过锁冲突检查

  • 能够在一组里提交的事务一定不会修改同一行
  • 主库上可以并行执行的事务,在从库上也可以
  • 实现
    • 在一组里提交的事务,有一个相同的commit id,下一组就是commit id +1
    • commit id直接写到binlog里
    • 传到从库上后,相同commit id的事务分发到多个work线程执行
    • 这一组执行完后,coordinator再去取下一批
  • 缺点

    • 容易被大事务拖累

      Mysql5.7的并行复制策略

  • 参数:slave-parallel-type

    • DATABASE:使用mysql5.6版本的按库并行策略
    • LOGICAL_CLOCK:使用类似MariaDB的策略

主备一致性 - 图2

  • 其实不要等redo log commit,其实党redo log处于prepared状态就表示已经通过锁冲突检验
    • mysql5.7并行复制策略思想
  • 同时处于prepared状态,在备库上也是可以并行执行的
  • 处于prepare状态的事务,和处于committed状态之间的事务,也可以在备库并行执行
  • bin_log的组提交,用于故意拉长binlog 从write到fsync的时间,提高写磁盘效率
  • bin_log的组提交,也可以用来提升备库的并行复制能力

    Mysql5.7.22的并行复制策略

  • mysql5.7.22版本中新增加了一种并行复制策略,基于WRITESET的并行复制
  • 新增加的参数binlog-transaction-dependency-tracking,用来控制是否启用这个新策略

    • COMMIT_ORDER:mysql5.7的策略
    • WRITESET:表示对事务设计更新的每一行,都计算出一个hash值(库名+表名+索引名+值),组成hash set。如果两个事务的hash set没有交集,则表明两个事务没有操作相同的行,可以并行执行
    • WRITESET SESSION:是在WRITESET基础上,增加了一个限制,即在主库上同一个线程上先后执行的两个事务,在备库上也要按照相应顺序执行

      读写分离之过期读

  • 主从延迟情况下,从库上读到的数据和主库不一致

    解决方案

    强制走主库

  • 对于必须拿到最新数据的查询请求,发送到主库

  • 对于允许拿到旧数据的,发送到从库
  • 缺点:有些时候,比如金融类的业务,所有的查询都必须是查询到最新的数据,那所有的请求都走主库,从库就没有意义了

    sleep方案

  • 在执行查询之前,先执行一条类似select sleep(1)的命令

  • 大多数情况下,主备延迟都在一秒内

    判断主备无延迟方案

    show_slave_status.jpg

  • 方法1:通过show slave status命令,拿到seconds_behand_master参数,来判断是否有延迟

  • 方法2:通过对比位点确保主备无延迟
    • Master_Log_File和Read_Master_Log_Pos,表示的是读到的主库的最新位点;
    • Relay_Master_Log_File和Exec_Master_Log_Pos,表示的是备库执行的最新位点。
  • 方法3:通过对比GTID集合确保主备无延迟
    • Auto_Position=1,表示这对主备关系使用了GTID协议。
    • Retrieved_Gtid_Set,是备库收到的所有日志的GTID集合;
    • Executed_Gtid_Set,是备库所有已经执行完成的GTID集合。
  • 对于中间状态,即主库已经发送binlog日志,但从库还没接收到binlog,这个情况并不适用

    配合semi-sync方案

  • semi-sync方案设计

    • 事务提交时候,主库将binlog发送给从库
    • 从库收到binlog后相应ack给主库
    • 然后主库才会返回客户端事务执行完成
  • 缺点:这个方案对对一主一备场景成立。当有多个从库时候,主库收到一个ack就返回了,这时候如果查询发送到的是没返回ACK的从库,那可能查询到的依然不是最新数据

    等主库位点方案

    等GTID方案