1. 复制切换

1.1. 复制是高可用性的基础

1.1.1. 总是保留一份持续更新的副本数据,会让灾难恢复更简单

1.2. “切换副本”(promoting a replica)和“故障切换”(failing over)是同义词

1.2.1. 意味着源服务器不再接收写入,并将副本提升为新的源服务器

1.3. 计划内切换

1.3.1. 常见原因

1.3.1.1. 安全补丁

1.3.1.2. 内核更新

1.3.1.3. 一些配置选项更改后需要重新启动才能生效

1.3.2. 步骤

1.3.2.1. 确定将哪个副本切换为新的源

1.3.2.1.1. 一个包含所有数据的副本

1.3.2.2. 检查延时,确保延时在秒级别

1.3.2.3. 通过设置super_read_only停止数据写入源服务器

1.3.2.4. 等待副本与目标完全同步

1.3.2.4.1. 通过比较GTID来确定

1.3.2.5. 在目标(需要切换为源的副本)上取消read_only设置

1.3.2.6. 将应用流量切换到目标上

1.3.2.7. 将所有副本重新指向新的源,包括刚刚被降级为副本的服务器

1.3.2.7.1. 配置了GTID和AUTO_POSITION=1

1.4. 计划外切换

1.4.1. 只要时间够长,每个系统都会因软件或硬件而出现故障

1.4.2. 当故障发生在承担写入的源服务器上时,会对用户体验产生重大影响

1.4.3. 这时候不再存在一个实时运行的源服务器了

1.4.4. 步骤

1.4.4.1. 确定需要切换的副本

1.4.4.1.1. 择数据最完整的副本

1.4.4.2. 在目标上关闭read_only设置

1.4.4.3. 将应用流量切换到目标上

1.4.4.4. 将所有副本重新指向新源(目标服务器),包括恢复后的原来提供服务的源服务器

1.4.4.4.1. 切换前的源服务器再次启动时,需要默认启用super_read_only
1.4.4.4.1.1. 防止任何意外的写入

1.5. 切换时的权衡

1.5.1. 很难知道切换后的目标(新的源服务器)可能丢失了多少数据

1.5.2. 有时不进行故障切换可能是更好的策略

1.5.3. 等待恢复的好处不会丢失任何数据,并且所有副本将继续从中断的地方工作

2. 复制拓扑

2.1. 几乎任何一个源和副本都可以配置MySQL复制

2.2. 主动/被动模式

2.2.1. 应用将所有读取和写入都指向单个源服务器

2.2.2. 不用担心复制延迟的问题

2.2.3. 可以防止应用程序不接受读取延迟数据的问题

2.2.4. 配置

2.2.4.1. 应该尽量让源和副本在CPU、内存等方面具有相同的配置

2.2.4.2. 副本上使用相同的硬件和软件配置就可以确保能够支持切换之前的应用流量容量和吞吐量

2.2.5. 冗余

2.2.5.1. 在物理硬件环境中,推荐使用至少三台服务器的n+2冗余

2.2.5.2. 如果数据足够少,或者可以轻松复制数据,也可以使用至少两台服务器的n+1冗余,否则还是建议n+2

2.2.5.3. 如果无法在源服务器上进行备份操作,可以使用其中一个副本作为备份服务器

2.2.6. 注意事项

2.2.6.1. 实际上是将读扩展绑定到单台服务器的容量上

2.2.6.2. 如果达到读扩展上限

2.2.6.2.1. 则必须演进到更复杂的拓扑
2.2.6.2.1.1. 主动/只读池配置
2.2.6.2.2. 否则就不得不利用分片来减少源上的读取压力

2.3. 主动/只读池配置

2.3.1. 将所有写入指向源服务器

2.3.2. 根据应用程序的需要,读取则可以被发送到源服务器或只读池

2.3.3. 只读池可以实现读取密集型应用程序的读水平扩展

2.3.4. 由于源上的复制请求,水平扩展能力会受到限制

2.3.5. 配置

2.3.5.1. 如果随着时间的推移,只读池在持续增长,则可以让副本用不同的硬件配置来优化成本

2.3.5.2. 将流量进行加权,运行在更好的硬件配置的副本上可以承担更多的流量

2.3.6. 冗余

2.3.6.1. 应满足先前提出的要求,还需要至少一台服务器可以充当故障切换的目标

2.3.7. 注意事项

2.3.7.1. 只读池的大小会决定管理的复杂度,以及何时应该考虑自动化

2.4. 不推荐的拓扑架构

2.4.1. 双源主动-主动架构

2.4.1.1. 双向复制

2.4.1.2. 涉及两台服务器,每台服务器都配置为源和另一台的副本

2.4.1.3. 一对协同源

2.4.2. 双源主动-被动模式

2.4.2.1. 主动-主动模式下的双源服务器有一个变种

2.4.2.2. 其中一台服务器是只读的“被动”服务器

2.4.3. 带有副本的双源模式

2.4.3.1. 两个可写的源只会带来麻烦

2.4.4. 环形复制

2.4.4.1. 具有三个或更多个源,其中每台服务器都在环中,它之前的服务器是它的副本,它之后的服务器作为它的源

2.4.4.2. 循环复制

2.4.5. 多源复制

3. 复制管理和维护

3.1. 在数据量很小而且写入负载一致的时候,通常不太需要经常查看复制延迟或者复制中断相关的问题

3.2. 复制监控

3.2.1. 复制同时需要源和副本上的磁盘空间

3.2.2. 监控复制的状态和错误

3.2.2.1. 如果复制线程没有正常运行,那么可以查看报错信息,以确定下一步应该做什么来修复错误

3.2.3. 延迟复制的实际延迟

3.2.3.1. 太长的延迟可能会使其使用起来更加耗时

3.3. 观测复制延迟

3.3.1. 副本与源之间的复制延迟是多少

3.3.2. 最好的解决方案是心跳记录,它需要在源上每秒更新一次时间戳

3.3.2.1. 创建了一个方便的时间戳,展示副本中的数据在什么时间点是最新的

3.3.2.2. Percona Toolkit中包含的pt-heartbeat脚本是当前最流行的复制心跳实现方案

3.3.2.3. 二进制日志中的复制心跳记录可用于许多目的

3.4. 确定副本数据的一致性

3.4.1. 副本始终是其源的精确复制减去其复制延迟的部分

3.4.2. 副本与源不一致的原因

3.4.2.1. 意外写入副本

3.4.2.2. 使用双源复制,双方都写入了数据

3.4.2.3. 非确定性语句和基于语句的复制

3.4.2.4. 当运行在弱持久化模式时MySQL崩溃

3.4.2.5. MySQL中的Bug

3.4.3. 建议遵循的规则或配置

3.4.3.1. 在副本上,始终启用super_read_only

3.4.3.1.1. 使用read_only可以防止没有SUPER权限的用户写入,但这不会阻止DBA在没有意识到他们在副本上的情况下运行DELETE或ALTER
3.4.3.1.2. super_read_only设置只允许复制写入,是运行副本的最安全方式

3.4.3.2. 使用基于行的复制或确定性语句

3.4.3.2.1. 基于行的复制是复制数据的最一致的方式
3.4.3.2.2. 使用ORDER BY,使行顺序具有确定性

3.4.3.3. 不要尝试同时写入复制拓扑中的多台服务器

3.4.3.3.1. 最实用的复制拓扑是使用一个源,执行所有写入操作,以及一个或多个副本,可选择地执行读取操作

4. 复制问题和解决方案

4.1. 使用副本扩展读取操作,使用分片技术扩展写入操作

4.2. 源端二进制日志损坏

4.2.1. 如果源上的二进制日志已被损坏,那么别无选择,只能重建副本

4.3. 非唯一的服务器ID

4.3.1. 如果你不小心配置了具有相同服务器ID的两个副本,不仔细观察的话,它们似乎可以正常工作

4.3.2. 在源服务器上,任何时候都只能看到两个副本中的一个

4.3.3. 解决此问题的唯一方法是在设置副本时要小心

4.3.3.1. 创建副本到服务器ID映射的规范列表很有帮助

4.3.3.2. 如果副本完全位于一个子网中,则可以使用每台机器IP地址的最后一个八位字节来作为唯一ID

4.4. 未配置服务器ID

4.4.1. MySQL将显示为使用CHANGEREPLICATION SOURCE TO配置了复制,但不会允许启动副本

4.4.2. 你可能会从SELECT@@server_id中获得一个值,但这只是一个默认值

4.4.2.1. 必须显式设置该值

4.5. 临时表丢失

4.5.1. 临时表在某些场景中使用起来很方便,但不幸的是,它们与基于语句的复制不兼容

4.5.2. 如果副本崩溃或将其关闭,则副本线程正在使用的任何临时表都会消失

4.5.3. 当重新启动副本时,引用丢失的临时表的任何相关语句都将失败

4.5.4. 最好的方法是使用基于行的复制

4.5.4.1. 其次是统一命名临时表(例如,以temporary_为前缀),然后使用复制规则完全跳过复制临时表

4.6. 没有复制所有变更

4.6.1. 如果误用SET SQL_LOG_BIN=0或不了解复制过滤规则,可能会导致副本不会执行源上发生的某些变更

4.7. 复制延迟过大

4.7.1. 多线程复制

4.7.2. 使用分片技术

4.7.2.1. 使用分片技术将写入分散到多个源也是一种非常有效的策略

4.7.3. 临时降低持久化要求

4.7.3.1. 如果复制延迟主要是由于写入操作导致的,则可以临时设置sync_binlog=0和innodb_flush_log_at_trx_commit=0以提高复制速度

4.7.3.2. 最好只在副本上执行此操作,如果此副本也用于执行备份操作,则更改这些设置可能会使你无法从备份中恢复完整的数据

4.7.3.3. 一种可能的策略是监控SHOW REPLICA STATUS命令中的Seconds_behind_source值,当它超过某个阈值时

4.7.3.3.1. 验证是否启用了super_read_only,以确保服务器是不可写副本
4.7.3.3.2. 更改sync_binlog和innodb_flush_log_at_trx_commit的配置以减少写操作
4.7.3.3.3. 定期检查SHOW REPLICA STATUS以获取Seconds_behind_source的值
4.7.3.3.4. 当延迟低于可接受的阈值时,将持久性相关参数恢复到正常值

4.8. 来自源服务器的超大数据包

4.8.1. 当源服务器的max_allowed_packet大小与副本不匹配时,可能会出现另一个难以跟踪的复制问题

4.8.2. 源服务器可以记录副本认为过大的数据包,当副本检索该二进制日志事件时,它可能会遇到各种问题,其中包括无休止的错误循环、重试或中继日志中的损坏

4.9. 磁盘空间耗尽

4.9.1. 当源服务器执行大量LOAD DATA INFILE语句并在副本上启用log_replica_updates时

4.9.2. 复制延迟越大,用于已从源端获取但尚未执行的中继日志占用的磁盘空间就越多

4.9.3. 可以通过监控磁盘使用情况并设置relay_log_space参数来防止这些错误出现

4.10. 复制的限制

4.10.1. 因为MySQL自身固有的一些限制,无论有没有出现明确的报错,MySQL复制都可能失败或不同步

4.10.2. 服务器中的bug

4.10.2.1. MySQL服务器的许多大版本历史上都有复制方面的bug

4.10.2.1.1. 尤其是在大版本的第一个版本中