我们生产遇到这一问题:

备份在一个从节点上执行,每周日作全量备份,周一至周六做基于周日全量的增备。每次增量备份都使从库复制延迟大大增加。

mysql 数据库有700多G, buffer pool 80G , 云磁盘IO性能不佳,经过分析监控发现,系统瓶颈在随机读。80G的buffer pool远远不能容纳数据库,许多更新操作都需要从磁盘读取数据页,这大大增加了事务的执行时间。

而我们全量备份几乎不会有延迟,而增量备份延迟剧增。说明xtrabackup的增量备份算法比全量备份消耗更多的read io。(我们有单独的备份盘,write IO不会给mysql的数据目录盘造成压力)

MySQL中的lsn

LSN(log sequence number) 用于记录日志序号,它是一个不断递增的整型变量。每一个redo log 记录都会分配一个唯一的lsn。 每一个redo log entry都是由mtr修改buffer pool中的相关page而产生的。lsn这一信息也会被记录到page中。innodb page的数据结构中有两个变量记录lsn,分别有不同的作用!见buf0buf.h中的代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class buf_page_t {
...
lsn_t newest_modification;
/*!< log sequence number of
the youngest modification to
this block, zero if not
modified. Protected by block
mutex */
lsn_t oldest_modification;
/*!< log sequence number of
the START of the log entry
written of the oldest
modification to this block
which has not yet been flushed
on disk; zero if all
modifications are on disk.
Writes to this field must be
covered by both block->mutex
and buf_pool->flush_list_mutex. Hence
reads can happen while holding
any one of the two mutexes */

代码中也有较为详细的注释:

  • newest_modification
    用于记录最近一次修改该page的lsn,它是不断变化的,有新的修改都会将旧的覆盖。
  • oldest_modification
    记录最初一次修改该page的lsn. 它在该page被刷盘之前是不变的。一旦该page被刷盘,它就会被置为0.初次修改一个page时,会同时更新这两个变量,并且把这个page 挂在flush list上。下次redo做checkpoint会根据这个变量的值决定是否刷盘。

因此数据页在刷盘时,newest_modification作为该page的最后lsn存到了磁盘。 xtrabackup 就是根据这个lsn来做增量备份的。

xtrabackup 的增量备份

在Percona官方文档也也说明,增量备份的问题:它需要扫描解析所有的数据页中的内容,读取lsn,并与全备结束的lsn相对比来决定这个page要不要拷贝,这其中需要消耗大量的读IO。为此Percona Server的XtraDB引擎对此进行了优化,使用位图文件跟踪数据页的修改,xtrabackup下次增备时,会读取这一位图文件,定位到哪些page需要拷贝,这大大提高了增备的效率!

An incremental backup copies each page whose LSN is newer than the previous incremental or full backup’s LSN. There are two algorithms in use to find the set of such pages to be copied. The first one, available with all the server types and versions, is to check the page LSN directly by reading all the data pages. The second one, available with Percona Server, is to enable the changed page tracking feature on the server, which will note the pages as they are being changed. This information will be then written out in a compact separate so-called bitmap file. The xtrabackup binary will use that file to read only the data pages it needs for the incremental backup, potentially saving many read requests. The latter algorithm is enabled by default if the xtrabackup binary finds the bitmap file. It is possible to specify xtrabackup –incremental-force-scan to read all the pages even if the bitmap data is available.

Percona server通过一个变量innodb_track_changed_pages来控制是否开启这个功能。开启这个功能后,会产生这样一个位图文件ib_modified_log__.xdb。 其中seq是该文件的序列号,因为这个文件不能无限大, 变量innodb_max_bitmap_file_size控制了它的大小,达到这一设置会产生一个新文件;startlsn是指该文件的记录的起始lsn。 xtrabackup备份时会使用这一信息。Percona server也提供了一些命令来控制相关行为, xtrabackup 在备份时会用这些命令与server交互。 详细参考官方文档

对于位图文件的详细工作原理还不清除,经测试它并没有随修改操作的增加而线性增大。没有读过xtrabackup的代码,猜想它大概是记录了修改page的分布吧?

一个page大小是固定的(16k),可以通过在表空间内的偏移来定位, 这个page变动跟踪大概是将这些page的偏移量记录到了bitmap文件,xtrabackup通过读取这个bitmap文件,快速seek定位到所要拷贝的page在表空间的位置,不用解析page的内容,直接拷贝即可,这样大大减轻了磁盘IO负载,提高了增备的效率!

上述是我对xtrabackup增量备份造成大量IO这一问题的一点分析,以及简单介绍了Percona的优化方案。也许不太正确,欢迎讨论。