3

我的所有表都在 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 |                             | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+

现在看来,我的大量查询都停留在“排序结果”步骤中:

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
4

6 回答 6

13

正如你应该从 MyISAM 迁移到 Innodb 中所写的那样吗?(这是最近的):

Innodb 需要调整作为关于 MyISAM 到 Innodb 迁移的最后说明,我应该提到 Innodb 调整。Innodb 需要调优。真的。许多应用程序的 MyISAM 可以很好地使用默认值。我已经看到数百 GB 的数据库使用默认设置的 MyISAM 运行,并且运行合理。Innodb 需要资源,并且它不能很好地使用默认值。从默认值调整 MyISAM 很少会获得超过 2-3 倍的收益,而对于 Innodb 表,尤其是对于写入密集型工作负载,它可能会高达 10-50 倍。在这里查看详细信息。

所以,关于 MySQL Innodb Settings,作者在Innodb Performance Optimization Basics中写道:

最重要的是:

innodb_buffer_pool_size 70-80% 的内存是安全的选择。我在 16GB 盒子上将它设置为 12G。

更新:如果您正在寻找更多详细信息,请查看有关 调整 innodb 缓冲池的详细指南

innodb_log_file_size – 这取决于您的恢复速度需求,但 256M 似乎是合理恢复时间和良好性能之间的良好平衡

innodb_log_buffer_size=4M 4M 适用于大多数情况,除非您在这种情况下将大 blob 传送到 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 基准测试 - 第 1 部分

于 2009-09-15T19:47:54.490 回答
4

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

MySQL 4.x 有一个限制,它只允许 MySQL 每个表使用一个索引。由于它使用 metaward_achiever.award_id 列上的索引进行 WHERE 选择,因此它也不能使用 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/

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

于 2009-09-15T19:14:01.393 回答
3

我的猜测是您可能没有配置超出默认值的 InnoDB 设置。你应该做一个快速的谷歌来设置你的 InnoDB 选项。

让我开箱即用的最明显性能问题是innodb_buffer_pool_size. 这应该设置为机器内存的 50-80%。默认情况下,它通常只有几 MB。向上曲柄,您应该看到明显的性能提升。

也看看innodb_additional_mem_pool_size

从这里开始,也可以在谷歌周围搜索“innodb 性能调整”。

于 2009-09-15T18:29:24.820 回答
2

从我的记忆来看,MySQL 的查询优化器并不好。尝试子选择而不是直接连接。

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

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

于 2009-09-15T18:01:00.727 回答
1

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

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

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

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

使用磁盘上的 filesort() 而不是内存中的文件排序要慢得多。但是,引擎应该对此没有任何影响。filesort 不是引擎功能,而是 MySQL 的核心功能。实际上,filesort 确实有很多方法,但通常不会那么慢。

于 2009-09-20T11:27:10.380 回答
0

尝试为字段添加键:metaward_achiever. alias_id, metaward_achiever. award_id, 和metaward_achiever. modified这会有很大帮助。并且不要在 varchar 字段上使用键,这会增加插入和更新的时间。此外,您似乎在成就表中有 77M 记录,您可能需要关心 innodb 优化。关于如何为它设置内存限制有很多很好的导师。

于 2009-09-15T17:59:33.727 回答