如何从InnoDB表中删除碎片?


13

我有一个具有表数量的数据库。

我想从表中删除一些记录,说记录数超过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)

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



1
还有一篇InnoDB文章:照顾 Percona博客网站中的碎片
ypercubeᵀᴹ

Answers:


14

我已经在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)MySQL将所有数据库转储到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

旁注:无论您为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(取决于MySQL的版本)重新创建ibdata1,分别以1G重新创建ib_logfile0和ib_logfile1

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

ibdata1将增长,但仅包含表元数据。实际上,随着时间的流逝,它会非常缓慢地增长。ibdata1快速增长的唯一方法是具有以下一项或多项:

  • 很多DDL的(CREATE TABLEDROP TABLEALTER 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

您将必须检索两个数字

来自OS的文件大小:您可以像这样从OS确定文件大小

ls -l /var/lib/mysql/mydb/mytable.ibd | awk '{print $5}'

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';

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

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

OPTIMIZE TABLE mydb.mytable;

要么

ALTER TABLE mydb.mytable ENGINE=InnoDB;

我不认为在/ var / lib中/ MySQL的/ ibdata1中是非常繁忙的,如果你使用的是推荐innodb_file_per_table = 1选项
CrackerJack9

1
@ CrackerJack9 ibdata1由于其中的原因而变得异常繁忙:1)双重写入缓冲区信息,2)为辅助索引插入缓冲区,3)数据字典,4)回滚段,5)撤消表空间。请转到scribd.com/doc/31337494/XtraDB-InnoDB-internals-in-drawing以图形方式表示这些内容。即使删除了InnoDB表的数据和索引页面,ibdata1在高事务环境中仍然可以显着增长。
RolandoMySQLDBA

1
@ CrackerJack9我还有一篇文章讨论ibdata1周围的其他活动:dba.stackexchange.com/a/23367/877
RolandoMySQLDBA 2013年

我没有意识到它仍然被大量使用。非常感激!
CrackerJack9

@RolandoMySQLDBA有空时可以弹出堆吗?
ypercubeᵀᴹ

5

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

如果不使用该innodb_file_per_table选项,则唯一可以做的就是导出和导入数据库,这是一个费时费力的过程。

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

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


information_schema.tables中选择table_schema,table_name,table_comment,在其中像'InnoDB'和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其中的引擎类似于'InnoDB'并且data_free> 100 * 1024 * 1024;

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

ALTER TABLE `TableName` ENGINE=InnoDB;

如果在InnoDB表上运行“优化表”,这就是MySQL的幕后工作。这将导致读取锁定,但不会导致完整表锁定。花费的时间完全取决于表中的数据量(而不取决于数据文件的大小)。如果您的表中有大量的删除或更新,则可能需要每月甚至每周运行一次。


还有一件事,我无法理解data_free> 100 * 1024 * 1024 ...的含义。当我看到结果时,我无法确定该表是否碎片化。.?有什么办法让我可以说表是零散的还是不零散的?
Abdul Manaf 2012年

看看我的更新部分。
Abdul Manaf 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.