mysql的磁盘碎片清理功能

mysql的磁盘碎片清理以及进度查看

Posted by Qiyibaba on March 19, 2020

mysql的磁盘碎片清理功能

command optimize alter table ENGINE = InnoDB , ALGORITHM=INPLACE, LOCK=NONE
  官方文档 官方文档
Engine InnoDB、 MyISAM or ARCHIVE
does not work with views.
is supported for partitioned tables
InnoDB
InnoDB 要求有 .ibd file文件  
  要求innodb_file_per_table =1 要求innodb-file-per-table=1 并且select @@innodb_file_per_table; 结果也为1
  requires SELECT and INSERT privileges use online DDL,concurrent DML is supported
  有全文索引的表, innodb_optimize_fulltext_only=1 有全文索引的表,会对表上X锁,一直到整理全表结束
  • optimize和alter table都只有fulltext的限制条件,经过实验,innodb_file_per_table = 0也是支持online DDL的。

  • 碎片整理命令,无论是optimize还是alter table ENGINE = InnoDB , ALGORITHM=INPLACE, LOCK=NONE均会同步到备机,只有 optimize NO_WRITE_TO_BINLOG table xxx 才不会同步

  • 备机binlog示例:

    #191230 15:15:33 server id 16782863  end_log_pos 1901 CRC32 0x8a3d0255  GTID    last_committed=8        sequence_number=9       rbr_only=no SET @@SESSION.GTID_NEXT= 'e108f58d-0cd8-11ea-bd66-287b09c271a4:269469'/*!*/; # at 1901 #191230 15:15:33 server id 16782863  end_log_pos 1997 CRC32 0x4718a59d  Query   thread_id=2     exec_time=26    error_code=0 use `test`/*!*/; SET TIMESTAMP=1577690133/*!*/; optimize table t2 /*!*/; # at 1997 #191230 15:18:39 server id 16782863  end_log_pos 2062 CRC32 0x65a9a789  GTID    last_committed=9        sequence_number=10      rbr_only=no SET @@SESSION.GTID_NEXT= 'e108f58d-0cd8-11ea-bd66-287b09c271a4:269470'/*!*/; # at 2062 #191230 15:18:39 server id 16782863  end_log_pos 2201 CRC32 0x028fc877  Query   thread_id=2     exec_time=22    error_code=0 SET TIMESTAMP=1577690319/*!*/; alter table t2 engine=innodb  , ALGORITHM=INPLACE, LOCK=NONE /*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
    
  • 对于innodb引擎来说optimize 是ALTER TABLE … FORCE的别名

  • 有全文索引,alter table t2 engine=innodb , ALGORITHM=INPLACE, LOCK=NONE会报错,但optimize table t2不会

    mysql> alter table t2 engine=innodb  , ALGORITHM=INPLACE, LOCK=NONE;   
    ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: InnoDB presently supports one FULLTEXT index creation at a time. Try ALGORITHM=COPY.
    -- 只允许  alter table t2 engine=innodb,ALGORITHM=COPY;这种方式执行
    -- optimize 在这中情况下,仅仅会阻塞DML操作,不会报错
    
  • 推荐使用alter table t2 engine=innodb , ALGORITHM=INPLACE, LOCK=NONE方式,因为DB会自己报错

执行阶段加的锁是SHARED_UPGRADABLE,该阶段允许并行读写;Prepare和Commit阶段,加的锁是EXCLUSIVE,这两个阶段不能并行DML,Prepare和Commit阶段的耗时很短,占整个DDL流程比例非常小,对业务影响可以忽略不计。MDL_SHARED_UPGRADABLE之间是互斥的,所以可以保证同一张表不会并行执行多个DDL。

监控Online DDL进度

Mysql5.7提供了监控DDL进度的功能,可以通过该功能实时查看DDL执行进展。

开启统计DDL进度功能

UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'stage/sql/altering table';
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'stage/%';
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_stages%';
UPDATE performance_schema.setup_timers SET TIMER_NAME = 'MICROSECOND' WHERE NAME = 'stage';
UPDATE performance_schema.setup_instruments SET ENABLED='YES' WHERE NAME='stage/sql/copy to tmp table';
UPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME LIKE 'events_stages_%';

查看DDL进度

SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED,(WORK_COMPLETED/WORK_ESTIMATED)*100 as COMPLETED FROM performance_schema.events_stages_current;

进度信息说明:

字段名 字段说明
EVENT_NAME 记录DDL当前执行阶段的名称,Online DDL包括以下阶段:read PK and internal sortmerge sortinsertflushlog apply table
WORK_ESTIMATED 该阶段预期工作单元总数
WORK_COMPLETED 该阶段已完成工作单元数
COMPLETED 通过WORK_COMPLETED/WORK_ESTIMATED计算完成百分比

案例

mysql> alter table sbtest1 ENGINE = InnoDB , ALGORITHM=INPLACE, LOCK=NONE;
Query OK, 0 rows affected (1 min 5.58 sec)
Records: 0  Duplicates: 0  Warnings: 0
过程检测脚本
event="begin"
etime=0
otime=0
c="mysql -uroot -pdb10\$ZTE"
while true
do
        line=`$c -e "SELECT concat(EVENT_NAME,'|',(WORK_COMPLETED/WORK_ESTIMATED)*100) FROM performance_schema.events_stages_current;" 2>/dev/null | sed '1d'`
        tevent=`echo $line | awk -F'|' '{print $1}'`
        tproc=`echo $line | awk -F'|' '{print $2}'`

        #if [ "x$tevent" == "x$event" ];then
                #ecost=`echo "$ecost+0.01"|bc`
        #else
        if [ "x$tevent" != "x$event" ];then
                if [ "x$event" == "xbegin" -a "x$tevent" == "x" ];then
                        continue
                elif [ "x$event" != "xbegin" -a "x$tevent" == "x" ];then
                        echo "$event total cost $ecost s"
                        exit
                else
                        #echo "$event total cost $ecost s"
                        etime=`date +%s`
                        if [ "x$event" != "xbegin" ];then
                                echo "$event total cost `echo "$etime-$otime"|bc` s"
                        fi
                        otime=$etime
                        event=$tevent
                        ecost=0
                fi
        fi
        sleep 0.1
done
结果输出
[ltdb1@db02 ~]$ ./moniddl.sh
stage/innodb/alter table (read PK and internal sort) total cost 43 s
stage/innodb/alter table (merge sort) total cost 3 s
stage/innodb/alter table (insert) total cost 2 s
stage/innodb/alter table (flush) total cost 30 s
stage/innodb/alter table (log apply table) total cost 0 s

附:碎片率超过50%的表检查

SELECT
	TABLE_SCHEMA AS `db`,
	TABLE_NAME AS `tbl`,
	1- ( TABLE_ROWS * AVG_ROW_LENGTH ) / ( DATA_LENGTH + INDEX_LENGTH + DATA_FREE ) AS `fragment_pct` 
FROM
	information_schema.TABLES 
WHERE
	TABLE_SCHEMA NOT IN ( 'information_schema', 'mysql', 'performance_schema', 'sys' ) 
	AND ( 1- ( TABLE_ROWS * AVG_ROW_LENGTH ) / ( DATA_LENGTH + INDEX_LENGTH + DATA_FREE ) ) > 0.5 
	AND ( DATA_LENGTH + INDEX_LENGTH + DATA_FREE ) > 1024 * 1024 * 1024;

附:碎片清理需要磁盘空间

由于碎片整理采用的是copy table的方式,所以会另外占用磁盘空间,需要在执行之前对主备机进行磁盘空间检测,确保磁盘空间足够,需要磁盘空间分2块: 1.与原表ibd文件大小一样的空间 2.innodb_online_alter_log_max_size配置大小的空间,默认128M,用来存放执行过程中的DML,如果业务并发很大,需修改该配置的大小,支持动态修改。