这是一篇关于MySQL数据库,redo log,LSN,崩溃恢复,在线热备的长文,耐心读完,如果没有收获,可以捶我。
研发的童鞋每次对MySQL库表做重大操作之前,例如:
那实际上,DBA是如何对MySQL进行库备份的呢?今天,和大家说说MySQL备份的来龙去脉,以及内核原理。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从架构,离线物理备份是如何实施的呢?(1)第一步,将一个从库从集群里摘下并下线,此时离线库文件不会再发生变化;(2)第二步,scp拷贝库文件,即完成了库的物理备份;缺点也显而易见:备份过程中从库无法对线上持续提供服务。那么问题来了,有没有一种方案,又能够快速备份物理文件,又能够持续对线上提供服务呢?PXB的全称是,Percona XtraBackup,官网是这么吹的:PXB是全世界唯一一款开源免费的,支持MySQL热备的,非阻塞备份工具。画外音:Percona XtraBackup is the world’s only open-source, free MySQL hotbackup software that performs non-blocking backups tool.
(1)保持数据库持续提供线上服务,库文件不断变化时;为了把问题讲透,这就要从redo log,从LSN,从MySQL的故障恢复(crash-recovery)机制聊起。事务提交后,必须将事务对数据页的修改刷(fsync)到磁盘上,才能保证事务的ACID特性。这个刷盘,是一个随机写,随机写性能较低,如果每次事务提交都刷盘,会极大影响数据库的性能。(1)先写日志(write log first),将随机写优化为顺序写;第一个优化,将对数据的修改先顺序写到日志里,这个日志就是redo log。假如某一时刻,数据库崩溃,还没来得及将数据页刷盘,数据库重启时,会重做redo log里的内容,以保证已提交事务对数据的影响被刷到磁盘上。一句话,redo log是为了保证已提交事务的ACID特性,同时能够提高数据库性能的技术。逻辑上,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既能够实现以页为单位顺序刷盘数据,又极大缩小了日志大小,其性能又进一步的增加了。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)的概念。要聊redo log,要聊故障恢复,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
又假设,第一个事务trx1已经刷盘,而第二个事务trx2还没有刷盘,只写了redo log。画外音:最近一次刷盘的页,即最近一次检查点(checkpoint),也是通过LSN来记录的,它也会被写入redo log里。为了提高数据库性能,数据库基本都是使用WAL(Write Ahead Log)的方式,先写日志再刷盘,所以很容易能够想到,磁盘数据页里的LSN,会小于最新redo log中的LSN。画外音:此时,redo log中记录的checkpoint也是1000。它和MySQL的故障恢复(crash-recovery)机制紧密相关。四、InnoDB故障恢复(crash-recovery)这里的故障恢复,是指MySQL非正常退出,然后再次启动之前,要恢复数据一致性的操作。第一步,redo log操作:保证已提交事务影响的最新数据刷到数据页里。第二步,undo log操作:保证未提交事务影响的数据页回滚。第三步,写缓冲(change buffer)合并。画外音: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的启动日志更形象的说明了这一点:(2)然后不断的扫描大于checkpoint的redo log,不断的恢复数据;画外音:redo log的LSN可以看到恢复的进程。第一,幂等性,同一条redo log执行多次,不影响数据的恢复。第二,崩溃恢复时,从比checkpoint更早的LSN开始执行恢复,也不影响数据最终的一致性,因为一个数据页,最终一定会被更大值的LSN日志恢复到最新的数据上来;(1)保持数据库持续提供线上服务,库文件不断变化时;首先,PXB启动一个线程,并不断监听并复制redo log的增量到另外的文件,不能直接备份redo log的原因是,redo log循环使用的,PXB则必须记录下checkpoint LSN之后的所有redo log。然后,PXB启动另一个线程,然后开始复制数据文件,复制数据文件过程可能会比较长,整个过程中数据文件可能在不停的修改,导致数据不一致。但没有关系,所有的修改都已经记录在了第一步中,额外记录的redo log里。画外音:务必注意,备份redo log的线程,必须在开始备份数据文件之前启动,之后结束。最后,通过备份的数据文件,重放redo log,执行类似于MySQL崩溃恢复过程中的动作,就能够使得数据文件恢复到能保证一致性的checkpoint检查点。画外音:PXB还可以对非MySQL,非InnoDB进行在线热备,这里就不展开了。这是一篇关于MySQL数据库,redo log,LSN,崩溃恢复,在线热备的长文,耐心读完,如果没有收获,可以捶我。
MySQL内核相关文章:
《缓冲池(buffer pool),这次彻底懂了!》
《写缓冲(change buffer),这次彻底懂了!》
《日志缓冲(log buffer),这次彻底懂了!》
《double write buffer,这次彻底懂了!》
都看到这里了,不来个三连吗?谢转!