Rails上的慢查询加入

时间:2017-08-07 07:33:55

标签: mysql ruby-on-rails join mysql-slow-query-log

以下rails查询会在慢查询日志中返回:

Class ParserRun

scope :active, -> {
where(completed_at: nil)
  .joins('LEFT JOIN system_events ON parser_runs.id = system_events.parser_run_id')
  .where("system_events.created_at > '#{active_system_events_threshold}' OR parser_runs.created_at > '#{1.minute.ago.to_s(:db)}'")
}

如何优化此功能?

慢querylog:

SELECT `parser_runs`.*
FROM `parser_runs`
INNER JOIN `system_events` ON `system_events`.`parser_run_id` = `parser_runs`.`id`
WHERE `parser_runs`.`type` IN ('DatasetParserRun')
  AND `parser_runs`.`completed_at` IS NULL
  AND (system_events.created_at <= '2017-08-05 04:03:09');

# Time: 170805  5:03:43

'show create table parser_runs;'

的输出
| parser_runs | CREATE TABLE `parser_runs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`customer_id` int(11) DEFAULT NULL,
`options` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`completed_at` datetime DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_parser_runs_on_customer_id` (`customer_id`)
) ENGINE=InnoDB AUTO_INCREMENT=143327 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

输出'show create table system_events;'

 | system_events | CREATE TABLE `system_events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`log_level` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`customer_id` int(11) DEFAULT NULL,
`classification` int(11) DEFAULT NULL,
`information` text COLLATE utf8_unicode_ci,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`parser_run_id` int(11) DEFAULT NULL,
`notified` tinyint(1) DEFAULT '0',
`dataset_log_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_system_events_on_classification` (`classification`),
KEY `index_system_events_on_customer_id` (`customer_id`),
KEY `index_system_events_on_parser_run_id` (`parser_run_id`),
KEY `index_system_events_on_dataset_log_id` (`dataset_log_id`)
) ENGINE=InnoDB AUTO_INCREMENT=730539 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

EXPLAIN的输出:

  EXPLAIN for: SELECT `parser_runs`.* FROM `parser_runs` LEFT JOIN system_events ON parser_runs.id = system_events.parser_run_id WHERE `parser_runs`.`completed_at` IS NULL AND (system_events.created_at > '2017-08-07 10:09:03')
+----+-------------+---------------+--------+-------------------------   -------------+---------+---------+--------------------------------------+-    -------+-------------+
| id | select_type | table         | type   | possible_keys                          | key     | key_len | ref                                  | rows   |    Extra       |
+----+-------------+---------------+--------+--------------------------------------+---------+---------+--------------------------------------+--------+-------------+
|  1 | SIMPLE      | system_events | ALL    | index_system_events_on_parser_run_id | NULL    | NULL    | NULL                                    | 655946 | Using where |
|  1 | SIMPLE      | parser_runs   | eq_ref | PRIMARY                                | PRIMARY | 4       | ashblood.system_events.parser_run_id |      1 | Using where |
+----+-------------+---------------+--------+--------------------------------------+---------+---------+--------------------------------------+--------+-------------+

2行(0.00秒)

3 个答案:

答案 0 :(得分:1)

查询执行计划的第一步(EXPLAIN SELECT ...的输出)表示正在扫描整个system_events表,以便检查system_events表中的哪些行将是在join中使用parser_runs表。

请在created_at的{​​{1}}列添加索引,然后重复查询。请检查新的执行路径以验证是否正在扫描整个表,或者是否正在使用新索引。

此外,虽然可能不是问题的根源,但您可以在表system_events的{​​{1}}和type列上添加索引。请注意,我的意思是两列(按给定顺序)的索引,而不是每列的索引。

答案 1 :(得分:0)

INDEX(type, completed_at)
INDEX(completed_at, type)
INDEX(created_at, parser_run_id)
INDEX(parser_run_id, created_at)

优化程序更喜欢哪些索引并不明显;添加所有这些。

答案 2 :(得分:-1)

不要使用连接。而是在单独的查询中断开连接查询并将这些数据存储在变量中。然后从这些数据中获得所需的结果。