在表上创建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.