如何从 InnoDB 表中删除碎片?

Abd*_*naf 14 mysql innodb mysql-5.5

我有一个包含多个表的数据库。

我想从表中删除一些记录,说记录数超过 20K 或 50K。

所有的表都是 InnoDB。并且file_per_table关闭的

当我从多个表中删除记录时,表中会出现碎片。

有没有办法消除碎片。?

4月17日更新

mysql> select TABLE_NAME, TABLE_SCHEMA, Data_free from information_schema.TABLES where TABLE_SCHEMA NOT IN ('information_schema', 'mysql') and Data_Free >0;
+-----------------+--------------+-----------+
| TABLE_NAME      | TABLE_SCHEMA | Data_free |
+-----------------+--------------+-----------+
| City            | world_innodb |   5242880 |
| City_Copy       | world_innodb |   5242880 |
| Country         | world_innodb |   5242880 |
| CountryLanguage | world_innodb |   5242880 |
| a               | world_innodb |   5242880 |
| t1              | world_innodb |   5242880 |
| t2              | world_innodb |   5242880 |
+-----------------+--------------+-----------+
7 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

所以现在我的问题是我将如何决定我的表是否碎片化。

Rol*_*DBA 16

早在 2010 年 10 月,我就在 StackOverflow 中解决了这个问题

记住 InnoDB 基础设施中最繁忙的文件:/var/lib/mysql/ibdata1

该文件通常包含四种类型的信息

  • 表格数据
  • 表索引
  • MVCC(多版本并发控制)数据
  • 表元数据(表空间 ID 列表)

OPTIMIZE TABLE针对存储在 ibdata1 中的 InnoDB 表运行有两件事:

  • 使表的数据和索引在 ibdata1 内连续,从而更快地访问
  • 它使 ibdata1 增长,因为连续的数据和索引页被附加到 ibdata1

虽然您可以将表数据和表索引从 ibdata1 中分离出来并使用innodb_file_per_table独立管理它们,但 ibdata1 中巨大的整个磁盘空间不会消失并且无法回收。你必须做得更多。

一劳永逸缩小 ibdata1,您必须执行以下操作:

1) MySQLDump 所有数据库到一个 SQL 文本文件中(称之为 /root/SQLData.sql)

2)删除所有数据库(mysql模式除外)

3)关闭mysql

4) 将以下行添加到 /etc/my.cnf

[mysqld]
innodb_file_per_table
innodb_flush_method=O_DIRECT
innodb_log_file_size=1G
innodb_buffer_pool_size=4G
Run Code Online (Sandbox Code Playgroud)

旁注:无论您为 innodb_buffer_pool_size 设置什么,请确保 innodb_log_file_size 是 innodb_buffer_pool_size 的 25%。

5)删除ibdata1、ib_logfile0和ib_logfile1

此时,/var/lib/mysql中应该只有mysql模式

6)重启mysql

这将重新创建 10 或 18MB 的 ibdata1(取决于 MySQL 的版本),ib_logfile0 和 ib_logfile1 各 1G

7) 将 /root/SQLData.sql 重新加载到 mysql

ibdata1 会增长但只包含表元数据。事实上,它会多年来增长非常缓慢。ibdata1 快速增长的唯一方法是,如果您有以下一项或多项:

  • 大量的 DDL ( CREATE TABLE, DROP TABLE, ALTER TABLE)
  • 大量交易
  • 每个事务提交的大量更改

每个 InnoDB 表都将存在于 ibdata1 之外

假设您有一个名为 mydb.mytable 的 InnoDB 表。如果你进入/var/lib/mysql/mydb,你会看到两个代表表的文件

  • mytable.frm(存储引擎标题)
  • mytable.ibd(mydb.mytable 的表数据和表索引的主页)

ibdata1 将不再包含 InnoDB 数据和索引。

使用 /etc/my.cnf 中的 innodb_file_per_table 选项,您可以运行OPTIMIZE TABLE mydb.mytable;并且文件 /var/lib/mysql/mydb/mytable.ibd 实际上会缩小。

在我作为 MySQL DBA 的职业生涯中,我已经多次这样做了

事实上,我第一次这样做时,我将一个 50GB 的 ibdata1 文件压缩成 500MB。

试一试。如果您对此有更多疑问,请给我发电子邮件。相信我。这将在短期和长期内奏效!!!

更新 2012-04-19 09:23 EDT

运行上述步骤后,如何确定哪些表需要进行碎片整理?有可能找出来,但您将编写脚本。

这是一个示例:假设您有 table mydb.mytable。启用 innodb_file_per_table 后,您将拥有文件 /var/lib/mysql/mydb/mytable.ibd

您将必须检索两个数字

FILESIZE FROM OS :您可以像这样从操作系统确定文件大小

ls -l /var/lib/mysql/mydb/mytable.ibd | awk '{print $5}'
Run Code Online (Sandbox Code Playgroud)

FILESIZE FROM INFORMATION_SCHEMA :您可以从 information_schema.tables 中确定文件大小,如下所示:

SELECT (data_length+index_length) tblsize FROM information_schema.tables
WHERE table_schema='mydb' AND table_name='mytable';
Run Code Online (Sandbox Code Playgroud)

只需从 OS 值中减去 INFORMATION_SCHEMA 值,然后将差值除以 INFORMATION_SCHEMA 值。

从那里您将决定认为有必要对该表进行碎片整理的百分比。当然,您可以使用以下命令之一对其进行碎片整理:

OPTIMIZE TABLE mydb.mytable;
Run Code Online (Sandbox Code Playgroud)

或者

ALTER TABLE mydb.mytable ENGINE=InnoDB;
Run Code Online (Sandbox Code Playgroud)


Mah*_*til 5

如果您经常删除行(或更新具有可变长度数据类型的行),最终可能会在数据文件中浪费大量空间,类似于文件系统碎片。

如果您不使用该innodb_file_per_table选项,您唯一能做的就是导出和导入数据库,这是一个耗时且占用大量磁盘空间的过程。

但是,如果您正在使用innodb_file_per_table,则可以识别并回收该空间!

在 5.1.21 之前,可用空间计数器可从 information_schema.tables 的 table_comment 列中获得。下面是一些 SQL 来识别至少有 100M(实际上是 97.65M)可用空间的表:

SELECT table_schema, table_name, table_comment FROM
information_schema.tables WHERE ENGINE LIKE 'InnoDB' AND table_comment RLIKE 'InnoDB free: ([0-9]{6,}).*' ;

从 5.1.21 开始,这被移动到 data_free 列(一个更合适的地方):

SELECT table_schema, table_name, data_free / 1024 / 1024 AS data_free_MB FROM information_schema.tables WHERE ENGINE LIKE 'InnoDB' AND data_free > 100 * 1024 * 1024 ;

您可以通过重建表来回收丢失的空间。最好的方法是使用“alter table”而不实际更改任何内容:

ALTER TABLE `TableName` ENGINE=InnoDB;
Run Code Online (Sandbox Code Playgroud)

如果您在 InnoDB 表上运行“优化表”,这就是 MySQL 在幕后所做的事情。它会导致一个读锁,但不是一个全表锁。需要多长时间完全取决于表中的数据量(而不是数据文件的大小)。如果您有一个包含大量删除或更新的表,您可能希望每月甚至每周运行一次。