《MySQL实战45讲》学习小结(运维篇)

丁奇老师《MySQL实战45讲》的学习小结

第一篇:基础概念

第二篇:运维管理

之前对数据库主要是使用,运维管理做得很少,主要是备份、备份、备份 ^_^

通过这门课,在运维管理这方面学到的知识最多,学习内容的整理也最吃力。

照例从问题入手。

要理解MySQL在运维方面的机制,首先要理解数据库运维面对的问题:

扫描二维码关注公众号,回复: 14720194 查看本文章
  1. 数据库服务崩溃,但存储器正常,如何恢复?

  2. 服务器损毁,服务器上数据损坏或丢失,如何恢复数据和服务?

  3. 误删数据,如何恢复?

  4. 系统压力超过单个数据库服务器的能力,如何分布到多个服务器?

  5. 数据库性能急剧下降,怎么办?

在处理这些问题之前,先理解MySQL的几个运行机制,再来梳理如何应对这些问题。

第一部分:运行机制

1. 日志和主备同步(第24讲,28讲)

主库和从库之间的数据同步过程,如下图所示:

从库上有两个线程。

io_thread负责向主库发起请求,建立长连接,然后接收主库发送的binlog,写入relay log。从库在发起请求时,可指定从哪个位置开始获取binlog。

sql_thread负责读取relay log,解析出日志里的命令,并执行。

从一个事务在主库上完成,到这个事务在从库上完成,时间差即为同步延迟。

如果主库掉电,事务已完成,但binlog尚未发送给备库,会导致数据丢失。

为了解决这个问题,可采用 semi-sync 模式。在主库上,事务需等待至少一个从库确认已收到日志,才能完成事务,向客户端发送response。

主库上有个dump_thread,负责监听来自从库的请求,并随时把binlog发送给从库。

一个主库可能要监听多个从库的请求,多个从库请求的binlog起始位置也可能不同。

因此一个主库不要带很多个从库,以免binlog的同步消耗太多资源。

2. 主备切换(第24讲,25讲,27讲)

主备服务器的配备,目前多采用对称结构,即主备服务器的硬件配置一致,各一台,都提供读写服务,这样一旦主库出现问题,可以快速切换到备库,对应用的影响最小。备库虽然可读写,但正常情况下不做写操作。以下的内容均以此为前提。

由于存在同步延迟,因此在主备切换时,从可靠性优先角度,常见做法是先把主库设为只读,待同步延迟归0后,再切换到备库。通常情况下同步延迟小于1秒,这期间出现短暂的全库只读,对应用的影响不大。(需要HA工具支持)

但如果遇到异常,导致同步延迟很大,或者主库掉电无法访问等情况,只能选择可用性优先策略。这种情况下,可能会出现数据错误,需要人工介入。

数据库服务器的配备,一主多从是目前使用最多的结构。

一主多从结构下,备库是从库的一种,它可能被切换为主库,而其他从库不会。

一主多从结构下的主备切换,如下图所示:

《MySQL实战54讲》第27讲 一主多从基本结构 -- 主备切换

在一主多从结构下,主备切换包括了从库切换master的过程。

除了主备的数据同步延迟之外,还需要考虑各从库的同步延迟,且每个从库的同步延迟不尽相同。

位点:

5.6版本之前,MySQL只能通过找位点的方式。

从库跟主库建立联系时,可指定从哪个binlog文件、哪个偏移量开始同步。这个文件+偏移量就是位点。但对于同一个事务(sql),在主库和备库上,位点是不同的。可通过切换时间定位到一个近似值,但并不精确。需要人工介入。

GTID:

MySQL 5.6版本引入了GTID以解决这个问题。

GTID = server_uuid:gno

gno是在每个MySQL实例上,单调递增的数值,在事务提交时分配。server_uuid + gno 的组合可以保证全局唯一。GTID被包含在binlog中,发给从库。

每个MySQL实例都维护了一个GTID集合,记录“这个实例执行过的所有事务”。

在主备切换时,从库会发送本实例的GTID集合,例如 set B 给新的主库 A'。新的主库 A' 对比自己的和从库B的GTID集合,先检查set B在本实例上全部存在,再发送 set A' - set B 这些事务的binlog给从库B,然后就回到正常的binlog同步。

GTID的全部集合很大,MySQL应该会定期移除历史,不断提高水位。这个教程中未涉及,是猜测。

GTID除了解决主备切换时的同步问题,还可以用来在同步时跳过特定的事务。

3. 缓存和落盘(第12讲,23讲)

为了提升性能,MySQL对数据的读写都优先在内存上进行,并选择合适的时机将数据读入内存、将修改结果同步到磁盘。不仅对数据如此,对日志也使用这个策略。

为了保证数据可靠性,需要控制redo log和binlog尽快写入磁盘。

MySQL提供了两个参数来控制:

  • innodb_flush_log_at_trx_commit = 1 每次事务提交时都将 redo log 直接持久化到磁盘

  • sync_binlog = 1 每次提交事务都会执行 fsync

redo log的刷盘有两点说明:

  • 根据上一篇redo log + binlog 实现crash-safe的机制,有binlog的情况下,有prepare状态的redo log即可恢复数据。因此,一个事务只需要等待redo log prepare刷盘,不用等redo log commit刷盘。

  • redo log cache为共用,在一个事务提交时,把该事务最后一条日志及在此之前的所有redo log刷盘,实现group commit,提升IO效率。

在这样的机制下,我们可以认为MySQL的数据(及log)不会丢失。

后续关于运维问题的小结,均在此基础上进行。

第二部分:运维场景

1. 数据库服务崩溃,但存储器正常,如何恢复(第2讲,23讲)

先明确恢复的目标。

站在应用的角度,提交了事务,数据库服务返回response,即认为数据已更新。这类数据需要恢复。如果事务未提交,数据库服务崩溃,则数据不应更新,数据库恢复后的状态,同事务回滚。

如果提交了事务,但未收到response服务器连接就断开了,事务的状态依赖于数据库的log来判断。

详见如下流程图:

几点说明:

  1. 客户端发出“提交事务”命令后,服务器成功返回,则客户端认为事务完成、数据已更新。但是,如果 2、3 这两步都是异步请求的话,在服务器崩溃时,binlog和redo log有可能都未被持久化。因此教程中建议采用“双1”配置,保证在一个事务完成前,binlog和prepare状态的redo log被持久化,以保证能恢复数据。

  2. 客户端发出提交“提交事务”命令后,服务器崩溃,客户端没有收到response,这种情况下,只要binlog已持久化,即视为事务已完成,需要用相应的redo log恢复数据。

  3. 数据的持久化,即“flush脏页”,必然是异步的。

2. 服务器损毁,服务器上数据损坏或丢失,如何恢复数据和服务

这个问题分两种情况,一是主库损毁,二是从库损毁。两种情况都要利用备份。

2.1 主库损毁:

如果做了热备份,就可以将备库切换为新的主库。见上文的“主备切换”。

如果原主库上的binlog尚未被发送到备库,会发生数据损失。

binlog被发送到备库,转存为relay log后再被执行。这里有个时间差。只要业务上能接受,应在relog log都被执行后再开放服务(至少把写操作放在这个时间点后)。

如果没有热备,那就只能用 全量备份 + 全量备份时间点之后的binlog备份 来恢复。数据损失大。

2.2 从库损毁

从库损毁,只要主库正常,数据不会丢失。重建从库即可。

先把从库恢复到某个时间点的数据版本,再执行这个时间点之后的binlog。前一步通过物理拷贝恢复,后一步为逻辑恢复。完成这个操作后,再从主库获取后续的binlog(用位点或GTID找齐)。

从库损毁,主要考虑的是应用的影响,需要尽快发现问题,把原来指向从库的请求重定向到其他库,并通知运维人员,尽快重建从库。

尽快发现数据库出现异常,这很重要,早发现早处理,减少对业务造成的影响。这也是HA的基础。

判断数据库是否出现问题,有很多种方式,见第29讲,这里不再赘述。

3. 误删数据,如何恢复(第31讲)

误删行:

根据binlog,flashback(binlog_format=row,binlog_row_image=FULL)

恢复操作不要直接在主库上执行。在别的库上执行,并确认后,再将其恢复回主库。

误删库/表:

全量备份,加增量日志恢复。重放增量日志时,跳过误操作的那个语句。最好使用GTID模式。

要求线上有定期的全量备份,并且实时备份binlog。

因为全量备份可能是数天前的,重放增量日志很慢,要着重考虑如何加速恢复速度。

rm删除数据

即上一条,服务器损毁、数据损坏的情况。

建立有高可用机制的MySQL集群应对这种情况,并做好备份。

强调两点:

- 做好预案,尽量把恢复过程固化为工具定期演练

- 预防数据误删,例如:只分配必要的权限,删除前先做标记(更新、重命名)。

4. 系统压力超过单个数据库服务器的能力,如何分布到多个服务器(第28讲)

一主多从结构,主库可读写,从库只读,这是典型的读写分离结构,以分摊主库的压力。

在这种结构下,客户端访问数据库服务,有两种方案

  1. 客户端自行制定访问哪个库

  2. 通过一个proxy层,由proxy来选择访问哪个库

两种方案,都会遇到“过期读”的问题。即由于存在同步延迟,在从库上读到的不是最新数据。

存在几种不同的处理方案:

  1. 强制走主库,由客户端判断业务上是否必须读取最新的数据,是则指定访问主库。

  2. sleep,多用在写完之后的读,例如发帖后的查看,控制延迟1秒再发查看请求。看起来很笨拙,但很实用。主从延迟,正常情况下不超过1s。

  3. 等待从库获取最新数据,这又可细分3种方法
     - 等待 seconds_behind_master = 0
     - 先查已接收的最新位点,等待从库执行的最新位点跟前者一致
     - 先查备库已收到的所有日志的GTID集合,等待这些事务在从库上都已经被执行

后两种方法,只能保证已接收的日志都已经被执行,无法处理主库执行后,从库还未接收日志的情况。为了解决这个问题,需要结合 semi-sync 机制。

5. 数据库性能急剧下降,怎么办(第22讲)

首先分析原因,按照原因,对症下药。必要时,牺牲一定的可靠性,来提升可用性。

可能的原因:

连接风暴、慢查询、QPS突增、长事务、online DDL、死锁检测。

应对策略

  • 白名单、账号管理,可用于屏蔽来自特定服务器、特定应用的请求,避免局部问题拖垮整体。

  • sql动态重写,可临时屏蔽大量占用资源的问题sql,或强制其使用正确的索引。

  • online DDL是风险比较高的操作,最好在从库上执行,然后做主从切换,再在原主库上执行。

  • 长事务往往会带来并发问题,应进行监控,发现问题,及早解决。

这里要再次强调下:

  1. “消灭问题”优于“解决问题”。设立规范、流程,减少人为操作失误。良医治未病。

  2. 建立监控体系,定期监测长事务、慢SQL等问题,尽早发现,不让小问题堆积成大问题。

  3. 问题应对方法流程化、工具化,定期演练,遇到问题时才能快速响应。功夫在平时。

本文内容为丁奇老师《MySQL实战45讲》的学习笔记,只是一个提纲。

这门课程的内容和组织方式,每一讲的思考题、大家的留言、老师的点评,都非常棒。

继续推荐。

黄鹤

2019-12-03

猜你喜欢

转载自blog.csdn.net/oldcrane/article/details/103450575
今日推荐