我上周将一个项目的所有MySQL表从MyISAM迁移到InnoDB,以支持交易。我使用了alter table
的命令。
大多数工作正常,但是一个特定的查询运行速度非常慢,并且总是会出现错误Incorrect key file for table '/tmp/#sql_xxxx_x.MYI
后来我将问题缩小到2个表的内连接,即user
表和agreement
表。并且内连接发生在user
的外键字段(即agreement_id
)和agreement
的主键字段(即id
)之间。
user
表只有50,000行数据,agreement
表有一行。我们已为用户agreement_id
设置了索引。
无论如何,这似乎是一个非常轻量级的查询,但事实证明它是整个瓶颈。
以下是agreement
的完整架构:
CREATE TABLE IF NOT EXISTS `agreement` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`remark` varchar(200) NOT NULL,
`content` longtext NOT NULL,
`is_active` tinyint(1) NOT NULL,
`date_stamp` datetime NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
我怀疑的一件事是remark
表中agreement
的longtext字段,但我们没有使用该字段进行内连接,事实上即使我们没有使用该字段也很慢在查询结果中选择remark
。
最后,我们将agreement
的表从innoDB
转换回MyISAM
,而不是一切正常。查询在不到1秒的时间内完成。
现在,我的问题是这里究竟发生了什么?这是否意味着一旦innoDB表包含任何文本字段,那么该表不能用于内连接?
我希望我能知道真正的原因,以便将来可以避免同样的问题。
非常感谢。
答案 0 :(得分:2)
这是一个着名且棘手的问题。最可能的原因是你在/ tmp中没有空间。
以下是我在书签中保留的链接,可以帮助您:http://www.mysqlperformancetuning.com/a-fix-for-incorrect-key-file-for-table-mysql
根据我的经验,虽然有限,但看到的主要原因 此错误消息是因为您的tmpdir空间不足。喜欢 我你会检查你有多少可用空间:1Gb,2Gb,4Gb。有可能 还不够。这就是原因:MySQL可以创建临时表 比在几秒钟内更大,快速填补任何免费 空间。取决于查询的性质和大小 数据库自然。
你也可以在你的桌子上试试REPAIR,但对我而言,它与霹雳舞一样有用:/
答案 1 :(得分:1)
InnoDB有自己的缓冲区大小等设置。检查一下,如果可以调整它,请继续。只是为了测试尝试加倍,如果它有帮助,你可能想要优化它。它可以产生很大的不同。
一些可能有用的链接:
http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/
http://dev.mysql.com/doc/refman/5.5/en/innodb-buffer-pool.html
答案 2 :(得分:1)
这里的问题可能是remark
字段定义为varchar(200)
?请记住,临时表和内存表存储具有固定长度的varchar
。所以带有varchar(200)
的50k行可能会消耗大量内存,即使它们都是空的。
如果这是问题所以你可以尝试以下几种方法之一:
varchar(200)
列,并始终使用NULL值而不是空字符串varchar(200)
更改为文本(当然有缺点 - 它将始终使用磁盘上的临时表)VARCHAR
尺寸答案 3 :(得分:1)
您的查询的目的是什么?我从您的评论中看到,您只列出了用户信息,而且协议中没有任何内容让我相信您正在寻找有协议的用户? 由于您在引擎之间进行转换,因此我会在添加约束之前认为您正在进行清理。如果是这样,请考虑使用用户表中的左连接,如:
select user.* from user left join agreement on user.agreement_id = agreement.id where user.agreement_id != 0;
如果它不是清理,而只是寻找具有协议的用户,我们就这么简单
select user.* from user where user.agreement_id != 0;
如果目的不是其他,请考虑在user.agreement_id上添加索引,因为内部联接可能需要它来提高速度。让我们知道真正的目的,你可以得到更好的帮助。