表上的 MySQL 索引创建失败已满

Noa*_*oam 11 mysql innodb index

更新:tl;dr:问题是 MySQLTMPDIR在创建索引时使用。而我TMPDIR的磁盘空间不足。

原问题:

我正在尝试向 InnoDB 表添加索引,并获得table is full error. 我有足够的磁盘空间,并且 MySQL 配置有一个 file-per-table=1。表数据为 85GB,我假设索引将在 20GB - 30GB 左右,而且我的磁盘空间比这多得多。我也在使用 ext3,所以从操作系统的角度来看,我认为文件大小限制没有任何问题。

记录的错误如下所示:

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full
Run Code Online (Sandbox Code Playgroud)

这是什么原因造成的,我该如何解决?

创建表:

`CREATE TABLE `my_table` (
 `uid_from` bigint(11) NOT NULL,
 `uid_to` bigint(11) NOT NULL,
 `counter` int(11) NOT NULL,
 `updated` date NOT NULL,
 PRIMARY KEY (`uid_to`,`uid_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8`
Run Code Online (Sandbox Code Playgroud)

数据是 87.4GB,我估计大约有 1.5B 行。

SHOW GLOBAL VARIABLES LIKE 'tmpdir';
Variable_name   Value
tmpdir  /tmp

[root@web ~]# df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   24G   14G  64% /
Run Code Online (Sandbox Code Playgroud)

Rol*_*DBA 9

请不要被错误信息所迷惑The table 'my_table' is full。这种罕见的情况与磁盘空间完全无关。这个表已满的情况与 InnoDB 的内部管道有关。

首先看一下这张InnoDB架构图

InnoDB 架构

请注意,系统表空间(文件 ibdata)只有128 个回滚段和每个回滚段 1023 个回滚槽。这限制了事务回滚容量的大小。换句话说,如果单个回滚段需要超过 1023 个插槽来支持事务,则事务将达到该table is full条件。

想想新泽西的 Red Lobster 餐厅。它可能有200人的容量。如果餐厅客满,可能会有一排人到外面等候。如果线上的人不耐烦,他们可能会因为餐厅客满而离开。显然,解决方案不是让新泽西更大(或获得更多磁盘空间)。解决方案是将红龙虾餐厅做得更大。这样,您可以将座位容量增加到 240 人。即便如此,如果超过 240 人决定来 Red Lobster,则外面可能会排成一行。

举个例子,我有一个有 2TB 系统表空间的客户端,并且innodb_file_per_table被禁用。(ibdata1 为 346G,ibdata2 为重置)。我运行了这个查询

SELECT SUM(data_length+index+length) InnoDBDataIndexSpace
FROM information_schema.tables WHERE engine='InnoDB';
Run Code Online (Sandbox Code Playgroud)

接下来,我从 ibdata1 和 ibdata2 的文件大小总和中减去 InnoDBDataIndexSpace。有两件事让我震惊

  • 系统表空间中剩余 106GB。
  • 我得到了同样的Table is Full条件

这意味着 106GB 用于 InnoDB 的内部管道。客户当时正在使用 ext3。

我的解决方案是添加 ibdata3。我在我的旧帖子中讨论了这个问题如何用“innodb_file_per_table”解决“表......已满”?

我在其他帖子中也讨论过这个问题

请记住,即使启用了innodb_file_per_table,这种情况也可能发生。如何?回滚和撤消日志是 ibdata1 增长不受控制的峰值的来源

您的实际问题

由于您正在向表中添加索引并获取Table is Full,因此该表必须很大并且无法容纳在单个回滚段内。您必须执行以下操作:

步骤 01

将数据放入转储文件

mysqldump --no-create-info mydb mytable > table_data.sql
Run Code Online (Sandbox Code Playgroud)

步骤 02

登录到 MySQL 并运行这个

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;
Run Code Online (Sandbox Code Playgroud)

步骤 03

加载带有附加索引的表和数据

mysql -Dmydb < table_data.sql
Run Code Online (Sandbox Code Playgroud)

就这样。

看,问题在于 ALTER TABLE 将尝试将大表中的所有行作为单个事务注入。使用 mysqldump 将一次将数据插入表(现在有一个新索引)数千行,而不是单个事务中的所有行。

不用担心what if this doesn't work?原始表会被命名mytable_old以防万一。它可以作为备份。当您知道新表适合您时,您可以删除备份。

试一试 !!!

更新 2014-06-16 11:13 EDT

如果您担心数据的转储更大,只需 gzip 即可。

您可以执行相同的步骤,但如下所示

步骤 01

将数据放入转储文件

mysqldump --no-create-info mydb mytable | gzip > table_data.sql.gz
Run Code Online (Sandbox Code Playgroud)

步骤 02

登录到 MySQL 并运行这个

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;
Run Code Online (Sandbox Code Playgroud)

步骤 03

加载带有附加索引的表和数据

gzip -d < table_data.sql.gz | mysql -Dmydb
Run Code Online (Sandbox Code Playgroud)

或者

gunzip < table_data.sql.gz | mysql -Dmydb
Run Code Online (Sandbox Code Playgroud)

更新 2014-06-16 12:55 EDT

关于这个问题,我只是想到了另一个方面。

由于您正在执行 DDL 而不是 DML,因此这可能不是 InnoDB 内部管道。由于DDL 无法回滚 InnoDB,问题必须是外部管道。这个外部管道在哪里堵塞?我怀疑操作系统的临时文件夹。为什么?

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full
Run Code Online (Sandbox Code Playgroud)

看到了disk quota exceeded吗?这个磁盘配额在哪里被强加?

运行此查询

SHOW GLOBAL VARIABLES LIKE 'tmpdir';
Run Code Online (Sandbox Code Playgroud)

你说这是 /var/lib/mysqltmp

现在,在操作系统中运行它

df -h /var/lib/mysqltmp
Run Code Online (Sandbox Code Playgroud)

有东西告诉我空间/var/lib/mysqltmp快用完了 毕竟你只有14G空闲。在 DDL 的眼中(创建索引),发生了回滚,不是在 ibdata1 文件中,而是在tmpdir位置。如果/var/lib/mysqltmp没有安装在任何地方,则临时数据正在写入根分区。如果/var/lib/mysqltmp安装在某处,则该安装正在填充行数据。无论哪种情况,都没有足够的空间来完成 DDL。

你在这里有两个选择

选项1

您还可以创建一个大磁盘(可能有 100+GB)并安装/var/lib/mysqltmp在该大磁盘上。

选项#2

即使您拥有的磁盘空间有限,我的 3 步建议仍然有效

评论

很遗憾您发布的错误消息说

InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.
Run Code Online (Sandbox Code Playgroud)

这意味着操作系统很好。对于这种情况,也不存在任何相关的错误编号。

还有一件事你应该知道。MySQL 文档说 InnoDB 的快速索引创建仍然转到磁盘

在索引创建期间,文件被写入临时目录(Unix 上的 $TMPDIR、Windows 上的 %TEMP% 或 --tmpdir 配置变量的值)。每个临时文件都足够大,可以容纳构成新索引的一列,一旦合并到最终索引中,就会立即删除每一列。

由于 MySQL 的限制,当您在 TEMPORARY TABLE 上创建索引时,会复制表,而不是使用“快速索引创建”。这已被报告为MySQL Bug #39833

更新 2014-06-16 13:58 EDT

[mysqld]
datadir                         = /mnt/cbsvolume1/var/lib/mysql
tmpdir                          = /mnt/cbsvolume1/var/lib/mysql
Run Code Online (Sandbox Code Playgroud)

请确保/mnt/cbsvolume1/var/lib/mysql有100G或更多免费


归档时间:

查看次数:

17951 次

最近记录:

8 年,6 月 前