MySQL 如何整理表空间碎片

MySQL
前言

MySQL 在进行大量的 insert,update,delete 之后,表可能出现碎片,从表面上来看,我们可以会发现数据库占用的空间要比我们真实的数据大小要大很多,从性能的角度来说,表空间碎片会影响数据库性能,这是因为 当 MySQL 进行数据扫描时,扫描的大小是表的容量需求上限,也就是数据被写入的区域中处于峰值的部分。

对于 delete 操作来说,删除数据时只是在数据行打上 delete Mark 的标签,当下次进行插入时,这部分空间可以重用,但是如果某个空白空间一直没有被大小合适的数据占用,仍然无法将其彻底占用,就形成了碎片;

如何查看表空间碎片

1.使用 show table status 的命令进行查看:

mysql> show table status like 'sbtest4'\G*************************** 1. row ***************************
           Name: sbtest4
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 826772
 Avg_row_length: 1850
    Data_length: 1529921536
Max_data_length: 0
   Index_length: 0
      Data_free: 14680064
 Auto_increment: 10850668
    Create_time: 2022-03-15 15:28:30
    Update_time: 2022-03-15 16:05:27
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 
1 row in set (0.02 sec)

其中 data free 就表示表空间碎片

2.查看表空间的碎片比率

mysql> SELECT ENGINE,
concat( TABLE_SCHEMA, '.', TABLE_NAME ) AS table_name,
round( DATA_LENGTH / 1024 / 1024, 2 ) AS data_length,
round( INDEX_LENGTH / 1024 / 1024, 2 ) AS index_length,
round( DATA_FREE / 1024 / 1024, 2 ) AS data_free,
(data_free /(index_length + data_length )) AS frag_ratio 
FROM
	information_schema.TABLES 
WHERE
	DATA_FREE > 0 
ORDER BY
	frag_ratio ASC 
	LIMIT 2;
+--------+--------------------+-------------+--------------+-----------+------------+
| ENGINE | table_name         | data_length | index_length | data_free | frag_ratio |
+--------+--------------------+-------------+--------------+-----------+------------+
| InnoDB | insert_test.t_user |     2573.00 |      2243.92 |      7.00 |     0.0015 |
| InnoDB | sbtest.sbtest4     |     1459.05 |         0.00 |     14.00 |     0.0096 |
+--------+--------------------+-------------+--------------+-----------+------------+
2 rows in set (0.05 sec)
如何消除表空间碎片

可以使用 alter table 或 OPTIMIZE TABLE 命令来进行表空间碎片的整理,从官方文档上来看,执行表空间碎片的整理有如下好处[1]:

OPTIMIZE TABLE reorganizes the physical storage of table data and associated index data, to reduce storage space and improve I/O efficiency when accessing the table

mysql> optimize table sbtest.sbtest4;
+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| sbtest.sbtest4 | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| sbtest.sbtest4 | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (1.68 sec)

mysql> alter table sbtest.sbtest4 engine = innodb;
Query OK, 0 rows affected (1.64 sec)
Records: 0  Duplicates: 0  Warnings: 0

2.使用存储过程批量进行表碎片清理

DELIMITER $$

CREATE PROCEDURE `optimize_tables_proc`(
 IN db_name varchar(255))
BEGIN
  declare cnt int default 0;
  declare i int default 0;
  select count(*) as total from information_schema.tables where table_schema = db_name into cnt;
  while i < cnt
  do
    set @stmt = concat('select table_name from information_schema.tables where table_schema = ''',db_name,''' order by table_name asc limit ',i,',1 into @tb_name');
    prepare s1 from @stmt;
    execute s1;
    drop prepare s1;
    set @stmt = '';
    set @stmt = concat('optimize table ',db_name,'.',@tb_name);
    prepare s1 from @stmt;
    execute s1;
    drop prepare s1;
    set @stmt = '';
    set i = i + 1;
  end while;
  flush tables;
END$$

DELIMITER ;

调用示例如下:sbtest为数据库的名称

mysql> call optimize_tables_proc('sbtest');
+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| sbtest.sbtest1 | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| sbtest.sbtest1 | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (0.04 sec)

+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| sbtest.sbtest2 | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| sbtest.sbtest2 | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (0.70 sec)

3.使用 mysqlcheck 原生工具进行表空间优化

(base) [root@ip-10-0-0-22 ~]# mysqlcheck -h111.62.xxx.xxx -urudonx -pxxxx -o sbtest
mysqlcheck: [Warning] Using a password on the command line interface can be insecure.
sbtest.sbtest1
note     : Table does not support optimize, doing recreate + analyze instead
status   : OK
sbtest.sbtest2
note     : Table does not support optimize, doing recreate + analyze instead
status   : OK

上述的案例相当于对整个数据库做表空间优化,还可以对某张表做表空间优化,可以使用如下命令:

mysqlcheck -o sbtest sbtest1

注意:如上整理表空间的命令,建议您在数据库可维护的时间窗口内执行,避免对业务造成影响。

参考文档

[1] https://dev.mysql.com/doc/refman/5.7/en/optimize-table.html

如果您有其他问题,欢迎您联系火山引擎技术支持服务

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论