一、异步复制
异步复制原理图
- master:
- Dump_Thread,通知IO_Thread数据变更
- slave:
- IO_Thread,拉取binlog增量
- SQL_Thread,SQL逻辑重放
异步复制流程
- master 写undo、redo
- master 发起commit,写binlog(filename,position)
- master 完成提交事务
- slave 通过IO_Thread拉取binlog的filename,position,并写入到本地的relaylog中
- slave 通过SQL_Thread逻辑重放relaylog中的SQL(单线程)
两种场景
- 实时同步(IO_Thread主从没有延迟),写数据之前通知IO_Thread有变更,IO_Thread拉取binlog增量
- 主从长时间延迟(全备恢复),IO_Thread直接去主库本地磁盘拉取binlog增量
复制中的坑
-
row模式下,表最好是有主键,其次要有普通索引。否则sql_thread重放需要全表扫描匹配,速度非常慢(mysql5.7中做了优化)
二、半同步复制(MySQL 5.5 after commit)
半同步复制流程图
半同步复制流程
- master 写undo、redo
- master 发起commit,sync binlog(filename,position)
- master 存储引擎commit完成
- master_sender_thread 等待slave_reciver_thread返回ack(等待过程中会阻塞下一个事务)
- master_sender_thread接收到slave_reciver_thread返回的ack,并返回给客户端,客户端才可以继续操作
after带来的三个问题
- 性能问题:AFTER_COMMIT半同步是单线程处理的,master把事务发送完毕后,要接受和处理slave的ack应答,处理完ack后才能继续发送下一个事务,对性能影响比较大
- master commit完成后才开始等待slave的ACK,其实这个时候在master上事务已经提交完成并且其他客户端已经可以读到,只是提交该事物的客户端处于等待状态。
- 如果master等待ack时master crash,而slave又未接收到该事务的话,那么切换到从库后就会出现读取的结果不一致的情况(因为主库已commit而从库未收到该事务binlog)
三、增强半同步复制(MySQL 5.7 after sync)
增强半同步复制流程图
增强半同步复制流程
- master 写undo、redo
- master 发起commit,sync binlog(filename,position)
- master 通过单独的semisync_reciver_thread等待slave_reciver_thread返回ack,此过程中master_sender_thread可以处理其他事务的请求
- semisync_reciver_thread接收到slave_reciver_thread返回的ack,并返回给客户端
- master 存储引擎层commit
增强半同步解决的问题
- AFTER_SYNC采用双工处理,master采用单独semisync_reciver_thread处理ack应答,不阻塞其他事务,提升性能
- AFTER_SYNC等待ack的操作是在引擎层commit之前处理,避免了其他客户端脏读
- 在等待ack的期间master crash,由于master引擎层未commit,如果slave未接受到该事务,那么数据是一致的
增强半同步带来的新问题
极端情况:在master sync binlog(写入xid)后,且在发送日志之前,这个时间master crash了。那么slave是没有拿到master的binlog增量的,而master重启后的crash recovery会认为该事务已写到binlog中,然后进行重做。这样就会导致主从不一致
5.7增强半同步配置
- master
SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%';
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
set global rpl_semi_sync_master_enabled=ON;
- slave
install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
set global rpl_semi_sync_slave_enabled=ON;
- 主要参数
- mster:
- rpl_semi_sync_master_enabled=ON 表示在master上已经开启半同步复制模式。
- rpl_semi_sync_master_timeout=10000 该参数默认为10000毫秒,即10秒,可以调整,表示如果主库在某次事务中等待事件超过10秒,则降级为异步复制模式,不再等待slave,如果master探测到slave恢复,则会自动回到半同步模式。
- rpl_semi_sync_master_wait_no_slave=ON 表示是否允许master每个事务提交后都要等待slave的确认信号,默认是ON,即每一个事务都会等待,如果是OFF,则slave追赶上之后,也不会回到半同步模式。
- rpl_semi_sync_master_trace_level=32 表示开启半同步复制模式时的调试级别,默认是32
- slave:
- rpl_semi_sync_slave_enabled=ON
- rpl_semi_sync_master_trace_level=32
四、并行复制
并行复制的演变
- MySQL 5.6 是基于库级别的并行复制(适用于单实例多库而且库之间的写入分布比较平均的情况,比较鸡肋,可以忽略)
- MySQL 5.7 是基于事务级别的并行复制(主要介绍)
- MySQL 8.0 是基于行级别的并行复制(太新还没来得及研究,后面补上)
MySQL 5.7基于行的并行复制
原理:
与组提交结合,一个组提交的事务都是可以并行回放,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。
复制中的重要参数
- log-bin = /binlog_dir
- binlog_format = row
- binlog_row_image = full //默认full
- gtid_mode = on
- enforce_gtid_consistency = on //打开gtid之前必须打开此选项,服务器通过允许仅执行可使用GTID安全记录的语句来强制执行GTID一致性(开启后强制检测gtid一致性,在事务中更改非事务表将会报错)
- binlog_group_commit_sync_delay = 100 //单位(微秒)。如果不启用组提交,则每次提交一个事务,binlog做一次fsync。如果启用了binlog-group-commit,此时sync_binlog=N代表每N组事务,而不是每N个事务。建议设置sync_binlog=1
- binlog_group_commit_sync_no_delay_count = 10
- binlog_order_commit = off //开启后事务提交顺序与binlog顺序一致,默认on,设置为off则事务可并行提交,对数据一致性可能产生影响。如果启用了binlog-group-commit,则设置为off
- transaction_write_set_extraction = on //8.0特性,5.7默认off
- binlog_transation_dependency_tracking = COMMIT_ORDER //等同于打开binlog_order_commit,基于行级别并行复制配置成writeset_session
- binlog_transation_dependency_history_size = 25000 //控制队列长度
- slave_net_timeout = 20|30 //io_thread超时时间
- log_slave_updates
- slave_parallel_type = LOGICAL_CLOCK //从库开启并行复制
- slave_parallel_type = 4|8
- slave_preserve_commit_order = on //保证从库提交顺序与主库一致,前置条件log-bin,log_slave_update,slave_parallel_type = LOGICAL_CLOCK
- slave_rows_search_algorithms = TABLE_SCAN,INDEX_SCAN //配置成INDEX_SCAN,HASH_SCAN,当没主键的表复制中可以用hash索引
- relay_log_info_reposity = table //crash-safe replication
- sync_relay_log_info = 1
- relay_log_recovery = 1