MySQL,在线热备的内核原理!

网友投稿 674 2023-06-05

MySQL,在线热备的内核原理!

MySQL,在线热备的内核原理!

研发的童鞋每次对MySQL库表做重大操作之前,例如:

修改表结构;批量修改或者删除数据;

都会向DBA申请进行数据库的备份。

画外音:又或者说,不备份直接操作啦?

那DBA童鞋是怎么进行MySQL备份的呢?

调研了几十个RD和QA,基本是3种答案:

不太清楚;在线逻辑备份,mysqldump;离线物理备份(冷备),拷贝从库库文件;

那实际上,DBA是如何对MySQL进行库备份的呢?

现在基本上使用的是PXB方案。

今天,和大家说说MySQL备份的来龙去脉,以及内核原理。

在线逻辑备份,mysqldump是咋回事?

mysqldump是MySQL工具集中的一个工具,可以用来导出或备份数据。

mysqldump的产出物是一个包含了建表,插入数据的SQL语句集合,类似于这样:

-- MySQL dump 1.2.3 -- Host: localhost Database: test -- Server version 4.5.6  CREATE TABLE t_user (  id int(11)NOT NULL unique,  name varchar(40) NOT NULL default '',  PRIMARY KEY  (id) );  INSERT INTO t_user VALUES (1,'shenjian'); INSERT INTO t_user VALUES (2,'zhangsan'); INSERT INTO t_user VALUES (3,'lisi');

因此,它才称为逻辑备份。

使用mysqldump进行备份的优点是:可以在线进行,不影响数据库对线上持续提供服务。

缺点也显而易见:相比物理备份拷贝库文件,备份和恢复都要慢非常多。

离线物理备份,拷贝从库库文件又是咋回事?

为了提高备份效率,缩短备份时间,这也就引发了第二种方案,直接物理备份库文件。

如上图所示,数据库集群设置为左侧的1主2从架构,离线物理备份是如何实施的呢?

第一步,将一个从库从集群里摘下并下线,此时离线库文件不会再发生变化;第二步,scp拷贝库文件,即完成了库的物理备份;文件拷贝完成后,将从库挂回集群。

使用离线物理备份的优点是:备份和恢复都非常快。

缺点也显而易见:备份过程中从库无法对线上持续提供服务。

那么问题来了,有没有一种方案,又能够快速备份物理文件,又能够持续对线上提供服务呢?这就是如今MySQL备份最流行的PXB方案。

什么是PXB?

PXB的全称是,Percona XtraBackup,官网是这么吹的:PXB是全世界唯一一款开源免费的,支持MySQL热备的,非阻塞备份工具。

画外音:Percona XtraBackup is the world’s only open-source, free MySQL hotbackup software that performs non-blocking backups tool.

那么,PXB是如何实现:

保持数据库持续提供线上服务,库文件不断变化时;通过MySQL文件;来进行库文件物理热备份的呢?

为了把问题讲透,这就要从redo log,从LSN,从MySQL的故障恢复(crash-recovery)机制聊起。

一、redo log

为什么要有redo log?

事务提交后,必须将事务对数据页的修改刷(fsync)到磁盘上,才能保证事务的ACID特性。

这个刷盘,是一个随机写,随机写性能较低,如果每次事务提交都刷盘,会极大影响数据库的性能。

随机写性能差,有什么优化方法呢?

架构设计中有两个常见的优化方法:

先写日志(write log first),将随机写优化为顺序写;将每次写优化为批量写;

这两个优化,数据库都用上了。

第一个优化,将对数据的修改先顺序写到日志里,这个日志就是redo log。

第二个优化,就是redo log的三层架构:

log buffer:应用层缓冲;OS cache:操作系统缓存;redo log file:物理文件;画外音:此处不是本文的重点,不再展开详述。

假如某一时刻,数据库崩溃,还没来得及将数据页刷盘,数据库重启时,会重做redo log里的内容,以保证已提交事务对数据的影响被刷到磁盘上。

一句话,redo log是为了保证已提交事务的ACID特性,同时能够提高数据库性能的技术。

二、redo log的格式

逻辑上,MySQL以行(row)为单位管理数据;物理上,MySQL以页(page)为单位管理数据,MySQL的缓冲池(buffer)机制,也是以页为单位管理数据,事务提交之后,不用每次都随机写落盘刷新数据页,而是通过顺序写redo log来提高性能,那么redo log是直接保存等待刷盘的数据页吗?

如果redo log直接保存待刷盘的数据页,存在这样的问题,假如某个SQL语句只修改了一行记录里的一个属性,例如:

update set sex=1 where name='shenjian'

物理上,其实只修改了1个字节,难道redo log要将这个属性所在的一页数据(16K)全部保存下来吗?

完全不用,redo log只需要记录:

某个数据页中(page num);某个某个偏移位置(offset);某个类型的数据(type);改成了什么值(value);

如此一来,redo log既能够实现以页为单位顺序刷盘数据,又极大缩小了日志大小,其性能又进一步的增加了。

update set sex=1 where name='shenjian'

仍以这个SQL为例,假设它修改了第1234页,偏移量为5678处,1个字节的数据,这个字节的sex由0改成了1,那么,很容易想到redo log是类似于这样的一个结构:

如此一来,当数据库崩溃的时候,如果缓冲池中的数据没有来得及刷盘,就可以通过redo log,把第1234页,偏移量为5678处的1个字节改为1,以此来恢复数据。

当然,MySQL会通过一系列的数据结构对redo log来进行管理,最小单位的redo log是一个512字节的数据块(block),这个数块由12字节的header,508字节的body,4字节的trailer组成,body里保存的就是上述数据页如何进行修改的记录。

记录redo log的文件有若干个,每个都固定大小,循环使用。

画外音:为了使得行文通俗易懂,本文尽量没有提及Mini-Transaction(mtr)的概念。

三、LSN

要聊redo log,要聊故障恢复,LSN是一个绕不开的概念。

什么是LSN?

LSN,Log Sequeue Number,直译过来叫日志序列号,是InnoDB中,随着日志的写入,一个只增不减的8字节序列号。

听上去叫日志序列号,但LSN并不只存在redo log中,它还存储在数据页里。

画外音:缓冲池中的数据页,磁盘上的数据页都存储了LSN。

数据页(page)里存储的LSN,可以用来标记数据页的“版本号”,记录该数据页最后一次被修改的日志序列的位置。

举个例子,假设逻辑上连续执行了两个事物,且都已经提交:

trx1: update set sex=0 where name='shenjian'         redolog lsn=1000 trx2: update set sex=1 where name='shenjian'         redolog lsn=1001

画外音:lsn增加了。

又假设,第一个事务trx1已经刷盘,而第二个事务trx2还没有刷盘,只写了redo log。画外音:最近一次刷盘的页,即最近一次检查点(checkpoint),也是通过LSN来记录的,它也会被写入redo log里。

这两个事务修改的是同一个数据页,很容易想到:磁盘数据页上的LSN=1000

而redo log里有两条记录:

第一条,redo log lsn=1000第二条,redo log lsn=1001

为了提高数据库性能,数据库基本都是使用WAL(Write Ahead Log)的方式,先写日志再刷盘,所以很容易能够想到,磁盘数据页里的LSN,会小于最新redo log中的LSN。

画外音:此时,redo log中记录的checkpoint也是1000。

LSN有什么用呢?

它和MySQL的故障恢复(crash-recovery)机制紧密相关。

四、InnoDB故障恢复(crash-recovery)

这里的故障恢复,是指MySQL非正常退出,然后再次启动之前,要恢复数据一致性的操作。

画外音:可能直译叫崩溃恢复更准确一些。

InnoDB的崩溃恢复过程是怎么样的?

主要分为四个步骤:

第一步,redo log操作:保证已提交事务影响的最新数据刷到数据页里。

第二步,undo log操作:保证未提交事务影响的数据页回滚。

第三步,写缓冲(change buffer)合并。

画外音:不是今天的重点,关于写缓冲的概念,详见《写缓冲(change buffer),这次彻底懂了!》。

第四步,purge操作。

画外音:InnoDB的一种垃圾收集机制,使用单独的后台线程周期性处理索引中标记删除的数据,也不是今天的重点,未来可以详细讲。

第一个步骤中,redo log操作是如何恢复最新的数据页的呢?

(1)从redo log中读取checkpoint lsn,它记录的是最后一次刷盘的页,对应日志的LSN;

(2)如果redo log中记录的日志LSN小于checkpoint,说明相关数据已经被刷盘,不用额外操作;

(3)如果redo log中记录的日志LSN大于checkpoint,说明相关数据只写了redo log,没来得及刷盘,就需要对相关数据页重做日志,例如:

将第1234页,偏移量为5678处的1个字节改为1,以此来恢复数据。

崩溃恢复过程中,MySQL的启动日志更形象的说明了这一点:

先找到checkpoint;然后不断的扫描大于checkpoint的redo log,不断的恢复数据;

画外音:redo log的LSN可以看到恢复的进程。

多说一句,redo log还有两个特性:

第一,幂等性,同一条redo log执行多次,不影响数据的恢复。第二,崩溃恢复时,从比checkpoint更早的LSN开始执行恢复,也不影响数据最终的一致性,因为一个数据页,最终一定会被更大值的LSN日志恢复到最新的数据上来。

五、PXB在线热备原理

不知不觉写了几千字,差点忘了缘起的问题。

PXB是如何实现:

保持数据库持续提供线上服务,库文件不断变化时;通过MySQL文件;来进行库文件物理热备份的呢?

通过上面大把的铺垫,这个问题的回答就容易了。

首先,PXB启动一个线程,并不断监听并复制redo log的增量到另外的文件,不能直接备份redo log的原因是,redo log循环使用的,PXB则必须记录下checkpoint LSN之后的所有redo log。

然后,PXB启动另一个线程,然后开始复制数据文件,复制数据文件过程可能会比较长,整个过程中数据文件可能在不停的修改,导致数据不一致。但没有关系,所有的修改都已经记录在了第一步中,额外记录的redo log里。

画外音:务必注意,备份redo log的线程,必须在开始备份数据文件之前启动,之后结束。

最后,通过备份的数据文件,重放redo log,执行类似于MySQL崩溃恢复过程中的动作,就能够使得数据文件恢复到能保证一致性的checkpoint检查点。

画外音:PXB还可以对非MySQL,非InnoDB进行在线热备,这里就不展开了。

是不是很神奇啊!

戳这里,看该作者更多好文

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:MYSQL 深潜 - 剖析 Performance Schema 内存管理
下一篇:MySQL自增主键为何不是连续的呢?
相关文章