暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL--Sorted Index Builds 导致备份失败故障分析

原创 进击的CJR 2022-09-13
1347

问题现象

xtrabackup备份失败,日志中有这样的信息

InnoDB: An optimized (without redo logging) DDLoperation has been performed. All modified pages may not have been flushed to the disk yet.

问题定位与分析

备份失败原因分析

MySQL 5.7 redo logs会跳过一些DDL,Percona XtraBackup 监测到redo log 有跳过时,它会中止备份以防止创建一个损坏的备份,所以上面的问题也就出现了。

问题根源Sorted Index Builds

官方解释了,MySQL 5.7 redo log会跳过一些DDL的这个功能是一个新的功能,这个新功能叫"Sorted Index Builds",具体解释就是,索引创建的时候分成三个阶段:

There are three phases to an index build. In the first phase, the clustered index is scanned, and index entries are generated and added to the sort buffer. When the sort buffer becomes full, entries are sorted and written out to a temporary intermediate file. This process is also known as a “run”. In the second phase, with one or more runs written to the temporary intermediate file, a merge sort is performed on all entries in the file. In the third and final phase, the sorted entries are inserted into the B-tree.

(1)第一阶段,扫描clustered index,生成索引项并添加到sort buffer。当sort buffer满时,索引项将被排序并写入临时文件。这个过程也被称为 “run”;

(2)第二阶段,一个或多个线程写入临时文件或对sort buffer的索引项进行合并排序;

(3)第三阶段,排序完后的索引项插入B-tree。

那为什么还引入Sort Index Builds?

老版本创建索引方式

其实就是5.5版本之前,创建索引是表复制的方式。当需要创建索引时,MySQL会根据创建的索引,按照所需的表结构新建一个空表,然后锁定源表,将数据一行一行地插入到空表中,同时更新维护空表上的索引。当数据全部复制完毕,索引也就建成了,最后源表被删除,而新建的表则更名为源表的名字。
这种方式弊端非常多,不但需要复制全表数据,十分耗时,而且在复制数据期间,源表是被锁定的状态,排斥任何读写操作。

Sorted Index Builds优势

在引入Sorted Index Builds之前,创建索引的过程是一遍扫描源表,一遍将得到的数据插入到新建的空表中,也就是边扫描边插入B树索引中。这种方式由于索引键的插入是乱序的,所以在插入的过程中需要不断地寻找和确认插入的位置,期间必然伴随着索引页的分裂和合并。
而Sorted Index Builds的方式,由于事先已经将索引条目排序,插入B树索引时,只需要顺序地往索引页里插入数据,再索引页写满时,再向右分配一个索引页即可,整个索引创建过程中,都是在写入最右的叶子节点,无需去寻找确认插入的位置,也不会引发索引页的分裂合并。

遗留问题

官当在“Sorted Index Builds and Redo Logging”这里提到
在使用Sorted Index方式创建索引的时候会关闭掉Redo Logging,他是通过检查点的方式来保证创建索引的高可用性。检查点强制将所有脏页写入磁盘,在索引创建期间, page cleaner会定期的刷新脏页以保证检查点的快速前推。通常情况下,只有当干净页面的数量低于设置的阈值时,page cleaner才回去刷新脏页,但是在创建索引的过程中, page cleaner会快速刷新,来减少检查点的开销。

也正因为这个创建索引的时候关闭了Redo log,所以才会出现我们备份的问题。

问题处理

为了避免该问题官方提供的解决方案就是加上–lock-ddl

实验测试

准备压力测试脚本

sysbench /usr/share/sysbench/oltp_read_only.lua --db-driver=mysql --mysql-host=localhost --mysql-port=3306 --mysql-socket=/appdata/mysql/mysql.sock --mysql-user=root --mysql-password="paic1234A?" --mysql-db=db1  --table-size=2000000  prepare


sysbench /usr/share/sysbench/oltp_read_only.lua --db-driver=mysql --mysql-host=localhost --mysql-port=3306 --mysql-socket=/appdata/mysql/mysql.sock --mysql-user=root --mysql-password="paic1234A?" --mysql-db=db2 --table-size=2000000  --threads=50 prepare

准备DDL脚本

vim table1.sql
use db1 ;drop table if exists db1.sb1;
use db1 ;create table sb1 like select id,c from sbtest1 where id < 150000;
use db1 ;create unique index ix on sb1 (id);

vim table2.sql
use db2 ;drop table if exists db2.sb1;
use db2 ;create table sb1 as select id,c from sbtest1 where id < 150000;
use db2;create unique index ix on sb1 (id);

测试脚本
vim test.sh
#!/bin/bash
mysql -uroot -p"paic1234A?" < table.sql
sleep 1
mysql -uroot -p"paic1234A?" <  table1.sql

调用DDL

while true; do sh  test.sh; done

发起备份脚本

xtrabackup --defaults-file=/etc/my.cnf \
--user=root --password='paic1234A?'  --target-dir=/appdata/backup/  \
--backup --host=127.0.0.1 --port=3306 --binlog-info=AUTO  --parallel 4 \
--check-privileges --no-version-check --lock-ddl

备份成功

根据官方说明–lock-ddl只适用于Percona Server的MySQL服务器,如果像MariaDB是不适用的。产生的报错如下:
LOCK TABLES FOR BACKUP is not supported.

所以官方更推荐使用–lock-ddl-per-table,这个参数使用任何server。

问题总结

Sorted Index Builds不会写redo记录,而是用一个检查点来将脏页刷到磁盘。而页清理线程会及时地将索引脏页刷到磁盘,以减少执行检查点所花时间和开销。
同时也是由于创建索引不记录redo,所以在xtrabackup备份期间,如果执行了创建索引操作,备份将会终止,因为xtrabackup无法从redo中获取创建索引的操作,在利用备份apply-log时也就无法重现创建索引的操作。
所以备份的时候加上–lock-ddl-per-table就能够解决相关问题了。

最后修改时间:2022-09-17 09:41:09
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论