• MySQL复制


    一、复制概述

    复制是指将主库的 DDL DML 操作通过 binlog 日志传到从库上,然后在从库上对日志重新执行,使得从库和主库数据保持同步

    1.1 复制原理

    a) 主库事务提交时会把数据变更作为事件 event 记录在 binlog 日志中;MySQL主库上的 sync_binlog 参数控制 binlog 日志刷新到磁盘的频率

    b) 创建 I/O 线程连接主库,I/O 线程向主库请求数据

    c) 主库创建 binlog dump 线程读取 binlog 中的 event 。发送给 I/O 线程

    d) I/O 线程获取到 event 数据后更新到从库的 relay log

    e) 从库上的 SQL线程读取中继日志 relay log中的 event,更新到数据库

     

    二、三种binlog格式对复制的影响

    2.1 SBR

    基于语句级别的复制。binlog_format=statement,记录 SQL 语句到 binlog

    优点:

    • Binlog文件较小
    • 可用于审计

    缺点:

    • 执行insert select需要持有更多的行锁
    • update要扫表
    • auto_increment会阻塞其他insert语句
    • 对一些系统函数复制会导致主从不一致
      • Load_file()
      • Uuid()
      • User()
      • Found_rows()
      • Sysdate(),master执行时间是 00:00:00,由于有延时,到slave的执行时间是 00:00:01
      • ……

     

    2.2 RBR

    基于行级别的复制。binlog_format=row,记录每一行的变化到 binlog

    优点:

    • 在某些情况下复制速度更快(SQL复杂,表有主键)
    • 系统的特殊函数也可以复制
    • 更少的锁

    缺点

    • Binary log比较大
    • 单语句更新表的行数过多,会形成大量binlog。造成同步延迟(解决办法:使用SBR)
    • 无法从binlog看见用户执行的SQL(mysql5.6增加了一个新的eventbinlog_row_query_log_events,记录用户的query)
    • 对于非事务引擎,持有的锁比SBR多。使用RBRSLAVE上没有并行插入
    • 生成BLOB的值花费的时间比SBR

     

    行复制流程:

    a) master更新数据,写入binlog

    b) Slavemaster上注册io_thread接受日志变更

    c) Io_thread接收变更之后,会写入到本地的realy_log

    d) 通过sql_thread进行解析,执行

    e) 判断是否有主键

    f) 如果有主键,就进行主键匹配、写入

    g) 如果没有主键,就使用二级索引进行匹配

    h) 如果没有二级索引,就只能进行全表扫描

     

    2.3 MIXED

    混合使用rowstatement格式。对于DDL记录使用statement格式,对于table的行操作记录使用row格式

    binlog_format=mixed

    • 如果使用innodb表,事物级别使用READ COMMITREAD UNCOMMIT只能使用row格式
    • 但在使用ROW格式中DDL语句还是会记录成statement格式
    • Mixed模式,以下几种情况会将binlog_formatSBR改成RBR
      • DML语句更新一个NDB表时
      • 当函数中包含UUID()
      • 2个及以上包含AUTO_INCREMENT字段的表被更新时
      • 行任何INSERT DELAYED语句时
      • UDF
      •  试图中必须使用RBR。例如创建视图使用了UUID()函数

    三、GTID

    • GTID全称是global transaction identifieds(全局的事务标志)
    • 一个事物对应一个唯一ID
    • GTID组成:server_uuit + transaction_id
      • server_uuit 是全局唯一,默认保存在 $DATADIR/auto.cnf,如果有重复,删除了重启MySQL
      • transaction_id 是自增的
    • 搭建GTID需要配置以下参数
      • gtid_mode=on  
      • enforce-gtid-consistency=on  
      • log-bin=mysql-bin  

    3.1 GTID的意义

    传统复制模式下

    master 宕机后,一个 slave 被提升为 new master,如果需要继续维持复制关系,就需要把另外两个 slave change master 指向 new master。那么问题就来了,原来 slave 是指向 master filename + pos 的位置,现在要指向 new master 上新的 filename + pos 位置,这两个位置是比较难对应起来的,此时两个 slave change master 比较麻烦

     

    GTID模式下

    和上面的场景一样,两个 slave 需要重新指向 new master,由于使用了 GTID,目前 slave-A 获取到的日志对应的GTID=G_Aslave-B 获取到的日志对应GTID=G_B。此时 new_master上存在G_AG_B(通过选举出来的,获取的日志应该是最多的),那两个slave就可以直接使用 G_AG_B这两个GTID,通过指向 new master 接着继续复制

     

    3.2 GTID filename-pos 的对应

    binlog中,多了一个 GTID event,通过扫描 binlog 中的GTID值,就可以知道 GTID filename-pos的关系,如下所示

     

    +-----------------+--------+----------------+-----------+-------------+---------------------------------------------------------------------+
    | Log_name        | Pos    | Event_type     | Server_id | End_log_pos | Info                                                                |
    +-----------------+--------+----------------+-----------+-------------+---------------------------------------------------------------------+
    | mybinlog.000057 |      4 | Format_desc    |    873306 |         120 | Server ver: 5.6.37-log, Binlog ver: 4                               |
    | mybinlog.000057 |    120 | Previous_gtids |    873306 |         151 |                                                                     |
    | mybinlog.000057 |    151 | Gtid           |    873306 |         199 | SET @@SESSION.GTID_NEXT= '987c1082-8001-11e8-a03b-52540089b89e:1'   |
    | mybinlog.000057 |    199 | Query          |    873306 |         274 | BEGIN                                                               |
    | mybinlog.000057 |    274 | Table_map      |    873306 |         335 | table_id: 70 (hb.heartbeat)                                         |
    | mybinlog.000057 |    335 | Update_rows    |    873306 |         485 | table_id: 70 flags: STMT_END_F                                      |
    | mybinlog.000057 |    485 | Xid            |    873306 |         516 | COMMIT /* xid=10 */                                                 |
    | mybinlog.000057 |    516 | Gtid           |    873306 |         564 | SET @@SESSION.GTID_NEXT= '987c1082-8001-11e8-a03b-52540089b89e:2'   |
    | mybinlog.000057 |    564 | Query          |    873306 |         639 | BEGIN                                                               |
    | mybinlog.000057 |    639 | Table_map      |    873306 |         700 | table_id: 70 (hb.heartbeat)                                         |
    | mybinlog.000057 |    700 | Update_rows    |    873306 |         850 | table_id: 70 flags: STMT_END_F                                      |
    | mybinlog.000057 |    850 | Xid            |    873306 |         881 | COMMIT /* xid=16 */                                                 |
    | mybinlog.000057 |    881 | Gtid           |    873306 |         929 | SET @@SESSION.GTID_NEXT= '987c1082-8001-11e8-a03b-52540089b89e:3'   |
    +-----------------+--------+----------------+-----------+-------------+---------------------------------------------------------------------+

     

    但是如果 binlog 很大,扫描的成本会很大,所以在 binlog 的开头部分有一个 Previous_gtids event。如果我要的 GTID Previous_gtids 的大,就扫描当前文件,反之扫描之前的文件,以此类推。因为 binlog rotate 的时候,是知道当前最大的 GTID 的,可以将该值写入到下一个新的 binlog 的开头,即 Previous_gtids

     

    3.3 GTID 复制出错处理

    3.3.1 slave 上插入一行数据

    insert into a select 2;

    3.3.2 master 上插入同样数据

    insert into a select 2;

    3.3.3 查看复制状态

    root@localhost:mysql.sock  10:17:  [vcyber]>show slave statusG
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.84.87
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mybinlog.000058
              Read_Master_Log_Pos: 52587080
                   Relay_Log_File: mysql-relay-bin.000006
                    Relay_Log_Pos: 319820
            Relay_Master_Log_File: mybinlog.000058
                 Slave_IO_Running: Yes
                Slave_SQL_Running: No
                  Replicate_Do_DB: 
              Replicate_Ignore_DB: 
               Replicate_Do_Table: 
           Replicate_Ignore_Table: 
          Replicate_Wild_Do_Table: 
      Replicate_Wild_Ignore_Table: 
                       Last_Errno: 1062
                       Last_Error: Could not execute Write_rows event on table vcyber.a; Duplicate entry '2' for key 'id_2', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mybinlog.000058, end_log_pos 52584859
                     Skip_Counter: 0
              Exec_Master_Log_Pos: 52584646
                  Relay_Log_Space: 52587694
                  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: NULL
    Master_SSL_Verify_Server_Cert: No
                    Last_IO_Errno: 0
                    Last_IO_Error: 
                   Last_SQL_Errno: 1062
                   Last_SQL_Error: Could not execute Write_rows event on table vcyber.a; Duplicate entry '2' for key 'id_2', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mybinlog.000058, end_log_pos 52584859
      Replicate_Ignore_Server_Ids: 
                 Master_Server_Id: 873306
                      Master_UUID: 987c1082-8001-11e8-a03b-52540089b89e
                 Master_Info_File: /data/mysql/mysql_3306/data/master.info
                        SQL_Delay: 0
              SQL_Remaining_Delay: NULL
          Slave_SQL_Running_State: 
               Master_Retry_Count: 86400
                      Master_Bind: 
          Last_IO_Error_Timestamp: 
         Last_SQL_Error_Timestamp: 180723 10:17:19
                   Master_SSL_Crl: 
               Master_SSL_Crlpath: 
               Retrieved_Gtid_Set: 987c1082-8001-11e8-a03b-52540089b89e:1-146477
                Executed_Gtid_Set: 81e0915b-5187-11e8-b129-5254004a7fb5:1,
    987c1082-8001-11e8-a03b-52540089b89e:1-146470
                    Auto_Position: 1

    3.3.4 处理复制步骤

    a) 将 slave gtid_next 指向执行失败 gtid

        Executed_Gtid_Set: 987c1082-8001-11e8-a03b-52540089b89e:1-148588 是已经执行过的 GTIDgtid_next 的值应该是148589

    b) 执行一个空的事务,即 begin;commit;

    c) 将 gtid 还原为 automatic

    -- 设置 gtid_next 为失败的 gtid
    set gtid_next='987c1082-8001-11e8-a03b-52540089b89e:148589';
    -- 执行一个空的事务
    begin;commit;
    -- 还原为 automatic
    set gtid_next='automatic'

    3.3.5 启动 sql_thread,查看复制状态

    start slave sql_thread;
    show slave statusG

    四、并行复制

    MySQL5.7复制的前提是,所有处于 prepare 阶段的事务都是可以并行提交

    5.7 binlog 比 以前版本多了以下两个内容

    • last_committed:一个 last_commited 代表 一个组
    • sequence_number:一个 sequence_number 代表一个事务

     

    如图所示,last_committed3个值,分别是 0 1 4,这代表着 3 个组

    laster_commit = 4 中有3 sequence5 6 7,代表着 3 个事务,这 3 个事务可以并行复制。每一组中的 last_commited 值,都是上一个组中事务的 sequence_number 最大值。只要切换 binlog 文件,技术从0开始

     

    4.1 ordered_commit

    当事务提交,会经过三个阶段 FLUSH、SYNC 及 COMMIT。这三个步骤分别有各自的队列。

    4.1.1 FLUSH 阶段

    1. 首先进入 FLUSH 队列,如果某个事物加入时,队列是空的,则担任这个事务的队长,来代表其他事务执行提交操作。其他事务继续加入时,发现已经有了队长,那么就跟随队伍。
    2. 当队长将这个队列中的事物取出期间,其他事务进入FLUSH队列需要等待。
    3. 为每个事物分配一个 sequence_number。为组分配 last_committed。last_committed=sequence_number-1
    4. 将Redo log中prepare阶段的数据刷盘
    5. 将binlog数据写入文件,当然此时只是写入文件系统的缓冲,并不能保证数据库崩溃时binlog不丢失
    6. Flush阶段队列的作用是提供了Redo log的组提交
    7. 如果在这一步完成后数据库崩溃,由于协调者binlog中不保证有该组事务的记录,所以MySQL可能会在重启后回滚该组事务
    8. 等到队长取出队列中所有事务。其他事务就可以加入这个队列。

    4.1.2 SYNC阶段

    这里为了增加一组事务中的事务数量,提高刷盘收益,MySQL使用两个参数控制获取队列事务组的时机:

    • binlog_group_commit_sync_delay=N:在等待N μs后,开始事务刷盘(图中Sync binlog)
    • binlog_group_commit_sync_no_delay_count=N:如果队列中的事务数达到N个,就忽视binlog_group_commit_sync_delay的设置,直接开始刷盘(图中Sync binlog)

    Sync阶段队列的作用是支持binlog的组提交
    如果在这一步完成后数据库崩溃,由于协调者binlog中已经有了事务记录,MySQL会在重启后通过Flush 阶段中Redo log刷盘的数据继续进行事务的提交

    4.1.3 commit阶段

    1. 首先获取队列中的事务组
    2. 依次将Redo log中已经prepare的事务在引擎层提交(图中InnoDB Commit)
    3. Commit阶段不用刷盘,如上所述,Flush阶段中的Redo log刷盘已经足够保证数据库崩溃时的数据安全了
    4. Commit阶段队列的作用是承接Sync阶段的事务,完成最后的引擎提交,使得Sync可以尽早的处理下一组事务,最大化组提交的效率

     

    4.2 多线程复制分发原理

    1. 从库 SQL thread 拿到一个新事务,取出 last_committed 及 sequence_number 值
    2. 判断当前 last_committed 是否大于当前已经执行的 sequence_number 的最小值
    3. 如果大于,则说明上一个组的事务还没有完成。此时等待最小值变大,知道 last_committed与最小值相等,才可以继续
    4. 如果小于或等于,则说明当前事务与正在执行的组是同一个组,不需要等待
    5. SQL thread 通过统计,找到一个空闲的 worker 线程,如果没有空闲则等待;
    6. 将当前事务打包,交给 worker去 APPLY这个事务

    4.3 并行复制相关参数

    • slave-parallel-type:并行模式
      • LOGICAL_CLOCK:逻辑时钟
      • DATABASE:基于库级别
    • slave-parallel-workers:并行复制的线程
    • slave_preserve_commit_order:对于多线程slaves,来保障事务在slave上执行的顺序与relay log中的顺序严格一致,只有当“slave_parallel_workers”开启时有效;此时“log_bin”、“log_slave_updates”必须开启,而且“slave_parallel_type”值必须为“LOGICAL_CLOCK”(默认值为DATABASE)。即当多线程开启时,且根据relay log中事务的逻辑顺序执行statements,是否需要严格保持顺序,默认值为0表示并发执行忽略顺序
    • binlog_group_commit_sync_no_delay_count:等待一组里面有多少事物我才提交
    • binlog_max_flush_queue_time:等待多少时间后才进行组提交

    五、复制相关参数

    • log-bin:是否打开binlog日志,不可动态修改
    • server-id:数据库唯一的标识。以后可能会被server-uuid取代(gtid)
    • log-bin-index:指定mysql-logbin.index的位置
    • binlog_format:binlog日志格式:statement、row、mixed三种。可动态修改
    • binlog_cache_size:binlog写入的buffer,可以动态修改。通常配置成1M
    • max_binlog_size:指单个binlog文件的大小。默认是1G
    • sync_binlog:多少个SQL后,刷新binlog到disk。默认是0系统自动刷新,0是性能最好的。如果是交易型的,建议是1,1是每个SQL刷新到磁盘中。1是性能最差的。
    • expire_logs_days:指N天后自动删除binlog。默认没有开启,就会遇到binlog把磁盘占满
    • log_bin_trust_function_creators:存储过程调用到的。假如存储过程在测试环境创建的,导出之后就带着用户名。到线上的时候就调用不了,这时候打开这个参数就好了。
    • log_warnings:异常的断开连接Abort connection,拒绝连接请求。默认是0。等于1是不会记录。
    • binlog_error_action:默认是不开启,可以开启binlog_error_action=ABORT_SERVER。假如磁盘满了,binlog写不进去,就会在应用层直接报错。这个参数是5.6之后才有
    • binlog_row_image :5.6之后的参数。在用row的时候,日志很大。binlog_row_image=[full|minimal|noblob]
      • full: update tb set c2=5 where id=1;会记录变更之前的记录,还有之后的记录。默认值
      • minimal:假如update tb set c2=xxx where id=xxx;假如一个表有10个字段,然后值update c2字段,只记录c2跟主键。
      • noblob:如果没有涉及到blob字段的update,就不记录blob
    • binlog_rows_query_log_events=1|0:默认是关闭的,row格式下只记录具体变更的内容。开启这个参数后,会把用户执行的SQL写入到binlog
    • slave-skip-errors=1062,1053,1146 #跳过指定error no类型的错误

    -- GTID相关的参数

    • gtid_mode=on:打开GTID
    • log-slave-updates : 从库上也会记录binlog
    • enforce-gtid-consistency:控制GTID,哪些语句可以记录到LOG中。
    • gtid_next:跳过下一个事物
    • gtid_purged:进行同步的时间,忽略之前的数据。gtid_purged=uuid:1000;忽略1000以前个事物,需要reset master才能用。
    • gtid_executed:GTID到底执行过多少事物

     

    -- SLAVE相关的参数

    • server-id:数据库的唯一标识
    • relay-log:slave存放的日志路径
    • relay-log-index
    • read-only :只读,但是对super用户没  有作用
    • log-slow-slave-statements: sql_thread 执行sql超过long_query_time 会记到慢日志中
    • log_slave_updates
    • max_relay_log_size:设置relay日志的大小
    • relay-log-info-file:假如从库挂掉了,同步接不上了。从relay.info上读取信息。从这些信息向master重新请求。
    • relay_log_info_repository:table --SQL thread crash safe – SQL线程安全
    • relay-log-recover:1 --I/O thread crash safe – IO线程安全
    • master_info_repository = TABLE
    • read_only = 1
    • binlog_gtid_recovery_simpliefied :默认是不开启。假如从库挂了,重新启动slave,会从新主库拉一下relay log。
    • relay_log_purge
    • relay_log_recovery 
    • replicate-same-server-id :默认是不开启。复制自己相同的server-id。建议不开启
    • skip-slave-start:从库启动之后,不自动开启同步。
    • slave_transaction_retries:执行SQL语句的时间,把区间给锁了,sql_theard就要等这些锁的释放。就会有一个重试的过程,当超过这个重试的次数,就会报错。这时候start slave sql_thread就可以修复
    • slave_parallel_workers:5.6的参数。并行复制,默认是关闭;如果有2个库,就可以定义slave_parallel_workers=2
    • relay_log_space_limit:sql_thread=NO, IO_THREAD=YES,会发现本地的relay log越来越多,磁盘使用就会越来越多。限定relay占用磁盘的大小。
    • sync_master_info:master info存到数据库的表里面。默认情况下是1000秒,如果在5.6.17之前,配置成1,就会遇到内存溢出的错误。
    • sync_relay_log_info :relay info 存到数据库的表里面。
    • slave_net_timeout:从库跟主库多少时间断开,才认为是连不上主库。默认是3600秒。推荐配置成10秒
    • slave_skip_errors:slave上的参数,忽略错误。不建议忽略错误
    • sql_slave_skip_counter:忽略多少个复制事件,遇到个别错误(主键冲突、记录不存在等)时,可以忽略这些事件,继续复制进程。一般一次只忽略一个事件,除非很肯定,否则不要设置大于1

    六、复制过滤规则

    -- master端:

    • binlog-do-db = "abc":只记录 abc 库的binlog
    • binlog-ignore-db = "abc":不记录 abc 库的binlog

    -- slave端

    • replicate-do-db = "abc":只想记录某个DB的binlog
    • replication-ignore-db="mysql":忽略某个库
    • replicate-do-table = wubx.tb;:只记录某个表
    • replicate-ignore-table=wubx.quota:忽略某个表
    • replicate-rewrite-db='a_db->b_db':把A库复制到B库
    • replicate-wild-do-table="wubx.ds_%":只记录ds开头的表

    七、复制维护管理

    7.1 查看从库状态

    root@localhost:mysql.sock  17:35:  [(none)]>show slave statusG
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.84.88
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mybinlog.000002
              Read_Master_Log_Pos: 2967
                   Relay_Log_File: mysql-relay-bin.000008
                    Relay_Log_Pos: 359
            Relay_Master_Log_File: mybinlog.000002
                 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: 2967
                  Relay_Log_Space: 532
                  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: 883306
                      Master_UUID: 81e0915b-5187-11e8-b129-5254004a7fb5
                 Master_Info_File: /data/mysql/mysql_3306/data/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
                      Master_Bind: 
          Last_IO_Error_Timestamp: 
         Last_SQL_Error_Timestamp: 
                   Master_SSL_Crl: 
               Master_SSL_Crlpath: 
               Retrieved_Gtid_Set: 
                Executed_Gtid_Set: 6e6cd1a5-5187-11e8-b129-52540089b89e:1-11
                    Auto_Position: 1

    io_thread: master_log_file , read_master_log_pos 

    sql_thread: relay_master_log_file, exec_master_log_pos

    比较一下是不是同步完成?

    master_log_file = relay_master_log_file 

    read_master_log_pos = exec_master_log_pos 

    7.2 主从一致性数据校验与修复

    pt-table-checksunm:利用语句级别复制,主从库上对表分段进行校验,如果值不一样,说明不同步。     

    pt-table-sync:利用语句级别复制,所有的修复都是在主库上修复。

    八、Replication crash safe

    8.1 SQL thread crash safe

    8.1.1 relay_log_info_repository 为 FILE 情况下

    如果将 relay_log_info_repository 设置为 FILE,MySQL会把回放记录在 relay-info.log 文件中,其中包含 SQL 线程回放到的 relay_log 和 relay_log_pos,以及对应的 master 的 master_log 和 master_log_pos

    1. SQL 线程回放 event
    2. 将回放到的 binlog 文件名和位置写到 relay-info.log 文件
    3. 参数 sync_relay_log_info = 10000 (fsync) 代表每回放 10000个 event,写一次 realy-info.log,如果设置为 1,则表示每回放一个event,就写一次relay-info.log,那么性能会很差。而且设置为 1 后,即使性能上可以接受,可还是会丢失最后一次的操作。

    当一个Slave节点在复制数据时,可能发生如下情况, 数据2和数据3写入成功(且已经落盘),但是relay-info.log 中的记录还是数据1的位置(因为sync_relay_log_info 的关系,此时还没有fsync),此时Slave宕机,然后重启,便会产生如下的状况:

    1. Slave的库中存在数据2和数据3
    2. Slave读取 relay-info.lo g中的 Relay_log_name 和 Relay_log_pos ,此时记录的是回放到数据1的位置
    3. Slave 从数据1开始回放,继续插入数据2和数据3
    4. 但是,此时的数据库中存在数据2和数据3 ,于是发生了1062 的错误(重复记录)

    8.1.2 relay_log_info_repository 为 TABLE 情况下

    在 MySQL5.6 之后,将 relay_log_info_repository 设置为 TABLE,relay-info 写到 mysql.slave_relay_log_info 这张表中

    设置为 TABLE 的原理为,将 event 的回放 和 relay-info 的更新放在同一个事物里面,变成原子操作,从而保证一致性

    BEGIN;
         apply log event;
         apply log event;
    UPDATE mysql.slave_relay_log_info
         SET Master_log_pos = Exec_Master_Log_Pos,
              Master_log_name = Relay_Master_Log_File,
              Relay_log_name = Relay_Log_File,
              Relay_log_pos = Relay_Log_Pos;
    COMMIT;

    8.2 I/O thread crash safe

    IO thread 也是接受一个个的 event,将接受到的 event,通过设置参数 master_info_repository 将 master-info(IO线程接受到的位置,master_log_name 和 master_log_pos) 信息写入到文件或者数据库表中,然后将接受到的 event 写入 relay log file。

    参数 sync_master_info = 10000 表示每接收 10000 个 event,写一次 master-info

    假设存在下面这个情况,event2和event3已经写入到了relay-log,但是master-info还没有同步到master-info.log。此时如果服务宕机后,MySQL重启,I/O线程会读取master-info.log的内容,读取到的位置为event1的位置,然后I/O线程会继续将event2和event3拉取过来,然后继续写入到relay-log 中。event2 和 event3 被重复写入到了relay-log文件中,当SQL线程回放时,就会产生1062 的错误(重复记录),看到的现象还是SQL线程报错,IO线程正常,但其实是因为 IO线程重复写入了。

    8.2.1 master_info_repository

    IO thread 同样也提供了 master_info_repository参数,TABLE 记录数据库表,FILE 记录文件。但是可以解决 crash 问题吗?

    答案是不可以,为什么?

    因为 IO thread 写的是 relay-log 文件吧,即使 master-info 设置为 TABLE 也无法解决宕机问题。

    但是将 master-info-repository 设置为 TABLE,可以提高性能

    设置为TABLE 后, master-info 将信息保存到mysql.slave_master_info 中。

    那么 IO thread crash应该怎么解决?

    relay-log-recover

    8.2.2 relay-log-recover

    relay-log-recover = 1,该参数表示 IO thread crash 重启之后,把当前接收的所有 relay-log 都删除掉,然后以 SQL thread 应用到的位置,重新去 master 请求日志

    注意:以上参数都是 5.6 之后才有

     

    九、复制延迟

    9.1 Seconds_Behind_Master

    Seconds_Behind_Master 字面意思为Slave落后Master的秒数。计算的方式为: Slave的SQL线程执行时刻的时间减去 event产生时刻的时间(event_header 中的前 4个字节就是timestamp )。字面意思:当 sql_thread 执行 event 的时候才会计算。那么当 io_thread 延迟或者 stop。那么这时候,sql_thread 已经把接收到的 relay_log应用完,那么Seconds_Behind_Master 为0,但是实际上,主从有延时。所以 Seconds_Behind_Master 只能计算 sql_thread的延迟,无法计算 io_thread。那么有什么办法解决这个问题?答案是:pt-heartbeat

     

    9.2 pt-heartbeat

    9.2.1 pt-heartbeat 的原理

    在 Master 节点上创建一个带有时间戳字段的表, pt-heartbeat --update 每隔一段时间写入时间戳,通过主从复制,将表中的内容(带有时间戳的记录)复制到Slave节点,通过运行pt-heartbeat --monitor 的服务器的本地时间戳减去复制到Slave节点的记录的时间戳,就可知道Slave落后Master的时间

     

    9.2.2 使用pt-heartbeat

    # 主库:

    pt-heartbeat --update -u'root' -p'abc#1234' -S '/data/mysql/mysql_3306/tmp/mysql.sock' -D hb --create-table --daemonize

    -D:指定数据库

    --create-table:创建存储时间戳的表

    --daemonize:后台运行

     

    # 从库:

    pt-heartbeat --monitor -u'root' -p'abc#1234' -S '/data/mysql/mysql_3306/tmp/mysql.sock' --master-server-id 873306 -D hb

    0.00s [ 0.00s, 0.00s, 0.00s ]

    0.00s [ 0.00s, 0.00s, 0.00s ]

    0.00s [ 0.00s, 0.00s, 0.00s ]

     

    # monitor 从slave的 hb.heartbeat中找到master同步过来的记录,然后和本地系统时间做差值计算,得到落后时间

     

    9.2.3 主从延迟的几个可能原因

    1. 主从配置不一致,包括硬件,参数等
    2. 主库 TPS 太高,从库单线程回放
    3. 主库运行了大事务
    4. row 模式下没有主键最为致命
    5. 主库运行了 DDL 语句造成延时

     

    参考书籍:《MySQL运维内存》

    参考链接:https://mp.weixin.qq.com/s/_LK8bdHPw9bZ9W1b3i5UZA

  • 相关阅读:
    虚拟化技术一些概念整理
    更改KVM虚拟机root的密码
    文件作为块设备访问
    KVM虚拟机IO处理过程(二) ----QEMU/KVM I/O 处理过程
    KVM虚拟机IO处理过程(一) ----Guest VM I/O 处理过程
    KVM的初始化过程
    linux删除文件未释放空间问题处理
    cgroup测试存储设备IOPS分配
    数组中的逆序对
    VMware Tools安装方法及解决无法全屏显示问题
  • 原文地址:https://www.cnblogs.com/ziroro/p/9958974.html
Copyright © 2020-2023  润新知