在表上创建MySQL索引失败


10

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

原始问:

我正在尝试向InnoDB表添加索引,并获得一个table is full error。我有足够的磁盘空间,MySQL配置的文件表数量为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

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

创建表:

`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`

数据为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% /

1
“ InnoDB:检查您的操作系统和文件系统是否支持该大小的文件。InnoDB:还检查磁盘未满或磁盘配额超过。” -你检查了吗?您正在使用ext2吗?ext3?XFS?检查了用户的配额?
Philᵀᴹ

@Phil都选中了。我正在使用ext3,几乎可以肯定,磁盘空间没有问题。Hunch是与达到他的极限的日志有关的东西,但实际上却不知道如何验证和修复。
Noam 2014年

我想知道“(合并)”是什么文件?
akuzminsky 2014年

@akuzminsky嗯,我不知道。我仅从PHPMyAdmin运行了新的索引创建。
Noam 2014年

为TMPDIR +1,这也是我服务器上的问题。
乍得E.19年

Answers:


8

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

首先,看一下这个InnoDB体系结构图

InnoDB架构

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

想想新泽西州的Red Lobster餐厅。它可以容纳200人。如果餐厅满员,可能会有很多人在外面等。如果排队的人不耐烦,他们可能会因为餐厅已满而离开。显然,解决方案不是扩大新泽西州(或增加磁盘空间)。解决方案是使Red Lobster餐厅更大。这样,您可以将座位数增加到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';

接下来,我从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

步骤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;

步骤03

用数据的附加索引加载表

mysql -Dmydb < table_data.sql

就这样。

看到的问题是,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

步骤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;

步骤03

用数据的附加索引加载表

gzip -d < table_data.sql.gz | mysql -Dmydb

要么

gunzip < table_data.sql.gz | mysql -Dmydb

更新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

看到了disk quota exceeded吗?该磁盘配额在哪里实施?

运行此查询

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

你说是 /var/lib/mysqltmp

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

df -h /var/lib/mysqltmp

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

您有两个选择

选项1

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

选项#2

即使您的磁盘空间有限,我的三步建议也应该仍然有效

评论

您发布的错误消息说的可惜

InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.

这意味着操作系统很好。在这种情况下,也没有任何关联的错误号。

您还应该知道另一件事。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

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


从转储(85GB数据)开始,我花了数周的时间才开始构建表。除了重新执行此操作之外,还有其他方法吗?您是否建议对表进行分区?
Noam

分区将无济于事,因为添加索引的ALTER TABLE仍会尝试在单个事务中加载所有内容。请记住,您在与InnoDB体系结构而不是磁盘空间进行斗争。在这种情况下,我的答案应该对您有帮助,因为mysqldump每次插入都会插入几千行,而不是在临时表中插入所有内容,否则可能会回滚。
RolandoMySQLDBA 2014年

但是(再次)创建此表将花费很多时间。在禁用回滚选项的同时可以运行创建索引的任何方法吗?
Noam 2014年

创建索引是DDL而不是DML。您不能更改DDL行为。这就是为什么我的帖子使用DML技术。
RolandoMySQLDBA 2014年

顺便说一句,表有多少行?
RolandoMySQLDBA 2014年
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.