1.介绍
传统的MySQL复制提供了一种简单的Primary-Secondary复制方法,默认情况下,复制是单向异步的。MySQL支持两种复制方式:基于行的复制和基于语句的复制。(体现在mysqlbinlog日志的记录方式)这两种方式都是通过在主库上记录二进制日志(binlog)、在从库重放中继日志(relylog)的方式来实现异步的数据复制。二进制日志或中继日志中的记录被称为事件。所谓异步包含两层含义: 一是主库的二进制日志写入与将其发送到从库是异步进行的, 二是从库获取与重放日志事件是异步进行的。 注意:这意味着,在同一时间点从库上的数据更新可能落后于主库,并且无法保证主从之间的延迟间隔。复制给主库增加的开销主要体现在启用二进制日志带来的I/O,但是开销并不大,MySQL官方文档中称开启二进制日志会产生1%的性能损耗。出于对历史事务备份以及从介质失败中恢复的目的,这点开销是非常必要的。除此之外,每个从库也会对主库产生一些负载,例如网络和I/O开销。当从库读取主库的二进制日志时,可能会造成一定的I/O开销。如果从一个主库上复制到多个从库,唤醒多个复制线程发送二进制日志内容的开销将会累加。
但所有这些复制带来的额外开销相对于应用对MySQL服务器造成的高负载来说是很小的。
2.复制的用途
横向扩展(读写分离) 通过复制可以将读操作指向从库来获得更好的读扩展。所有写入和更新都在主库上进行,但读取可能发生在一个或多个从库上。 在这种读写分离模型中,主库专用于更新,显然比同时进行读写操作会有更好的写性能。 需要注意的是,对于写操作并不适合通过复制来扩展。在一主多从架构中,写操作会被执行多次,这时整个系统的写性能取决于写入最慢的那部分。负载均衡(高性能) 通过MySQL复制可以将读操作分不到多个服务器上,实现对读密集型应用的优化。对于小规模的应用,可以简单地对机器名做硬编码或者使用DNS轮询(将一个机器名指向多个IP地址)。 当然也可以使用复杂的方法,例如使用LVS网络负载均衡器等,能够很好地将负载分配到不同的MySQL服务器上。提高数据安全性 提高数据安全性可以从两方面来理解。 其一,因为数据被复制到从库,并且从库可以暂停复制过程,所以可以在从库上运行备份服务而不会影响相应的主库。 其二,当主库出现问题,还有从库的数据可以被访问。 但是,对备份来说,复制仅是一项有意义的技术补充,它既不是备份也不能够取代备份。例如,当用户误删除一个表,而且此操作已经在从库上被复制执行,这种情况下只能用备份来恢复。提高高可用性 复制能够帮助应用程序避免MySQL单点失败,一个包含复制的设计良好的故障切换系统能够显著缩短宕机时间。滚动升级 比较普遍的做法是,使用一个高版本MySQL作为从库,保证在升级全部实例前,查询能够在从库上按照预期执行。 测试没有问题后,将高版本的MySQL切换为主库,并将应用连接至该主库,然后重新搭建高版本的从库。
3.复制如何工作
如前所述,MySQL复制依赖二进制日志,所以要理解复制如何工作,先要了解MySQL的二进制日志。1.二进制日志 二进制日志包含描述数据库更改的事件,如建表操作或对表数据的更改等。开启二进制日志有两个重要目的: 用于复制。主库上的二进制日志提供要发送到从库的数据更改记录。主库将其二进制日志中包含的事件发送到从库,从库执行这些事件以对主服务器上的数据进行相同的更改。 用于恢复。当出现介质错误(如磁盘故障)时,数据恢复操作需要使用二进制日志。还原备份后,将重新执行备份后记录的二进制日志中的事件。 不难看出,MySQL二进制日志所起的作用与Oracle的归档日志类似。二进制日志只记录更新数据的事件,不用于SELECT或SHOW等语句。通过设置log-bin系统变量开启二进制日志,MySQL 8中缺省是开启的。 二进制日志有STATEMENT、ROW、MIXED三种格式,通过binlog-format系统变量设置: STATMENT格式,基于SQL语句的复制(statement-based replication,SBR)。 每一条会修改数据的SQL语句会记录到binlog中。 这种格式的优点是不需要记录每行的数据变化,这样二进制日志会比较少,减少磁盘I/O,提高性能。 缺点是在某些情况下会导致主库与从库中的数据不一致,例如last_insert_id()、now()等非确定性函数,以及用户自定义函数(user-defined functions,udf)等易出现问题。 ROW格式,基于行的复制(row-based replication,RBR)。 不记录每一条SQL语句的上下文信息,仅需记录哪条数据被修改了,修改成了什么样子,能清楚记录每一行数据的修改细节。 其优点是不会出现某些特定情况下的存储过程、函数或触发器的调用和触发无法被正确复制的问题。 缺点是通常会产生大量的日志,尤其像大表上执行alter table操作时候会让日志暴涨。 MIXED格式,混合复制(mixed-based replication,MBR)。 是语句和行两种格式的混合使用,默认使用STATEMENT模式保存二进制日志,对于STATEMENT模式无法正确复制的操作,会自动切换到基于行的格式,MySQL会根据执行的SQL语句选择日志保存方式。 MySQL 8缺省使用ROW格式。二进制日志的存放位置最好设置到与MySQL数据目录不同的磁盘分区,以降低磁盘I/O的竞争,提升性能,并且在数据磁盘故障的时候还可以利用备份和二进制日志恢复数据。2.复制步骤 总的来说,MySQL复制有五个步骤: 在主库上把数据更改事件记录到二进制日志中。 从库上的I/O线程向主库询问二进制日志中的事件。 主库上的binlog dump线程向I/O线程发送二进制事件。 从库上的I/O线程将二进制日志事件复制到自己的中继日志中。 从库上的SQL线程读取中继日志中的事件,并将其重放到从库上。 第一步是在主库上记录二进制日志。 在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志中。 MySQL会按事务提交的顺序而非每条语句的执行顺序来记录二进制日志。在记录二进制日志后,主库会告诉存储引擎可以提交事务了。 下一步,从库将主库的二进制日志复制到其本地的中继日志中。 首先,从库会启动一个工作线程,称为I/O线程,I/O线程跟主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制日志转储(binlog dump)线程,它会读取主库上二进制日志中的事件,但不会对事件进行轮询。 如果该线程追赶上了主库,它将进入睡眠状态,直到主库发送信号通知其有新的事件时才会被唤醒,从库I/O线程会将接收到的事件记录到中继日志中。 从库的SQL线程执行最后一步,该线程从中继日志中读取事件并在从库上执行,从而实现从库数据的更新。 当SQL线程追赶I/O线程时,中继日志通常已经在系统缓存中,所以重放中继日志的开销很低。 SQL线程执行的事件也可以通过log_slave_updates系统变量来决定是否写入其自己的二进制日志中,这可以用于级联复制的场景。 这种复制架构实现了获取事件和重放事件的解耦,允许这两个过程异步进行。也就是说I/O线程能够独立于SQL线程之外工作。 但这种架构也限制了复制的过程,其中最重要的一点是在主库上并发更新的查询在从库上通常只能串行化执行,因为缺省只有一个SQL线程来重放中继日志中的事件。 在MySQL 5.6以后已经可以通过配置slave_parallel_workers等系统变量进行并行复制。
4.redo log
pass