sql - InnoDB 排序真的那么慢吗?

标签 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

最佳答案

写在Should you move from MyISAM to Innodb ? (这是最近的):

Innodb Needs Tuning As a final note about MyISAM to Innodb migration I should mention about Innodb tuning. Innodb needs tuning. Really. MyISAM for many applications can work well with defaults. I’ve seen hundreds of GB databases ran with MyISAM with default settings and it worked reasonably. Innodb needs resources and it will not work well with defaults a lot. Tuning MyISAM from defaults rarely gives more than 2-3 times gain while it can be as much as 10-50 times for Innodb tables in particular for write intensive workloads. Check here for details.

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

The most important ones are:

innodb_buffer_pool_size 70-80% of memory is a safe bet. I set it to 12G on 16GB box.

UPDATE: If you’re looking for more details, check out detailed guide on tuning innodb buffer pool

innodb_log_file_size – This depends on your recovery speed needs but 256M seems to be a good balance between reasonable recovery time and good performance

innodb_log_buffer_size=4M 4M is good for most cases unless you’re piping large blobs to Innodb in this case increase it a bit.

innodb_flush_log_at_trx_commit=2 If you’re not concern about ACID and can loose transactions for last second or two in case of full OS crash than set this value. It can dramatic effect especially on a lot of short write transactions.

innodb_thread_concurrency=8 Even with current Innodb Scalability Fixes having limited concurrency helps. The actual number may be higher or lower depending on your application and default which is 8 is decent start

innodb_flush_method=O_DIRECT Avoid double buffering and reduce swap pressure, in most cases this setting improves performance. Though be careful if you do not have battery backed up RAID cache as when write IO may suffer.

innodb_file_per_table – If you do not have too many tables use this option, so you will not have uncontrolled innodb main tablespace growth which you can’t reclaim. This option was added in MySQL 4.1 and now stable enough to use.

Also check if your application can run in READ-COMMITED isolation mode – if it does – set it to be default as transaction-isolation=READ-COMMITTED. This option has some performance benefits, especially in locking in 5.0 and even more to come with MySQL 5.1 and row level replication.

仅作记录,mysqlperformanceblog.com 背后的人员运行了一个比较 Falcon、MyISAM 和 InnoDB 的基准测试。基准测试本来应该突出 Falcon,但 InnoDB 赢得了这一天,几乎在每个测试中每秒查询次数都超过了 Falcon 和 MyISAM:InnoDB vs MyISAM vs Falcon benchmarks – part 1 .

关于sql - InnoDB 排序真的那么慢吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1428678/

相关文章:

php - mysql_num_rows 期望参数 1 是资源

python - Digi ConnectPort 数据库适配器

c++ - 什么时候值得使用数据库?

sql - 如何返回表的列名?

mysql - on 子句中的未知列,无法在工作台中重现

php - 基于多个复选框状态构建 SQL 查询

mysql - 如何使用mysql中的更新从另一个表获取计数来增加一个表的计数?

database - 具有 3 个键的数据库表

c# - SQL Server - Guid VS。长

sql - 在 SQL 中如何比较日期值?