前言
MySQL在其3.23版本时就颇具前瞻性地推出主从复制(replication)特性,距今约有20年了。正是这一举措,使得MySQL赶上了互联网1.0时代的发展大势。利用复制特性可以很方便地实现数据库架构的扩展及读写分离功能,以提升架构承载的容量。
我想,从这个角度来说,复制是MySQL最主要的特性一点都不为过。
熟悉MySQL的人都知道,MySQL的复制技术是其核心技术之一,是灵活运用MySQL的基础。从3.23版本开始,MySQL推出了异步复制功能,之后不断进化,推出半同步复制、无损半同步复制,以及目前最新的组复制功能。其基本原理几乎是一致的——利用二进制日志文件在数据库服务器之间的传播和数据回放,实现多台数据库服务器之间的数据同步。一个合格的MySQL从业人员,必须掌握复制技术的基础知识,熟悉复制技术的各种解决方案,并灵活利用它们去满足生产系统中的各种需求。
MySQL能够成为“最流行的开源数据库”,其复制技术起了巨大的作用。Sharing Nothing架构、横向扩展、高可用、容灾、数据集成与聚合,这一系列名词所代表的架构和应用场景都与复制技术有关。多应用场景意味着与复制相关的技术灵活而复杂:异步复制、增强半同步复制、语句级复制、行级复制、按位点复制、GTID复制、多源复制、级联复制、多线程复制、双主架构、一主多从架构、延迟复制、读写分离等。不同的业务场景,使用不同的复制架构,该如何正确地构建MySQL复制拓扑、如何有效地监控和正确地维护,这些都是架构师和DBA要解决的问题。
随着国家加强对数据库技术安全可控的要求,使用开源的MySQL数据库已经成为一种趋势,开源代表着代码安全、数据库可控。各行各业不断地推进MySQL的部署,MySQL从业人员也由互联网行业扩散到传统行业。利用复制技术可以实现MySQL的高可用性及高扩展性,是保障MySQL数据安全和进行数据库扩展的必要技术。安全可控对于传统行业至关重要,因此极力推荐MySQL从业者,特别是传统行业的DBA阅读此文 。
目录
主要内容
全文共分为3篇:基础篇、方案篇和参考篇,总共38章的内容。
按照“基本原理”→“生产实践”→“更多参考”的逻辑顺序讲述,文中配了大量的原理与方案示意图,力求用通俗易懂的语言、直观明了的示意图、完整的知识涵盖面将MySQL复制技术讲透。
其中,基础篇侧重介绍主从复制的原理和复制技术的演进,方案篇侧重介绍主从复制技术在生产环境中的应用方案,参考篇侧重介绍二进制日志的基本组成及主从复制中常见对象复制的安全性等。
本文适用初、中、高级MySQL DBA、数据库架构师及相关开发人员阅读。
基础篇
第1章复制的概述;简单来说,“复制”就是将来自一个MySQL Server(这里指master角色,即主库)的数据变更,通过其逻辑的二进制日志(binlog)传输到其他的一个或多个MySQL Server(这里指slave角色,即从库)中,其他MySQL Server通过应用(回放)这些逻辑的二进制日志来完成数据的同步。这些MySQL Server之间的逻辑关系,我们称为“复制拓扑”(也可以称为“复制架构”)。
默认情况下,复制是异步的,即主库将二进制日志传输到从库之后,并不关心从库是否成功收到。从库是否收到这些二进制日志,不影响主库的任何读/写访问;而从库的复制线程也可以随意暂停或停止,并不影响主库的读/写访问。通常,异步模式能够发挥数据库的最高性能,但数据安全性却得不到很好的保证,如果对数据安全性的要求较高,可以考虑使用半同步复制。
另外,默认情况下,复制的数据是针对整个实例的(排除部分系统表),你可以根据自身需求选择是否需要复制整个实例的数据,是只复制某些库,还是只复制某些表的数据等。接下来将简要介绍MySQL中复制拓扑的一些适用场景,以及与复制相关的概念。
第2章复制的基本原理;第1章简单介绍了MySQL复制(技术)相关的基本概念,本章将详细介绍其基本原理。MySQL的复制技术自诞生以来,随着各种各样的应用场景对数据安全性及复制性能的要求不断提高,也在不断迭代与优化。要深刻理解MySQL的复制技术,就要从它的基本原理说起,下面将对此展开介绍。
第3章复制格式详解;通过前面的两章,我们了解了复制技术的使用场景及其基本原理与实现,知道复制是通过二进制日志记录在主从库之间的流转来实现的。第1章对二进制日志的记录格式做了简要的介绍,本章将详细阐述复制格式。
第4章传统复制与GTID复制;MySQL 5.6之前的版本只支持传统复制,即“基于二进制日志文件(binlog file)和位置(binlog pos)的复制”。在该复制模式中,复制拓扑的初始化配置和变更、复制的高可用切换等操作都需要找到正确的二进制日志文件和位置,否则就无法正确复制。然而,寻找该位置信息的过程所涉及的操作步骤较为繁琐,于是在MySQL 5.6及其之后的版本中,出现了基于GTID的复制(为了表述上的方便,文中也简称为GTID复制)模式。它利用GTID自动定位的特性,不再需要二进制日志的位置信息,也就省去了寻找这些信息所需的繁琐步骤,极大地简化了复制拓扑的初始化配置和变更,以及复制的高可用切换等操作。本章将对这两种复制模式做基本的介绍。
第5章半同步复制;除了内置的异步复制,MySQL 5.7还支持通过插件方式实现半同步复制接口。相对于MySQL 5.5和MySQL 5.6中的半同步复制,通常我们将MySQL 5.7中的半同步复制称为“增强半同步复制”,也称为“无损复制”(MySQL 5.5和MySQL 5.6虽然也支持半同步复制,但不能保证“无损复制”,详见5.4节“半同步复制的注意要点”)。本章将详细介绍MySQL 5.7中的半同步复制。
第6章多线程复制;MySQL 5.6之前的版本不支持从库并行重放主库的二进制日志,所以一旦主库的写压力稍微大一点,从库就容易出现延迟。当然,目前最新的MySQL版本已经能够很好地支持多线程复制。为了便于理解复制是如何一步一步演进为多线程复制的,本章将从单线程复制说起。在开始学习本章内容之前,也许你需要回顾一下复制的基本原理,详见第2章“复制的基本原理”。
第7章多源复制;一些业务数据被打散到多个数据库实例上之后,数据库的备份和恢复就比较烦琐,有没有什么简单的方案能够解决这个问题呢?MySQL5.7.6引入了复制通道的概念,使得同一个从库可以同时接收多个主库的数据,一个复制通道逻辑上就对应一个主库。本章将简要介绍如何在复制拓扑中使用通道、通道相关的概念,以及相关系统的设置对单源(单个复制通道)复制的影响。
第8章从库中继日志和状态日志;从库I/O线程从主库读取的二进制日志需要暂时存放在从库的磁盘文件中,这个磁盘文件就是中继日志(relay log)。I/O线程并不负责解析与重放二进制日志,而是由SQL线程负责。当复制线程正常运行时,我们对复制线程的工作位置(这里指的是I/O线程读取主库二进制日志的位置和SQL线程重放的位置)不是很敏感,但当从库的数据库进程或者主机发生崩溃(crash)时,从库重新启动之后,需要知道上一次复制线程进行到的正确位置,也就是说对复制线程的工作位置需要持久化,否则一旦丢失将无法知道上一次复制进行到哪里了。状态日志被用来对复制线程的工作位置进行持久化(状态日志包括relay loginfo和master info两种类型,具体用途详见下文),本章将详细介绍中继日志和状态日志。
第9章通过PERFORMANCE_SCHEMA库检查复制信息;通常,我们在查看与复制相关的状态信息时,都习惯性地使用SHOW SLAVE STATUS语句。也许你会说,“我也会用PERFORMANCE_SCHEMA库的表查看一些在复制时的报错信息”,但是,你知道SHOW SLAVE STATUS语句输出的信息和PERFORMANCE_ SCHEMA库中的复制信息记录表之间有什么异同吗?本章将详细介绍二者的异同。
第10章通过其他方式检查复制信息;第8章详细介绍了如何通过mysql.slave_master_info和
mysql.slave_relay_log_info表来检查复制信息,第9章详细介绍了如何通过PERFORMANCE_SCHEMA库下的复制记录表来检查复制信息,本章将对前面章节中未提及的与复制相关的小细节进行补充说明。例如,通过SHOW PROCESSLIST语句来查看I/O线程和SQL线程的状态信息、通过PERFORMANCE_SCHEMA库中的user_variables_by_thread表来查看I/O线程向主库注册的自定义变量信息等。
第11章MySQL复制延迟Seconds_Behind_Master究竟是如何计算的;在主从复制拓扑中,监控复制延迟是必不可少的工作。如果应用场景对复制延迟并不敏感,那么大多数时候通过采集SHOW SLAVESTATUS语句输出信息中Seconds_Behind_Master字段的值监控复制延迟就已经足够了。相信有MySQL使用经验的人对这种方法并不陌生,我们都知道Seconds_Behind_Master的值在某些场景下并不是那么可靠,也或多或少都知道一些计算这个值的方法。但这些计算方法真的正确吗?接下来,本章将对此进行讨论并确认正确的计算方法。
第12章如何保证从库在意外中止后安全恢复;为了保证复制线程能够在从库意外中止之后正确恢复到中止之前的状态(有时称为crash-safe),需要对从库中的某些系统变量与复制选项设置合适的值。本章将详细介绍如何设置这些系统变量与复制选项及其对应的不同影响。
第13章MySQL Server复制过滤;MySQL Server的复制过滤功能,虽然通常在标准的生产环境中不建议使用(因为如果使用不当,可能导致各种各样的主从库数据不一致的问题),但在某些场合下,为了区分业务数据或保证数据安全性(例如,对业务数据所有的写操作都统一写入主库,但是对业务数据的读取则需要按照业务模块来划分,因为不同业务系统之间有数据安全隔离的需求),MySQL Server的复制过滤功能可能是一个简单、快捷的实现方案。在有足够的了解,而且通过缜密的规划来规避其缺点的前提下,也可以将其列为能解决此类需求的可选方案之一。本章将简单梳理一遍MySQL Server复制过滤功能的实现逻辑。
方案篇
第14章搭建异步复制;异步复制是相对于同步复制和半同步复制而言的,这三者之间的区别,可参考第1章“复制的概述”和第5章“半同步复制”。相比于其他两种复制,异步复制的速度最快,且主库性能不受从库复制性能的影响,它也是MySQL中最早出现的复制技术。在MySQL 5.6之前,无论是同步复制、半同步复制,还是异步复制,都是传统复制(基于二进制日志文件和位置),维护复制拓扑时比较麻烦。MySQL 5.6及其之后的版本支持GTID复制模式,使得对复制拓扑的维护变得非常方便快捷。本章将详细介绍在传统复制和GTID复制的两种复制模式下,搭建异步复制的过程,关于传统复制和GTID复制的原理,可参考第4章。
第15章搭建半同步复制;本章将介绍性价比最高、实现方式最简单、最容易安装及部署的半同步复制解决方案。
第16章通过扩展从库以提高复制性能;在大多数OLTP或OLAP业务系统中,对数据库的操作通常读多写少(特殊场景除外。例如,数据采集、数据恢复等是写多读少的场景),所以,随着业务的增长,原有复制拓扑中的数据库实例渐渐难以支撑读访问流量(假定复制拓扑中的主库提供写服务,所有从库提供读服务),迫切需要提高数据库的读访问能力。那么,我们就需要一个方便、快捷、轻量的读访问能力扩展方案。本章以横向扩展为例,介绍整个方案的实施过程。
第17章复制模式的切换;MySQL 5.5及其之前的版本,由于不支持GTID机制,所以它们使用的都是传统复制(即基于二进制日志文件和位置的复制)。MySQL 5.6中引入了GTID机制,该机制有众多优点(详情可参考第4章、第12章等章节,以及下文中将要演示的复制模式切换过程,这里先不展开介绍),为MySQL管理者的维护工作带来了极大便利,所以大多数用户都会选择切换到GTID复制模式(即基于GTID的复制,下文统一称为“GTID复制”)。而除了一些特殊的应用场景之外,通常很少需要从GTID复制切换到传统复制。
在第1章中,我们将传统复制和GTID复制称为“数据同步方法”,但通常我们更喜欢将其称为“复制模式”。本章将对这两种复制模式中的一些概念以及它们的相互切换过程进行详细介绍。
第18章复制拓扑的在线调整;如果MySQL数据库的访问量并不大,那么可以使用一主一从的复制拓扑,主库提供写服务,从库提供读服务,采用MySQL 5.7的半同步复制配合高可用软件,即可实现数据的高可用、零丢失。在读访问量较大的场景中,可能需要将复制拓扑扩展为一主多从,甚至双主多从,缓解单个从库的读访问压力(关于从库的扩展,可参考第16章“通过扩展从库以提高复制性能”)。但扩展复制拓扑也就意味着增加管理难度。例如,在主库发生故障或者在线切换到新主库之后,连接原主库的从库需要进行调整以连接到新主库;或者为了提高复制性能,可能会增加一个中间库,并将复制拓扑调整为级联复制,这时就涉及复制拓扑的在线调整。本章将详细介绍复制拓扑的在线调整步骤。
第19章主从实例的例行切换;主从实例的例行切换,在这里指的是根据业务变更或者运维管理的需要,主动将主库的写访问请求转移到其他数据库实例,这个切换不是由故障触发的。那么,在什么场景下需要进行例行切换呢?
第20章数据库故障转移;数据库故障转移,在这里指的是由于主库故障而触发的,将主库读/写业务转移到其他数据库实例继续对外提供读/写服务的过程。例如,当探测到主库主机宕机、主库数据库进程不存在、主库数据库无法登录、主库数据库无法执行查询或更新操作时,为尽量减小主库故障对业务造成的影响,需要尽快将读/写访问入口转移到处于正常状态的数据库实例上。
在实际场景中,造成主库无法提供读/写服务的原因多种多样。对于数据库管理系统来说,考虑的因素越多,就会越复杂,可靠性就会越差。所以,通常建议将问题分为可确定故障原因和不可确定故障原因的场景,然后分别采用不同的方式解决。如果混为一谈,可能经常导致误操作转移。
对于可确定故障原因且通过自动故障转移能够恢复业务访问的场景,可以使用数据库管理系统自动进行故障转移。
第21章搭建多源复制;MySQL的多源复制(也可以称为“多主复制”),指的是复制拓扑中的从库同时从多个源Server(主库)接收二进制日志进行重放。多源复制可用于将多个Server的数据备份到单个Server中(从库),以及在分库分表场景中,将来自多个Server的分片表数据合并。从库在应用来自多个主库的二进制日志时,不会执行任何冲突检测或解决冲突,如有需要,则靠应用程序来解决这些问题。在多源复制拓扑中,从库会为每个主库建立一个单独的复制通道(单独的I/O线程、协调器线程、Worker线程),各自重放各自的二进制日志,互不依赖。
关于多源复制的原理与使用场景,详情可参考第7章“多源复制”。本章将详细介绍多源复制的搭建步骤。
第22章MySQL版本升级;MySQL的版本并不需要常升级,但如果要使用新版本的某个新特性,或者为了修复旧版本的某个bug,就不得不进行版本升级。在生产环境中,为避免单点故障,通常都会用多个MySQL实例构建一个复制拓扑。为了使升级操作对业务的影响尽可能小,可以先升级复制拓扑中的只读实例(从库),然后再执行一次主从角色切换(会造成应用短暂中断),最后将主库当作从库再做一次升级即可。
本章只阐述在复制拓扑中升级MySQL版本的一些注意事项,关于复制拓扑中的MySQL版本升级步骤,可参考19.2节“在线切换”。
第23章将不同数据库的数据复制到不同实例;将不同数据库中的数据复制到不同的实例(MySQL Server),在实现上具体指的就是从库将主库的全量二进制日志拉取到本地的中继日志之后,从库SQL线程在重放这些二进制日志时,根据自身配置的复制过滤规则,选择需要应用哪些库与表,以及需要忽略哪些库与表。
当然,在主库端也支持复制过滤,虽然在主库端配置复制过滤后能够减少二进制日志的传输量,但主库端只支持库级别的过滤规则,而且容易导致主从库数据不一致。通常不建议在主库端配置复制过滤规则,可靠的复制过滤都是在从库端实现的,因为这样才更合理,每个从库根据自己的需要来灵活配置复制过滤规则。
关于复制过滤的原理与流程,本章不做过多阐述,可参考第13章“MySQL Server 复制过滤”。本章将以在从库端配置复制过滤规则为例,详细介绍其操作步骤。
第24章发生数据误操作之后的处理方案;在当今信息大爆炸的时代,数据与信息是一家技术型公司赖以生存的基石。然而,在日常维护存放数据与信息的服务器过程中,对于技术人员来说,难免会有误操作(例如,误修改、误删除、误写入等)。不过,我们可以尽量减少误操作带来的损失。发生误操作之后,可以通过一些方法尽量恢复被误操作的数据。本章将详解介绍MySQL中几种常见的简单有效的恢复误删除数据的方法。
第25章常用复制故障排除方案;作为MySQL数据库管理人员,我们在日常的工作中或多或少都会碰到MySQL复制相关的问题,有些问题可能很快就解决了,有些问题具有一定的迷惑性,可能需要排查很久才能找到具体的原因。对于后者,我们大概率会在日后的工作中再次遭遇它们。再次遇到的时候,你是否有似曾相识但怎么也想不起来具体细节的感觉呢?
为了避免这种尴尬,建议在处理完故障之后,立即以文档形式总结故障现象、其复现与排查过程、解决方案和规避方法,予以留存。
另外,故障虽然多种多样,但其处理思路与流程是具有共性的。本章以处理MySQL复制相关的故障为主题,详细介绍一个排除复制故障的通用方案(注意,本章只讲解思路与流程,不介绍细节)。
参考篇
第26章二进制日志文件的基本组成;在使用MySQL数据库的平台上,很多关键的应用场景都是基于二进制日志实现的,例如主从复制(这也是本书的主题,前面用大量的篇幅介绍了复制的原理与使用案例)、基于时间点的备份与恢复、误操作数据的回滚、供数(解析二进制日志文件,并将得到的文本数据传输到另一个平台,如数据仓库、Kafka等)等,但是很少有人详细了解过二进制日志。本章将从二进制日志事件类型的角度对二进制日志文件中的内容进行详细的介绍。
第27章常规DDL操作解析;
第28章为何二进制日志中同—个事务的事件时间点会乱序;对于这个问题,相信很多人都只是大概知道其中的原理,并没有做过具体的案例分析。本章将以一个常见的UPDATE语句更新数据的事务作为案例,详细分析其中的过程。
第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语句是如何被正确复制到从库的。
第31章复制CREATE TABLE ...SELECT语句;第30章解释了CREATE ... IF NOT EXISTS语句(除CREATE TABLEIF NOT EXISTS ... SELECT之外)是如何被正确复制的,本章我们来看第30章未涉及的CREATE TABLE IF NOT EXISTS ... SELECT以及CREATE TABLE ... SELECT语句是如何被正确复制的。
第32章在主从复制中使用不同的表定义;正常情况下,我们使用MySQL主从复制时,都是为了保证主从库数据的一致性,但有时可能会遇到一些需求。比如,出于安全的考虑,上游数据库同步到下游数据库时需要过滤某些字段,或者中间库需要对字段类型进行调整,那么在MySQL中如果不同步表(结构)定义,人为地分别在主库和从库上创建不同的表定义,MySQL复制时只负责数据同步,这样可行吗?本章就来介绍一下在主从复制中使用不同的表定义进行复制。
第33章复制中的调用功能;日常工作中,DBA不可避免会遇到使用存储过程、触发器、自定义函数等的场景,因此,本章我们来讲一讲在MySQL的复制中是如何保证存储过程、触发器、自定义函数等的正确性的。
第34章复制LIMIT子句;DBA一般会建议开发人员在使用MySQL时不要使用大事务,比如在对某一张表清理上百万行数据时,不建议直接用一条DELETE语句清理完所有数据,而是建议使用LIMIT子句,小批量、多次清理。LIMIT子句对符合条件的结果集限定返回的行数,但是无法明确是哪几行。在MySQL的复制中,对于LIMIT子句这种具有不确定性的子句如何保证复制的一致性呢?本章我们就来看看LIMIT子句是如何被正确复制的。
第35章复制LOAD DATA语句;LOAD DATA语句能很方便地将文件中的数据导入MySQL,是开发人员平时习惯使用的一种导入数据的方式。本章我们就来看一看,在MySQL主从复制架构中是如何保证LOAD DATA语句正确地从主库复制到从库的。
第36章系统变量max_allowed_packet对复制的影响;熟悉MySQL的人都知道,MySQL有许多可设置的系统变量,用于控制其行为或者性能,比如系统变量max_allowed_packet可以控制客户端与服务端连接后,传输的数据包的最大尺寸。本章我们讲一讲系统变量max_allowed_packet对复制的影响,再介绍一些其他类似的系统变量。
第37章复制临时表;与普通的表相比,相信许多读者对于临时表应该既陌生又熟悉,本文讲的是MySQL的复制,那么本章就来讲一讲临时表在MySQL中是如何复制的,以及在复制中遇到临时表该怎么办。
第38章复制中的事务不—致问题;在MySQL复制中,我们都会担心主从库数据一致性的问题。有些情况下,主从库数据不一致是由于主从库事务不一致而导致的。本章我们就来讲一讲MySQL复制中事务不一致的类型、原因以及结果。
资源获取:
大家点赞、收藏、关注、评论啦 、查看👇🏻👇🏻👇🏻微信公众号获取联系方式👇🏻👇🏻👇🏻
精彩专栏推荐订阅:在下方专栏👇🏻👇🏻👇🏻👇🏻