• (5.3)mysql高可用系列——mysql复制(理论篇)【续写中】


    关键词:mysql主从复制,mysql复制,MGR,mysql并行复制

    目录

    【1】mysql支持的复制类型

    【2】mysql的主从4种同步方式介绍

      (1)异步  (2)同步  (3)5.6 半同步  (4)5.7增强半同步

    【3】MGR的介绍

    【4】mysql并行复制

      【4.1】并行复制的当前大三模式

      【库间并发】【组提交】【WriteSet

    正文

    【1】mysql支持的复制类型

      基于binlog的3种模式(详情参考:binlog的3种日志记录模式),oracle在mysql5.5版本收购

      【1.1】statement:基于语句的复制(5.5.6之前默认),主服务器执行的SQL语句,在从服务器执行同样的语句

      【1.2】row:基于行的复制(5.5.7之后默认),把改变的内容复制到从库,而不是把SQL命令在从库重新执行一遍。mysql5.0就开始支持

      【1.3】mixed:混合类型的复制,默认是使用 statement 语句方式复制,一旦发现基于语句无法精确复制时(比如now() 因为主从有延迟导致数据不一致)就会采用基于 row 行的方式复制。

      

    【2】mysql的主从4种同步方式介绍

          以下图片均引用自《深入浅出mysql开发、优化与管理维护》

           

            

      【2.1】异步复制(和MSSQL的高性能模式镜像一样):(3.23 native)

           主库只管binlog dump数据到从库,不保证主从完全一致,断电、崩溃等情况会丢失数据。   

          

       【2.2】全同步复制:

          【2.2.1】核心概念:主从复制,主库要等到从库重做事务并且提交成功,接受到ACK成功确认标识后,主库才能提交事务。

          【2.2.2】与半同步的区别:半同步是只需要持久化到relay log阶段即可返回ACK成功标识给主库,而全同步需要等待从库SQL进程完整的运行完该事务内容才能返回ACK成功标识。

          【2.2.3】原理:

          主库事务写入redo log和 binlog 文件后,binlog dump线程提取binlog数据给IO线程,IO线程把数据加载回从库的relay log文件。

          从库SQL线程开启事务重做应用relay log数据操作,等从库提交完后,返回ACK确认标识,主库才会提交。

      【2.3】传统半同步复制:(自5.5开始,插件)

          【2.3.1】原理:

            master事务commit 指令已经写入binlog(注意,这里已经提交了,再去同步,只是在等一个ACK)和 binlog 文件后,binlog dump线程提取binlog数据给IO线程,IO线程把数据加载回从库的relay log文件。

          只要IO线程-》slave的relay log已经flush disk 磁盘落地,slave就返回ACK确认标识给master。

          【2.3.2】特性:

            这里的commit主库上是已经在 binlog、redo log 中提交了的,其他session都可以看到。

            但需要等待从库返回ACK确认标识才会把事务提交到存储引擎持久化(即 ibdata、ibd等磁盘数据文件)且返回到client一个commit成功的指令。

          【2.3.3】宕机情况:

            master上这个事务其实是已经提交了并且落盘master的binlog了,master的其他Session是可以看到这个提交后的事务的,而slave还没有commit。

            这个时候master挂了,master上的数据和slave不一致。master crash后,master根据redo重做提交了这个事务.

            在切换到从后,slave因为没有commit而回滚了这个事务导致数据丢失。导致主从不一致。

          

          

      【2.4】增强半同步复制(mysql 5.7.4及以上才可以用,与MSSQL镜像高安全模式相同):

          与【2.3】传统半同步复制大致一样。唯一的区别就是,在事务提交过来后:

          核心区别:主库会等从库在relay log 阶段持久化该事务到该文件后,接受ACK成功确认标识后,再进行提交(commit指令写入redo log)

            【2.4.1】传统的半同步复制:

              master 会把binlog和redo log全部写了。

            【2.4.2】增强半同步复制:

              master 只会写binlog,然后等slave的IO线程把事务持久化(flush disk)到 relay log 上后,返回ACK确认标识。

              master收到确认标识后才会commit 写入到redo log,并返回给客户端commit成功的指令。

            【2.4.3】宕机情况:

              主库上这个事务因为没有写入redo log,所以这个事务被认为是未提交。master的其他session是不可以看到这个事务的。

              这个时候主库挂了,master上的数据和slave一致,master crash后,slave不丢数据。        

      【2.5】GTID 复制(mysql 在 5.6.2 及之后开始支持GTID):

          【2.5.1】GTID(Global Transaction Identifiers)概念:

            对于一个已提交事务的编号,事务的唯一编号,并且是一个全局唯一的编号。GTID和事务会记录到binlog中,用来标识事务。

            GTID是用来替代以前,传统复制方法(binlog+position),mysql 5.6.2开始支持GTID。

            mysql支持GTID后,一个事务在集群中就不再孤单,在每一个节点中,如果存在具相同标识符的情况,可以避免同一个事务,在同一个节点出现多次的情况。

              (可以初步理解成row模式的,和statement的区别,前者记得是具体做了什么事,后者记录的是位置)

            GTID的出现最直接的效果就是,每一个事物在集群中具有了唯一性的意义,相对于行复制来讲数据安全性更高,故障切换更简单。

          【2.5.2】简单案例解释概念:

            比如,当我们一主2从,主出故障后切换到从DB1上去了,那么另外2台机器,需要重新手动构建主从;

            具体为:     

    -- 使用传统方式构建的主库宕机重新搭建
    -- 麻烦点:每台机器的Binlog名字和Postion位置点都不一样,需要重新定位文件在哪里,位置在哪里
    change master to 
    master_host='xxx',
    master_user='xxx',
    master_password='xxx',
    master_port='xxx',
    master_log_file='xxx',
    master_log_pos='xxx';
    
    -- 使用GTID方式的主库宕机重新搭建
    -- 优势点:每台机器上的GTID都是一样的,不需要管文件是哪个,位置在哪里,可以自动根据GTID定位
    change master to 
    master_host='xxx',
    master_user='xxx',
    master_password='xxx',
    master_port='xxx',
    master_auto_postion=1;

     

          【2.5.3】GTID的组成

              GTID 是由 server_uuid:Sequence_Number 组成;

                (1)server_uuid:是一个mysql实例的全局唯一表示;存放在 ${datadir}/auto.cnf

                (2)Sequence_Number:是mysql内部的一个事务的标号,一个mysql实例不会重复的序列号(保证服务器内唯一),也表示在该实例上已经提交事务的数量,并且随着事务提交而递增。

                (3)根据GTID可以知道事务最初是在哪个实例上提交的,方便故障排查和切换

       【2.6】复制之间的区别

        

    【3】MGR 群组复制

      MGR一键部署参考脚本:https://github.com/zhaowengxing/automgr

        (1)整体架构

        

        (2)replication plugin 插件

          

        (3)GCS

          

      【2.3】MGR的模式

        【2.3.1】单主模式(single primary mode)

        (1)单主模式基本架构与形式概念

        

        (2)单主模式的运行机制

          

        【2.3.2】多主同步模式

        (1)多主同步模式的冲突检测(核心是基于主键),这个检测就是certify

          

           这个冲突检测就是最开始运行原理的certify

            

          如果真有主键相同的操作执行了怎么办?先执行的提交,后执行的冲突回滚,如图

            

        (2)多主同步模式下的限制

    限制和规则
    •仅InnoDB Engine(Transactional and row level lock)
    •表必须有主键
    •gtid-mode=ON
    •binlog格式为Row-based
    •对于同一个对象执行DDL和DML应在同一成员上执行,不支持在不同服务器上执行DDL
    •不支持具有多级外键依赖关系的表,特别是已定义CASCADING外键约束的表
    •不支持“serializable”隔离级别

      【2.4】MGR的监视  

      (1)常用系统表监视  

    两个performance_schema表
    •replication_group_members
    •replication_group_member_stats
    本地成员状态
    •扩展Replication performance_schema 表
    •group_replication_recovery channel information
    •group_replication_applier channel information
    •新的全局变量
    •group_replication_primary_member

      (2)MGR群组复制的高可用性

    更好的容错度
    群组复制的高可用性
    –故障(F)所需的服务器数量(N)N = 2F + 1.
    –最多支持9个成员
    •允许4个成员故障。


    –没有脑裂的问题
    仅当大多数成员(N/2+1)在线时,群组才可用,如下图
      

      【2.5】 脑裂问题

        (1)什么是脑裂?(2)MGR为什么会出现脑裂?

          (1)什么是脑裂:我理解的就是,一个大脑控制变成了2个大脑控制,各做各的。产生了不同步,不一致的操作与选择。

          (2)MGR为什么会出现脑裂:大多数情况是因为网络连接问题。如下图

            检测网络分区:Performance_Schema.replication_group_members

            有2个可能-》《1》真就另外3台挂掉了  《2》另外3台没挂,是因为和这2台连不上了无法通信

            我们这里说《2》这种情况

            

            即,本来MGR群组的5台机器都能互相连通,突然,S3-S5这3台机器与S1-S2机器失去了连接。

            只有这S1-S2这2台机器被监控到在线,但其他3台机器是多数节点,它们也可能有连接在使用。对于S3-S5而言,S1-S2是宕机概念的。

            然后对于S3-S5而言的MGR是可以正常运行的,就把其S1-S2丢失了。

            这个时候S1-S2 这2台 查询可以正常运行,但不可以进行增删改(如果发生增删改,就无法再次回到MGR群主里了),如下图

               

            如何处理呢?强制指定节点生成一个新的MGR。

              

        【2.6】MGR读取一致性参数解析

          group_replication_consistency8.0.14之后
            •EVENTUAL,BEFORE,AFTER和BEFORE_AND_AFTER


         (1)EVENTUAL(默认值)事务在执行之前不等待先前的事务应用,也不等待其他成员应用其更改。这是8.0.14之前的

              

        (2)BEFORE

          事务将在开始执行之前等待所有先前的事务完成。这可确保此事务将在最新的数据快照上执行,而不用管是哪个成员。

            

        (3)AFTER

          事务将等待其更改已应用于其他成员。这可确保一旦此事务完成,所有后续事务都会读取包含其更改的数据库状态,而不管它们在哪个成员上执行。

            

        (4)BEFORE_AND_AFTER

          •此事务将等到:

              1)所有先前的事务在开始执行之前完成;

              2)其变更已适用于其他成员。这可确保:

                1)此事务将在最新的数据快照上执行;

                2)一旦此事务完成,所有后续事务都会读取包含其更改的数据库状态,而不管它们在哪个成员上执行

            

        【2.7】了解MGR节点状态

          参考:https://blog.csdn.net/d6619309/article/details/70432640

    【4】mysql并行复制

    【4.1】并行复制的当前大三模式

      【库间并发】【组提交】【WriteSet

    总的来说MySQL关于并行复制到目前为止经历过三个比较关键的时间结点“库间并发”,“组提交”,“写集合”;真可谓是江山代有人才出,前

      浪死在沙滩上;总的来说就后面的比前面的不知道高到哪里去了!

    库间并发】(5.6)

      库间并发的理论依据是这样的 ---- 一个实例内可能会有多个库(schema),不同的库之间没有什么依赖关系,所以在slave那边为

      每一个库(schema)单独起一个SQL线程,这样就能通过多线程并行复制的方式来提高主从复制的效率。

      这个理论听起来没问题,但是事实上一个实例也就一个业务库,所以这种库间并发就没什么作用了;也就是说这个方式的适用场景

      比较少,针对这个不足直到“组提交”才解决!

    组提交】(5.7)

      组提交的理论依据是这样的 --- 如果多个事务他们能在同一时间内提交,这个就间接说明了这个几个事务锁上是没有冲突的,

      也是就说他们各自持有不同的锁,互不影响;逻辑上我们几个事务看一个组,在slave以“组”为单位分配给SQL线程执行,这样

      多个SQL线程就可以并行跑了;而且不在以库为并行的粒度,效果上要比“库间并发”要好一些。

      这个事实上也有一些问题,因为它要求库上要有一定的并发度,不然就有可能变成每个组里面只有一个事务,这样就有串行没什么

      区别了,为了解决这个问题MySQL提供了两个参数就是希望在提交时先等一等,尽可能的让组内多一些事务,以提高并行复制的效率。

      “binlog_group_commit_sync_no_delay_count” 设置一个下水位,也就是说一个组要凑足多少个事务再提交;为子防止永远也凑不足

      那么多个事务MySQL还以时间为维度给出了另一个参数“binlog_group_commit_sync_delay”这个参数就是最多等多久,

      超过这个时间长度后就算没有凑足也提交。

      

      亲身经历呀! 这两个参数特别难找到合的值,就算今天合适,过几天业务上有点变化后,又可能变的不合适了;如果MySQL能自己

      达到一个自适应的效果就好了;这个自适用要到WriteSet才完成(WriteSet并不是通过自动调整这两个参数来完成,

      它采用了完全不同的解决思路)。

    WriteSet】(8.0)

      WriteSet解决了什么问题?当然是解决了“组提交”的问题啦! 说了和没说一个样,好下面我们来举个例子(比较学院派);假设你第一天

      更新了id == 1 的那一行,第二天你更新了id == 2 的那一行,第三天有个slave过来同步你的数据啦! 以“组提交”的尿性,这两个更新

      会被打包到不同的“组”,也就是说会有两个组;由于每个组内只有一个事务,所以逻辑上就串行了,起来! 

      身为DBA的你一可以看出来这两个事实上是可以打包到同一个组里来的,因为他们互不冲突,就算打包到同一个组也不引起数据的不

      一致。 于是你有两个办法

      办法1): 妹妹你大胆的把“binlog_group_commit_sync_no_delay_count”设置成 2,也就是说一个组至少要包含两个事务,并且把

      “binlog_group_commit_sync_delay”设置成24小时以上!如果你真的做了,你就可以回家了,你的数据库太慢了(第一条update等了一天),

      才完成!

      办法2): 叫MySQL用一本小本子记下它最近改了什么,如果现在要改的数据和之前的数据不冲突,那么他们就可以把包到同一个组;还是

      我们刚才的例子,由于第二天改的值的id==2所以它和第一天的不冲突,那么它完全可以把第二天的更新和第一天的更新打包到同一个组。

      这样组里面就有两个事务了,在slave第三天回放时就会有一种并行的效果。

      这本小本子这么牛逼可以做大一点吗?当然!binlog_transaction_dependency_history_size 这个参数就小本子的容量了;那我的MySQL

      有这本小本子吗? 如果你的mysql比mysql-5.7.22新的话,小本子就是它生来就有的。

      也就是说“WriteSet”是站在“组提交”这个巨人的基础之间建立起来的,而且是在master上做的自“适应”打包分组,所以你只要在master上

      新增两个参数

    binlog_transaction_dependency_tracking  = WRITESET                 #    COMMIT_ORDER          
    transaction_write_set_extraction        = XXHASH64

    在MySQL 8.0中,该版本引入了参数binlog_transaction_depandency_tracking用于控制如何决定事务的依赖关系。

    该值有三个选项:

    • 默认的COMMIT_ORDERE表示继续使用5.7中的基于组提交的方式决定事务的依赖关系;

    • WRITESET表示使用写集合来决定事务的依赖关系;

    • 还有一个选项WRITESET_SESSION表示使用WriteSet来决定事务的依赖关系,但是同一个Session内的事务不会有相同的last_committed值。

      理论说完了,下面我们看一下实践。

    WriteSet实践

       基于WriteSet的并行复制环境怎么搭建我这里就不说了,也就是比正常的“组提交”在master上多加两个参数,不讲了;我这里想

      直接给出两种并行复制方式下的行为变化。

      1): 我们要执行的目标SQL如下

    复制代码
    create database tempdb;
    use tempdb;
    create table person(id int not null auto_increment primary key,name int);
    
    insert into person(name) values(1);
    insert into person(name) values(2);
    insert into person(name) values(3);
    insert into person(name) values(5);
    复制代码

      2): 看一下组提交对上面SQL的分组情况

      3): 看write_set的对“组提交”优化后的情况

       可以看到各个insert是可以并行执行的,所以它们被分到了同个组(last_committed相同);last_committedsequence_number

      这两个值在binlog里面记着就有,我在解析binlog的时候习惯使用如下选项

    mysqlbinlog -vvv --base64-output='decode-rows' mysql-bin.000002

       

     【总结

      WriteSet是在“组提交”方式上建立起来的,一种新的并行复制实现;相比“组提交”来说更加灵活;当然,由于并发度上去了,相比“组提交”

      WriteSet在性能上会更加好一些,在一些WriteSet没有办法是否冲突时,能平滑过度到“组提交”模式。

    参考文章

      https://www.cnblogs.com/conanwang/p/5935568.html

      详细复制参考文章:https://mp.weixin.qq.com/s/-AL3b4DaXPBN4EKjjSYdUg

  • 相关阅读:
    HDFS datanode源码分析
    hive udaf开发入门和运行过程详解
    hive原生和复合类型的数据加载和使用
    tomcat部署web应用(转)
    HDFS namenode源码分析
    HDFS dfsclient写文件过程 源码分析
    hive中UDTF编写和使用(转)
    HDFS dfsclient读文件过程 源码分析
    MapReduce源码分析总结(转)
    DataRabbit 轻量的数据访问框架(09) -- IDataSchemaAccesser
  • 原文地址:https://www.cnblogs.com/gered/p/11299168.html
Copyright © 2020-2023  润新知