1. 概述

1.1. 复制解决的基本问题是让一台服务器的数据与其他服务器保持同步

1.2. 在源服务器(source server)上,任何数据修改和数据结构变更的事件(event)都会被写入日志文件中

1.3. 副本服务器从源服务器上的日志文件中读取这些事件并在本地重放执行

1.4. 一个异步处理的过程

1.4.1. 不能保证副本服务器上的数据是最新的

1.4.2. 复制延迟(副本数据和最新数据之间的时间差)也并没有上限

1.5. MySQL复制是其内置的一把“瑞士军刀”

1.6. MySQL的复制基本上是向后兼容的

1.6.1. 新版本的服务器可以作为老版本的服务器的副本

1.6.2. 老版本的服务器作为新版本的服务器的副本通常是不可行的

1.7. 通过复制可以将读操作指向副本来获得更好的读扩展性

1.7.1. 并不适合通过复制来扩展写操作

1.8. 在一主库多副本库的架构中,写操作会被执行多次,这时候整个系统的性能取决于写入最慢的那部分

1.9. 在复制架构中,读取和重放日志事件是解耦的

1.9.1. 允许读取日志和重放日志异步进行

1.9.2. I/O线程和SQL线程都是可以独立运行的

2. 用途

2.1. 数据分发

2.1.1. 不会对带宽造成很大的压力

2.1.2. 基于行的复制会比传统的基于语句的复制模式的带宽压力更大

2.1.3. 如果为了保持很低的复制延迟,最好有一个稳定的、低延迟连接

2.2. 读流量扩展

2.2.1. 可以将读操作分布到多台服务器上,实现对读密集型应用的优化

2.2.2. 对于小规模的应用,可以简单地对机器名做硬编码或使用DNS轮询

2.3. 备份

2.3.1. 一项有助于备份的有价值的技术,但副本不是备份,也不能够取代备份

2.4. 分析与报告

2.4.1. 为报告/分析(在线分析处理,OLAP)查询使用专用的副本是一项很好的策略

2.4.2. 可以很好地隔离此类查询产生的压力,以避免对满足外部客户需求的在线业务产生影响

2.5. 高可用性和故障切换

2.5.1. 有助于避免MySQL成为应用程序中的单点故障

2.5.2. 一个包含复制的设计良好的故障切换系统能够显著地缩短宕机时间

2.6. MySQL升级测试

2.6.1. 先使用一个更高版本的MySQL作为副本,确保查询能够在此副本上按照预期执行,再升级所有的实例

3. 步骤

3.1. 源端把数据更改记录到二进制日志中,称之为“二进制日志事件”(binary log events)

3.2. 副本将源上的日志复制到自己的中继日志中

3.3. 副本读取中继日志中的事件,将其重放到副本数据之上

4. 原理

4.1. 复制格式

4.1.1. 基于语句的

4.1.1.1. 通过记录所有在源端执行的数据变更语句来实现的

4.1.1.2. 简单且紧凑

4.1.1.3. 一条更新了大量数据的SQL语句,在二进制日志中可能仅仅需要几十字节存储

4.1.1.4. “不确定性”的SQL语句问题

4.1.1.4.1. 如果在源和副本上,记录的排序不同,这条SQL语句在源和副本上删除的100条记录就会不同,这将导致数据不一致

4.1.1.5. 除非某些场景下明确需要临时使用基于语句的复制

4.1.2. 基于行的

4.1.2.1. 每条被改变的记录都会作为事件被写入二进制日志

4.1.2.2. 让二进制日志的大小发生巨大的增长

4.1.2.3. 建议坚持使用基于行的复制

4.1.2.3.1. 提供了最安全的数据复制方法

4.1.3. 混合模式

4.1.3.1. the mixed method

4.1.3.2. 事件的写入,默认使用基于语句的格式,仅在需要时才切换到基于行的格式

4.1.3.3. 在写入每个事件时会有很多的判断条件,以确定使用哪种格式,而这也会导致二进制日志中出现不可预测的事件

4.1.3.4. 不使用

4.2. 全局事务标识符

4.2.1. GTID

4.2.2. 使用GTID,源服务器提交的每个事务都被分配一个唯一标识符

4.2.3. 由server_uuid和一个递增的事务编号组成的

4.2.4. 当事务被写入二进制日志时,GTID也随之被写入

4.2.4.1. 当SQL线程提交事务时,它也会将GTID标记为执行完成

4.2.5. GTID解决了运行MySQL复制的一个令人痛苦的问题:处理日志文件和位置

4.2.6. 强烈建议在数据库中启用GTID

4.3. 崩溃后的复制安全

4.3.1. innodb_flush_log_at_trx_commit=1

4.3.1.1. 可以保障每个事务日志都被同步地写到磁盘

4.3.1.2. 这是一个符合ACID要求的配置,将最大限度地保护你的数据

4.3.1.3. 二进制日志事件首先被提交,然后事务将被提交并写入磁盘

4.3.1.4. 此参数设置为1将增加磁盘写入操作的频次,同时确保数据的持久性

4.3.2. sync_binlog=1

4.3.2.1. 控制MySQL将二进制日志数据同步到磁盘的频率

4.3.2.2. 设置为1意味着在每次事务执行的时候都会把二进制日志同步写入磁盘

4.3.2.3. 可以防止在服务器崩溃时丢失事务

4.3.2.4. 会增加磁盘写入量

4.3.3. relay_log_info_repository=TABLE

4.3.3.1. 信息将被转移到MySQL本身的InnoDB表中,允许复制更新同一事务中的事务和中继日志信息

4.3.3.2. 会在一个原子操作中完成,并有助于崩溃恢复

4.3.4. relay_log_recovery=ON

4.3.4.1. 使得副本服务器在检测到崩溃时会丢弃所有本地中继日志,并从源服务器中获取丢失的数据

4.3.4.2. 确保了在崩溃中发生的磁盘上的任何损坏或不完整的中继日志都是可恢复的

4.3.4.3. 不再需要配置sync_relay_log

4.3.4.3.1. 因为在发生崩溃时,中继日志将被删除,也就无须花费额外的操作将它们同步到磁盘

4.4. 延迟复制

4.4.1. 某些副本有一些延迟反而是有好处的

4.4.2. 可以让副本中的数据保持在线并且持续运行,但同时落后于源数据库数小时或者数天

4.4.3. 配置语句是CHANGEREPLICATION SOURCE TO,配置选项为SOURCE_DELAY

4.4.4. 场景

4.4.4.1. 删除了一个表

4.4.4.1.1. 从备份中恢复可能需要几个小时
4.4.4.1.2. 如果使用了延迟复制的副本,则可以找到DROP TABLE语句对应的GTID,使副本服务器的复制运行到表被删除之前的时间点,这会大大减少修复时间

4.5. 多线程复制

4.5.1. 在副本端运行多个SQL线程,从而加快本地中继日志的应用

4.5.2. 两种模式

4.5.2.1. DATABASE模式

4.5.2.1.1. 使用多线程更新不同的数据库
4.5.2.1.2. 但不会有两个线程同时更新同一个数据库
4.5.2.1.3. 将数据分布在MySQL的多个数据库中,则可以同时并且一致地更新它们,这种模式非常有效

4.5.2.2. LOGICAL_CLOCK模式

4.5.2.2.1. 允许对同一个数据库进行并行更新,只要它们都是同一个二进制日志组提交的一部分
4.5.2.2.2. 人工延迟的配置参数
4.5.2.2.2.1. binlog_group_commit_sync_delay(以微秒为单位的延迟)
4.5.2.2.2.2. binlog_group_commit_sync_no_delay_count(决定中止等待之前要等待的事务数)
4.5.2.2.2.3. 确保你的副本配置了参数replica_preserve_commit_order,这样就不会出现无序提交的问题

4.6. 半同步复制

4.6.1. 在启用半同步复制后,源在完成每个事务提交时,都需要确保事务至少被一个副本所接收

4.6.2. 需要确认副本已收到并成功将其写入自己的中继日志(但不一定应用到本地数据)

4.6.3. 如果在一定时间范围内没有副本确认事务,MySQL将恢复到标准的异步复制模式

4.6.4. 半同步复制不是一种防止数据丢失的方法,而是可以让你拥有更具弹性的故障切换的更大工具集的一部分

4.6.5. 建议不要依赖该功能来保证数据完整性

4.7. 复制过滤器

4.7.1. 可以让副本仅复制一部分数据

4.7.2. 复制过滤器是一颗定时炸弹

4.7.3. 从源上的二进制日志中过滤事件

4.7.3.1. binlog_do_db

4.7.3.2. binlog_ignore_db

4.7.3.3. 不仅有可能破坏复制,还会使从备份中进行时间点恢复变得不可能

4.7.3.3.1. 在大多数情况下都不应该使用它们

4.7.4. 从副本上的中继日志中过滤事件

4.7.4.1. replication_*选项在SQL线程从中继日志中读取事件时过滤事件