从 InnoDB 表中删除和回收空间

Noa*_*oam 24 mysql innodb

我有一个 700GB 的 InnoDB 表,我不再向其中写入任何数据(仅读取)。我想删除它保存的旧数据并回收该磁盘空间(因为我用完了)。删除部分非常简单,因为我有一个 auto-inc 主索引,所以我可以使用它在块中进行迭代,并删除行,但这不会让我回到空间。我认为OPTIMIZE TABLE会,但在 700GB 的表上可能需要永远,那么我忽略了另一个选择吗?

由 RolandoMySQLDBA 编辑

假设您的表是mydb.mytable,请运行以下查询并将其发布到此处,以便您可以确定表收缩所需的磁盘空间:

SELECT
    FORMAT(dat/POWER(1024,3),2) datsize,
    FORMAT(ndx/POWER(1024,3),2) ndxsize,
    FORMAT((dat+ndx)/POWER(1024,3),2) tblsize
FROM (SELECT data_length dat,index_length ndx
FROM information_schema.tables WHERE
table_schema='mydb' AND table_name='mytable') A;
Run Code Online (Sandbox Code Playgroud)

如果允许,我们还需要查看表结构。

诺姆编辑

这是查询的输出:

数据大小 ndxsize tblsize
682.51 47.57 730.08

这是表结构 ( SHOW CREATE TABLE)

`CREATE TABLE `mybigtable` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) NOT NULL,  
  `created_at` datetime NOT NULL,  
  `tid` bigint(20) NOT NULL,  
  `text` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL, 
  `ft` tinyint(1) NOT NULL,  
  `irtsd` bigint(20) NOT NULL,  
  `irtuid` int(11) NOT NULL,  
  `rc` int(11) NOT NULL,  
  `r` tinyint(1) NOT NULL,  
  `e` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,  `timezone` varchar(5) NOT NULL,  PRIMARY KEY (`id`),  UNIQUE KEY `uid_tid` (`uid`,`tid`)) ENGINE=InnoDB AUTO_INCREMENT=2006963844 DEFAULT CHARSET=utf8`
Run Code Online (Sandbox Code Playgroud)

Max*_*eul 43

这是一个很好的问题。你有几个解决方案,但你的桌子很大,所以没有一个是没有痛苦的:)

您有三种“缩小” InnoDB 表的解决方案:

1.优化表

您可以OPTIMIZE TABLE按照您提到的方式使用它,但您应该关心innodb_file_per_table变量:

mysql> show variables like "innodb_file_per_table";
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | ON    |
+-----------------------+-------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

让我解释:

OPTIMIZE TABLE与InnoDB表,锁表,复制一个新的干净表中的数据(这就是为什么结果是更小的),丢弃原始表,并与原来的名称重命名新表。这就是为什么您应该确保磁盘上可用空间是原始表的两倍(您可能需要更少的空间,因为优化后的表会更小,但安全总比后悔好)。

innodb_file_per_table = ON:在这种模式下,所有的表都有自己的数据文件。OPTIMIZE然后该语句将创建一个优化空间使用的新数据文件。操作完成后,MySQL 将删除原始版本并替换为优化版本(因此最终会释放操作期间生成的 700GB——可能更少,因为它会被优化——)

innodb_file_per_table = OFF在这种模式下,所有数据都包含在一个数据文件中:ibdata。这种模式有一个很大的缺点,因为它不能被优化。因此,在此OPTIMIZE过程中,您的新表将被创建(接近 700GB),但即使在删除和重命名操作(以及OPTIMIZE阶段结束)之后,您的ibdata也不会释放 ~700GB,因此您想释放一些数据,而不是您还有 700GB,很酷不是吗?

2. 更改表

您还可以使用ALTER TABLE语句,其ALTER TABLE工作方式与OPTIMIZE TABLE. 你可以只使用:

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

3. 更改表(在线)

的问题OPTIMIZE,并ALTER TABLE是,其在运行过程中锁定表。您可以使用 Percona 工具:pt-online-schema-change(来自 Percona Toolkit:link )。pt-online-schema... 提供优化表的机制,同时保持原始表可用于读写。我在生产中使用这个工具来ALTER处理大表上的语句,它非常酷。

请注意,任何FOREIGN KEY引用您的表的 s都可能使事情复杂化,因为锁定可能会导致锁定其他表等等。要检查这一点,只需查询:

mysql> SELECT COUNT(*) FROM information_schema.REFERENTIAL_CONSTRAINTS WHERE REFERENCED_TABLE_NAME = "myTable";
+----------+
| COUNT(*) |
+----------+
|        0 |
+----------+
1 row in set (0.04 sec)
Run Code Online (Sandbox Code Playgroud)

这是我如何使用 pt-online-schema-change:

pt-online-schema-change --alter "ENGINE=InnoDB" D=myBase,t=myTable --user --ask-pass
Run Code Online (Sandbox Code Playgroud)

请注意,我的注释innodb_file_per_table对于此解决方案也是如此。

4. mysqldump

最后一个解决方案是从转储中重新创建所有数据库。它需要永远,但它非常有效。请注意,这是优化您的 ibdata 文件的唯一解决方案,如果innodb_file_per_tableOFF

最大限度。