1. 复制概述
MySQL 内置的复制功能是构建基于 MySQL 的大规模、高性能应用的基础,复制解决的基本问题是让一台服务器的数据与其他服务器保持同步。接下来,我们将从复制概述及原理、复制的配置、常见的问题及解决方法来学习 MySQL 的复制功能。
1.1 复制解决的问题
下面是复制常见的用途:
- 数据分布。Mysql 复制通常不会对带宽造成很大压力,但在 5.1 版本中引入的基于行的复制会比传统的基于语句的复制模式产生更大的带宽压力。你可以随意地停止或开始复制,并在不同的地理位置来分布数据备份,例如不同的数据中心。另外,即使在不稳定的网络环境下,远程复制也可以工作。但如果未来保存很低的复制延迟,最好有一个稳定、低延迟的连接。
- 负载均衡。通过 Mysql 复制,可以将读操作分布到多个服务器上,实现对读密集型应用的优化,并且很容易实现,通过简单的代码修改就能实现基本的负载均衡。对应小规模的应用,可以简单的使用 DNS 轮询(将一个机器名指向多个 IP 地址)。
- 备份。对于备份来说,负载是一项很有意义的技术补充。
- 高可用性和故障切换。负载能够帮助应用避免 Mysql 单点失败,一个使用复制的设计良好的系统能够显著的缩短宕机时间。
- Mysql 升级测试。这是比较常见的做法,在更新 Mysql 版本前,先使用将要更新的版本作为备库,保证更新版本不会对系统造成影响。
1.2 复制是如何工作的?
在详细介绍如何设置复制之前,让我们先看看 Mysql 实际上是如何进行数据复制的。
总的来说,复制有三个步骤:
以上是复制的简单概述,下图描述了复制的细节:
整体复制过程:
这种复制架构实现了获取事件和重放事件的解耦,允许这两个过程异步进行。也就是说 I/O 线程能够独立于 SQL 线程之前工作。但是,这种架构也限制了复制的过程,其中最重要的一点是,在主库上并发运行的查询在备库上只能串行化执行,因为只有一个 SQL 线程来重放中继日志中的事件。
不过值得高兴的是,5.7 版本已经支持从库的并行复制了。基于二进制日志的并行复制,是在日志内容中新增了 last_committed 和 sequence_number,分别 表示事务提交的时间和上次事务提交的编号。如果事务具有相同的时间,表示这些事务是在一组内,可以进行并行回放。
2. 复制的原理
我们已经了解了复制的一些基本概念,接下来我们要更深入的了解复制,看看复制究竟是如何工作的,有哪些优缺点。
2.1 基于语句的复制
在 Mysql 5.0 及之前的版本中只支持基于语句的复制(也称为逻辑复制)。基于语句的复制模式,主库会记录那些造成数据更改的 SQL 语句,当备库读取并重放这些事件时,实际上只是把主库执行过的 SQL 再执行一遍。这种方式既有优点,也有缺点。
优点是:
但事实上,基于语句的方式可能并不如其看起来那么便利,其缺点是:
2.2 基于行的复制
Mysql 5.1 开始支持基于行的复制。这种方式会将实际数据记录在二进制日志中。同样的,它也有其自身的优缺点。
它的优点是可以更加准确的复制数据,而缺点,则是可能造成较大的开销。比如一个工资表中有一万个用户,我们把每个用户的工资+1000,那么基于行的复制则要复制一万行的内容,由此造成的开销比较大,而基于语句的复制仅仅一条语句就可以了。
由于没有哪种模式是对所有情况都是完美的,Mysql 就使复制模式可以动态切换。默认情况下使用的是基于语句的复制方式,但如果发现语句无法被正确地复制,就切换到基于行的复制模式。还可以根据需要来设置会话级别的变量 binlog_format,控制二进制日志格式。
2.3 复制文件解读
复制过程中会使用到一些文件。前面已经介绍了二进制日志文件和中继日志文件,除此之外,还有其他的文件会被用到。
使用这些文件来记录 Mysql 复制和日志状态是一种非常粗糙的方式。更不幸的是,它们不是同步写的。如果服务器断电并且文件数据没有被刷新到磁盘,在重启服务器后,文件中记录的数据可能是错误。不过好在这些问题以及在 5.5 版本里做了改进。
2.4 发送复制事件到其它备库
log_slave_update 选项可以让备库编程其它服务器的主库。在设置该选项后,Mysql 会将其执行过的事件记录到它自己的二进制日志中。这样它的备库就可以从其日志中检索并执行事件。下图阐述了这一过程:
在这种场景下,主库将数据更新事件写入二进制日志,第一个备库提取并执行这个事件。这个时候一个事件的生命周期应该已经结束了。但由于设置了 log_slave_updates,备库会将这个事件写到它自己的二进制日志中。这样第二个备库就可以从第一个备库中,将事件提取到它的中继日志中并执行。
这意味着作为源服务器的主库可以将其数据变化传递给没有与其直接相连的备库上。默认情况下,这个选项是被打开的,这样在连接到备库时就不需要重启服务器。
当第一个备库把自主库获得的事件写入到其它二进制日志中时,这个事件在备库二进制日志中的位置与其主库二进制日志中的位置几乎肯定是不相同的,可能在不同的日志文件或文件内不同的位置。这意味着你不能假定所有拥有同一逻辑复制点的服务器拥有相同的日志坐标。