前言
MySQL在其3.23版本时就颇具前瞻性地推出主从复制(replication)特性,距今约有20年了。正是这一举措,使得MySQL赶上了互联网1.0时代的发展大势。利用复制特性可以很方便地实现数据库架构的扩展及读写分离功能,以提升架构承载的容量。
我想,从这个角度来说,复制是MySQL最主要的特性一点都不为过。
熟悉MySQL的人都知道,MySQL的复制技术是其核心技术之一,是灵活运用MySQL的基础。从3.23版本开始,MySQL推出了异步复制功能,之后不断进化,推出半同步复制、无损半同步复制,以及目前最新的组复制功能。其基本原理几乎是一致的——利用二进制日志文件在数据库服务器之间的传播和数据回放,实现多台数据库服务器之间的数据同步。一个合格的MySQL从业人员,必须掌握复制技术的基础知识,熟悉复制技术的各种解决方案,并灵活利用它们去满足生产系统中的各种需求。
目录
主要内容
全文共分为3篇:基础篇、方案篇和参考篇,总共38章的内容。
按照“基本原理”→“生产实践”→“更多参考”的逻辑顺序讲述,文中配了大量的原理与方案示意图,力求用通俗易懂的语言、直观明了的示意图、完整的知识涵盖面将MySQL复制技术讲透。
其中,基础篇侧重介绍主从复制的原理和复制技术的演进,方案篇侧重介绍主从复制技术在生产环境中的应用方案,参考篇侧重介绍二进制日志的基本组成及主从复制中常见对象复制的安全性等。
本文适用初、中、高级MySQL DBA、数据库架构师及相关开发人员阅读。
基础篇
第1章复制的概述;简单来说,“复制”就是将来自一个MySQL Server(这里指master角色,即主库)的数据变更,通过其逻辑的二进制日志(binlog)传输到其他的一个或多个MySQL Server(这里指slave角色,即从库)中,其他MySQL Server通过应用(回放)这些逻辑的二进制日志来完成数据的同步。这些MySQL Server之间的逻辑关系,我们称为“复制拓扑”(也可以称为“复制架构”)。
默认情况下,复制是异步的,即主库将二进制日志传输到从库之后,并不关心从库是否成功收到。从库是否收到这些二进制日志,不影响主库的任何读/写访问;而从库的复制线程也可以随意暂停或停止,并不影响主库的读/写访问。通常,异步模式能够发挥数据库的最高性能,但数据安全性却得不到很好的保证,如果对数据安全性的要求较高,可以考虑使用半同步复制。
另外,默认情况下,复制的数据是针对整个实例的(排除部分系统表),你可以根据自身需求选择是否需要复制整个实例的数据,是只复制某些库,还是只复制某些表的数据等。接下来将简要介绍MySQL中复制拓扑的一些适用场景,以及与复制相关的概念。
第5章半同步复制;除了内置的异步复制,MySQL 5.7还支持通过插件方式实现半同步复制接口。相对于MySQL 5.5和MySQL 5.6中的半同步复制,通常我们将MySQL 5.7中的半同步复制称为“增强半同步复制”,也称为“无损复制”(MySQL 5.5和MySQL 5.6虽然也支持半同步复制,但不能保证“无损复制”,详见5.4节“半同步复制的注意要点”)。本章将详细介绍MySQL 5.7中的半同步复制。
第7章多源复制;一些业务数据被打散到多个数据库实例上之后,数据库的备份和恢复就比较烦琐,有没有什么简单的方案能够解决这个问题呢?MySQL5.7.6引入了复制通道的概念,使得同一个从库可以同时接收多个主库的数据,一个复制通道逻辑上就对应一个主库。本章将简要介绍如何在复制拓扑中使用通道、通道相关的概念,以及相关系统的设置对单源(单个复制通道)复制的影响。
mysql.slave_relay_log_info表来检查复制信息,第9章详细介绍了如何通过PERFORMANCE_SCHEMA库下的复制记录表来检查复制信息,本章将对前面章节中未提及的与复制相关的小细节进行补充说明。例如,通过SHOW PROCESSLIST语句来查看I/O线程和SQL线程的状态信息、通过PERFORMANCE_SCHEMA库中的user_variables_by_thread表来查看I/O线程向主库注册的自定义变量信息等。
方案篇
第14章搭建异步复制;异步复制是相对于同步复制和半同步复制而言的,这三者之间的区别,可参考第1章“复制的概述”和第5章“半同步复制”。相比于其他两种复制,异步复制的速度最快,且主库性能不受从库复制性能的影响,它也是MySQL中最早出现的复制技术。在MySQL 5.6之前,无论是同步复制、半同步复制,还是异步复制,都是传统复制(基于二进制日志文件和位置),维护复制拓扑时比较麻烦。MySQL 5.6及其之后的版本支持GTID复制模式,使得对复制拓扑的维护变得非常方便快捷。本章将详细介绍在传统复制和GTID复制的两种复制模式下,搭建异步复制的过程,关于传统复制和GTID复制的原理,可参考第4章。
第17章复制模式的切换;MySQL 5.5及其之前的版本,由于不支持GTID机制,所以它们使用的都是传统复制(即基于二进制日志文件和位置的复制)。MySQL 5.6中引入了GTID机制,该机制有众多优点(详情可参考第4章、第12章等章节,以及下文中将要演示的复制模式切换过程,这里先不展开介绍),为MySQL管理者的维护工作带来了极大便利,所以大多数用户都会选择切换到GTID复制模式(即基于GTID的复制,下文统一称为“GTID复制”)。而除了一些特殊的应用场景之外,通常很少需要从GTID复制切换到传统复制。
在第1章中,我们将传统复制和GTID复制称为“数据同步方法”,但通常我们更喜欢将其称为“复制模式”。本章将对这两种复制模式中的一些概念以及它们的相互切换过程进行详细介绍。
第20章数据库故障转移;数据库故障转移,在这里指的是由于主库故障而触发的,将主库读/写业务转移到其他数据库实例继续对外提供读/写服务的过程。例如,当探测到主库主机宕机、主库数据库进程不存在、主库数据库无法登录、主库数据库无法执行查询或更新操作时,为尽量减小主库故障对业务造成的影响,需要尽快将读/写访问入口转移到处于正常状态的数据库实例上。
在实际场景中,造成主库无法提供读/写服务的原因多种多样。对于数据库管理系统来说,考虑的因素越多,就会越复杂,可靠性就会越差。所以,通常建议将问题分为可确定故障原因和不可确定故障原因的场景,然后分别采用不同的方式解决。如果混为一谈,可能经常导致误操作转移。
对于可确定故障原因且通过自动故障转移能够恢复业务访问的场景,可以使用数据库管理系统自动进行故障转移。第21章搭建多源复制;MySQL的多源复制(也可以称为“多主复制”),指的是复制拓扑中的从库同时从多个源Server(主库)接收二进制日志进行重放。多源复制可用于将多个Server的数据备份到单个Server中(从库),以及在分库分表场景中,将来自多个Server的分片表数据合并。从库在应用来自多个主库的二进制日志时,不会执行任何冲突检测或解决冲突,如有需要,则靠应用程序来解决这些问题。在多源复制拓扑中,从库会为每个主库建立一个单独的复制通道(单独的I/O线程、协调器线程、Worker线程),各自重放各自的二进制日志,互不依赖。
第22章MySQL版本升级;MySQL的版本并不需要常升级,但如果要使用新版本的某个新特性,或者为了修复旧版本的某个bug,就不得不进行版本升级。在生产环境中,为避免单点故障,通常都会用多个MySQL实例构建一个复制拓扑。为了使升级操作对业务的影响尽可能小,可以先升级复制拓扑中的只读实例(从库),然后再执行一次主从角色切换(会造成应用短暂中断),最后将主库当作从库再做一次升级即可。
本章只阐述在复制拓扑中升级MySQL版本的一些注意事项,关于复制拓扑中的MySQL版本升级步骤,可参考19.2节“在线切换”。
第23章将不同数据库的数据复制到不同实例;将不同数据库中的数据复制到不同的实例(MySQL Server),在实现上具体指的就是从库将主库的全量二进制日志拉取到本地的中继日志之后,从库SQL线程在重放这些二进制日志时,根据自身配置的复制过滤规则,选择需要应用哪些库与表,以及需要忽略哪些库与表。
当然,在主库端也支持复制过滤,虽然在主库端配置复制过滤后能够减少二进制日志的传输量,但主库端只支持库级别的过滤规则,而且容易导致主从库数据不一致。通常不建议在主库端配置复制过滤规则,可靠的复制过滤都是在从库端实现的,因为这样才更合理,每个从库根据自己的需要来灵活配置复制过滤规则。
关于复制过滤的原理与流程,本章不做过多阐述,可参考第13章“MySQL Server 复制过滤”。本章将以在从库端配置复制过滤规则为例,详细介绍其操作步骤。
第25章常用复制故障排除方案;作为MySQL数据库管理人员,我们在日常的工作中或多或少都会碰到MySQL复制相关的问题,有些问题可能很快就解决了,有些问题具有一定的迷惑性,可能需要排查很久才能找到具体的原因。对于后者,我们大概率会在日后的工作中再次遭遇它们。再次遇到的时候,你是否有似曾相识但怎么也想不起来具体细节的感觉呢?
为了避免这种尴尬,建议在处理完故障之后,立即以文档形式总结故障现象、其复现与排查过程、解决方案和规避方法,予以留存。
另外,故障虽然多种多样,但其处理思路与流程是具有共性的。本章以处理MySQL复制相关的故障为主题,详细介绍一个排除复制故障的通用方案(注意,本章只讲解思路与流程,不介绍细节)。
参考篇
第26章二进制日志文件的基本组成;在使用MySQL数据库的平台上,很多关键的应用场景都是基于二进制日志实现的,例如主从复制(这也是本书的主题,前面用大量的篇幅介绍了复制的原理与使用案例)、基于时间点的备份与恢复、误操作数据的回滚、供数(解析二进制日志文件,并将得到的文本数据传输到另一个平台,如数据仓库、Kafka等)等,但是很少有人详细了解过二进制日志。本章将从二进制日志事件类型的角度对二进制日志文件中的内容进行详细的介绍。
第27章常规DDL操作解析;
第29章复制AUTO_INCREMENT字段;设计MySQL的表结构时,一般建议使用AUTO_INCREMENT字段作为表的主键,而不是使用UUID作为主键,原因是使用前者作为主键能保证数据行是按顺序写入的。如果采用随机写入的方式,InnoDB在写入数据时会产生大量的随机I/O操作,并且会频繁做数据页的分裂操作。
AUTO_INCREMENT字段主键在插入性能以及抑制碎片空间的产生方面都比较有优势。但是它的值是由MySQL Server产生的,那么在复制拓扑中,MySQL是如何保证主从库之间AUTO_INCREMENT字段数据的一致性的呢?本章讨论在MySQL中是如何保证AUTO_INCREMENT字段被正确复制的。
第30章复制CREATE ...IF NOT EXISTS语句;MySQL中有CREATE ... IF NOT EXISTS语句,对于程序创建库或者表很方便:如果库或者表不存在,则创建;反之,则不创建。无论主库中是否存在某个库,使用CREATE DATABASE IF NOT EXISTS语句创建库的时候,该语句都能被正确地复制到从库。同样,无论主库中是否存在某张表,CREATE TABLE IF NOT EXISTS语句(除CREATE TABLE IFNOT EXISTS ... SELECT外)都能被正确复制到从库。本章我们就来看看CREATE DATABASE IF NOT EXISTS和CREATE TABLE IF NOT EXISTS语句是如何被正确复制到从库的。
第34章复制LIMIT子句;DBA一般会建议开发人员在使用MySQL时不要使用大事务,比如在对某一张表清理上百万行数据时,不建议直接用一条DELETE语句清理完所有数据,而是建议使用LIMIT子句,小批量、多次清理。LIMIT子句对符合条件的结果集限定返回的行数,但是无法明确是哪几行。在MySQL的复制中,对于LIMIT子句这种具有不确定性的子句如何保证复制的一致性呢?本章我们就来看看LIMIT子句是如何被正确复制的。