替代GROUP BY来优化SQL调用

时间:2013-06-28 02:27:06

标签: mysql sql sql-optimization

我有以下非常慢的SQL语句。它从600-800毫秒不等!

我正在寻找可能的优化方法,但不确定最佳路线。我的数据库相当大,entries表有400,000行,devices表有90,000行。


SQL语句

SELECT devices.manufacturer, COUNT(devices.manufacturer) AS device_count 
FROM entries 
   JOIN devices ON entries.device_id=devices.id 
WHERE waypoint_id IN (1,2,3,5) 
  AND entries.updated_at >= '2013-06-20 21:01:40 -0400' 
  AND entries.updated_at <= '2013-06-27 21:01:40 -0400'
  GROUP BY devices.manufacturer;

这个SQL语句是否因为我在糟糕的硬件上运行它,或者因为语句错误,或者我没有正确构建表而导致语句缓慢?任何想法都将不胜感激!


声明目标

获取所有设备制造商的列表,以及制造商在条目表中显示的次数的相关计数。


表格结构

设备

id int(11) NOT NULL AUTO_INCREMENT,
mac_address varchar(255) DEFAULT NULL,
user_id int(11) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
manufacturer varchar(255) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY mac_address (mac_address),
KEY manufacturer (manufacturer)
ENGINE=InnoDB AUTO_INCREMENT=839310 DEFAULT CHARSET=utf8;

条目

id int(11) NOT NULL AUTO_INCREMENT,
device_id int(11) DEFAULT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
waypoint_id int(11) DEFAULT NULL,
unsure tinyint(1) DEFAULT '0',
PRIMARY KEY (id),
KEY device_index (device_id)
ENGINE=InnoDB AUTO_INCREMENT=3389538 DEFAULT CHARSET=utf8;

另外 - 我一直在研究备用数据库。考虑到这个数据库将来需要非常快速的读/写,Redis会有用吗?

2 个答案:

答案 0 :(得分:2)

如果在entries(waypoint_id, updated_at)上添加了多列索引,查询将运行得更快。

此外,您的查询看起来会更好:

SELECT
    devices.manufacturer,
    COUNT(devices.manufacturer) AS device_count 
FROM
    entries
JOIN
    devices ON devices.id = entries.device_id
WHERE
    entries.waypoint_id IN (1,2,3,5)
AND
    entries.updated_at BETWEEN '2013-06-20 21:01:40 -0400' AND '2013-06-27 21:01:40 -0400'
GROUP BY
    devices.device_id

P.S。:明确声明device_id作为外键不是一件好事吗?

答案 1 :(得分:1)

您需要Entries {waypoint_id, updated_at}上的索引。这应该满足:

WHERE waypoint_id IN (1,2,3,5) 
  AND entries.updated_at >= '2013-06-20 21:01:40 -0400' 
  AND entries.updated_at <= '2013-06-27 21:01:40 -0400';

根据实际的基数,您可能想要或不想要反转此复合索引中字段的顺序。

或者,在Entries {waypoint_id, updated_at, device_id}上创建covering索引,以避免完全访问Entries表。


最重要的是,考虑在Devices {id, manufacturer}上创建索引。希望MySQL能够足够智能地使用它来满足JOIN和聚合,甚至无需访问Devices表。