InnoDB排序真的很慢吗?

时间:2009-09-15 17:51:42

标签: sql mysql database innodb myisam

我在myISAM中拥有了所有的表,但是当我长时间运行更新作业时,表级锁定开始杀了我。我将我的主表转换为InnoDB,现在我的许多查询都需要花费1分钟才能完成,这些查询几乎是在myISAM上完成的。它们通常卡在Sorting result步骤中。我做错了吗?

例如:

SELECT * FROM `metaward_achiever` 
 INNER JOIN `metaward_alias` ON (`metaward_achiever`.`alias_id` = `metaward_alias`.`id`) 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100  

现在大约需要90秒。这是描述:

+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
| id | select_type | table             | type   | possible_keys                                         | key                        | key_len | ref                             | rows  | Extra                       |
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
|  1 | SIMPLE      | metaward_achiever | ref    | metaward_achiever_award_id,metaward_achiever_alias_id | metaward_achiever_award_id | 4       | const                           | 66424 | Using where; Using filesort | 
|  1 | SIMPLE      | metaward_alias    | eq_ref | PRIMARY                                               | PRIMARY                    | 4       | paul.metaward_achiever.alias_id |     1 |                             | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+

现在看来,我的查询中的TONS卡在“排序结果”步骤中:

mysql> show processlist;
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| Id     | User | Host      | db   | Command | Time | State          | Info                                                                                                 |
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| 460568 | paul | localhost | paul | Query   |    0 | NULL           | show processlist                                                                                     | 
| 460638 | paul | localhost | paul | Query   |    0 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460710 | paul | localhost | paul | Query   |   79 | Sending data   | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460722 | paul | localhost | paul | Query   |   49 | Updating       | UPDATE `metaward_alias` SET `modified` = '2009-09-15 12:43:50', `created` = '2009-08-24 11:55:24', ` | 
| 460732 | paul | localhost | paul | Query   |   25 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

为什么这个简单的更新会停留49秒?

如果有帮助,这里有模式:

| metaward_alias | CREATE TABLE `metaward_alias` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(255) DEFAULT NULL,
  `shortname` varchar(100) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `type_id` int(11) NOT NULL,
  `md5` varchar(32) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_alias_user_id` (`user_id`),
  KEY `metaward_alias_type_id` (`type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=858381 DEFAULT CHARSET=utf8 | 

| metaward_award | CREATE TABLE `metaward_award` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(20) NOT NULL,
  `owner_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `description` longtext NOT NULL,
  `owner_points` int(11) NOT NULL,
  `url` varchar(500) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `parent_award_id` int(11) DEFAULT NULL,
  `slug` varchar(110) NOT NULL,
  `true_points` double DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_award_owner_id` (`owner_id`),
  KEY `metaward_award_parent_award_id` (`parent_award_id`),
  KEY `metaward_award_slug` (`slug`),
  KEY `metaward_award_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=122176 DEFAULT CHARSET=utf8 | 

| metaward_achiever | CREATE TABLE `metaward_achiever` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `award_id` int(11) NOT NULL,
  `alias_id` int(11) NOT NULL,
  `count` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `metaward_achiever_award_id` (`award_id`),
  KEY `metaward_achiever_alias_id` (`alias_id`)
) ENGINE=InnoDB AUTO_INCREMENT=77175366 DEFAULT CHARSET=utf8 | 

这些在我的my.cnf

innodb_file_per_table
innodb_buffer_pool_size = 2048M
innodb_additional_mem_pool_size = 16M
innodb_flush_method=O_DIRECT

6 个答案:

答案 0 :(得分:13)

正如Should you move from MyISAM to Innodb ?中所写的那样(这是最近的事情):

  

Innodb需要调整作为关于MyisAM到Innodb迁移的最后一点,我应该提一下Innodb调整。 Innodb需要调整。真。许多应用程序的MyISAM可以很好地使用默认值。我已经看到数百个GB数据库使用默认设置运行MyISAM,并且它运行合理。 Innodb需要资源,并且它对默认值不会很好。从默认值调整MyISAM很少会提供超过2-3倍的增益,而对于Innodb表来说,尤其是写入密集型工作负载可能会高达10-50倍。查看here了解详情。

因此,关于MySQL Innodb设置,作者在Innodb Performance Optimization Basics中写道:

  

最重要的是:

     

innodb_buffer_pool_size 70-80%   记忆是一个安全的赌注。我把它设置为12G   在16GB的盒子上。

     

更新:如果您正在寻找更多内容   详细信息,请查看详细指南   tuning innodb buffer pool

     

innodb_log_file_size - 这取决于   你的恢复速度需要256M   似乎是一个很好的平衡   合理的恢复时间和良好   性能

     

innodb_log_buffer_size = 4M 4M很好   对于大多数情况,除非你是管道   在这种情况下,Innodb的大块   增加一点。

     

innodb_flush_log_at_trx_commit = 2 如果您不关心ACID,并且在完全操作系统崩溃的情况下可能会丢失最后一两秒的事务而不是设置此值。它可以产生戏剧性的效果,特别是对很多短写事务。

     

innodb_thread_concurrency = 8 即使使用当前的Innodb可伸缩性修复程序,有限的并发性也有帮助。实际数字可能更高或更低,具体取决于您的应用程序和默认值8是正常的开始

     

innodb_flush_method = O_DIRECT 避免双重缓冲并降低交换压力,在大多数情况下,此设置可提高性能。如果您没有电池备份RAID缓存,请务必小心,因为写IO可能会受到影响。

     

innodb_file_per_table - 如果您没有太多的表使用此选项,那么您将无法获得无法控制的innodb主表空间增长,这是您无法回收的。此选项已在MySQL 4.1中添加,现在足够稳定可供使用。

     

同时检查您的应用程序是否可以在READ-COMMITED隔离模式下运行 - 如果是 - 将其设置为缺省为transaction-isolation = READ-COMMITTED。此选项具有一些性能优势,特别是在锁定5.0甚至更多内容时,MySQL 5.1和行级复制。

仅仅是为了记录,mysqlperformanceblog.com背后的人们运行了比较Falcon,MyISAM和InnoDB的基准。基准测试真的应该突出Falcon,除了它赢得了当天的InnoDB,几乎每一次测试都以每秒查询次数计算Falcon和MyISAM:InnoDB vs MyISAM vs Falcon benchmarks – part 1

答案 1 :(得分:4)

这是MySQL必须手动排序的大型结果集(66,424行)。尝试向metaward_achiever.modified添加索引。

MySQL 4.x存在一个限制,它只允许MySQL为每个表使用一个索引。由于它在WHERE选择的metaward_achiever.award_id列上使用索引,因此它不能使用metaward_achiever.modified上的索引进行排序。我希望你使用MySQL 5.x,这可能会改善这一点。

您可以通过对此简化查询进行说明来了解此信息:

SELECT * FROM `metaward_achiever` 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100

如果你可以使用WHERE选择和排序的索引来获得这个,那么你就可以了。

您还可以使用metaward_achiever.award_id和metaward_achiever创建复合索引。如果MySQL没有使用它,那么你可以提示它或只删除award_id上的那个。

或者,如果你可以摆脱metaward_achiever.id并将metaward_achiever.award_id作为主键并在metaward_achiever.modified上添加一个键,或者更好的是将metaward_achiever.award_id与metaward.modified结合使用你的主键,那么你''真的很棒。

您可以尝试通过修改设置来优化文件排序。不幸的是,我没有这方面的经验,因为我们的DBA处理配置,但你可能想看看这个伟大的博客: http://www.mysqlperformanceblog.com/

这是一篇关于filesort的文章: http://s.petrunia.net/blog/?p=24

答案 2 :(得分:3)

我的猜测是您可能尚未将InnoDB设置配置为超出默认值。你应该快速谷歌设置你的InnoDB选项。

导致我开箱即用的最明显的性能问题的是innodb_buffer_pool_size。应将其设置为机器内存的50-80%。默认情况下,它通常只有几MB。用方式进行练习,你应该会看到明显的性能提升。

另请查看innodb_additional_mem_pool_size

Start here,还要谷歌周围的“innodb性能调整”。

答案 3 :(得分:2)

从我的记忆中看,MySQL的查询优化器并不好。尝试使用subselect而不是直接连接。

SELECT * FROM (SELECT * FROM `metaward_achiever` 
               WHERE `metaward_achiever`.`award_id` = 1507) a
INNER JOIN `metaward_alias` ON (a.`alias_id` = `metaward_alias`.`id`) 
ORDER BY a.`modified` DESC 
LIMIT 100

或类似的东西(上面未经测试的语法)。

答案 4 :(得分:1)

排序是由MySQL中的数据库服务器而不是存储引擎完成的。

如果在这两种情况下,引擎都无法以已排序的形式提供结果(这取决于所使用的索引),那么服务器需要对它们进行排序。

MyISAM / InnoDB可能不同的唯一原因是行返回的顺序可能会影响数据的排序方式--MyISAM 可以以“更多排序”顺序返回数据一些情况(反之亦然)。

然而,排序60k行不会花费很长时间,因为它是一个非常小的数据集。你确定你的排序缓冲区设置得足够大吗?

使用光盘上的filesort()而不是内存中的文件很慢。但是,引擎应该对此没有任何影响。 filesort不是引擎函数,而是MySQL核心函数。事实上,filesort在很多方面都很糟糕,但通常情况下并不那么慢。

答案 5 :(得分:0)

尝试为字段添加密钥:metaward_achieveralias_idmetaward_achieveraward_idmetaward_achievermodified这会有很多帮助。并且不要在varchar字段上使用键,这将增加插入和更新的时间。此外,您似乎在成就者表中有77M记录,您可能想要关注innodb优化。关于如何设置内存限制,有很多优秀的导师。