mysql查询优化
Posted
技术标签:
【中文标题】mysql查询优化【英文标题】:mysql query optimization 【发布时间】:2012-10-26 04:32:36 【问题描述】:我的任务是帮助加快此查询,我认为这些表上的某些索引设置不正确。我也相信它们不会全部用于 b/c 一个函数应用于我有索引的 col。谁能看到我如何优化这些表或查询? requests
表将是 3 个表中最大的表,将有超过 20 万条记录。 devices
目前有大约 500 条记录,clients
也会更小。
查询:
explain extended SELECT MAX(Request.datetime) AS datetime, Device.id,
Device.client_id, Device.mac_address, Device.type, Device.manufacturer,
Device.model_number, Client.id, Client.email_address,
Request.device_id, Request.datetime, Request.ip_address
FROM livefi.devices AS Device
LEFT JOIN livefi.clients AS Client
ON (Client.id = Device.client_id)
INNER JOIN livefi.requests AS Request
ON (Request.device_id = Device.id)
GROUP BY Request.device_id, Request.client_id
+----+-------------+---------+--------+---------------------------------------------------------+---------------+---------+-------------------------+------+----------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+--------+---------------------------------------------------------+---------------+---------+-------------------------+------+----------+---------------------------------+
| 1 | SIMPLE | Device | ALL | PRIMARY | NULL | NULL | NULL | 617 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | Client | eq_ref | PRIMARY | PRIMARY | 4 | livefi.Device.client_id | 1 | 100.00 | |
| 1 | SIMPLE | Request | ref | idx_device_id,inx_requests_deviceId_datetime_ip_address | idx_device_id | 5 | livefi.Device.id | 144 | 100.00 | Using where |
+----+-------------+---------+--------+---------------------------------------------------------+---------------+---------+-------------------------+------+----------+---------------------------------+
3 rows in set, 1 warning (0.04 sec)
表格:
CREATE TABLE `clients` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`email_address` varchar(100) DEFAULT NULL,
`mac_address` varchar(17) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email_address` (`email_address`),
KEY `idx_mac_address` (`mac_address`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `devices` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`client_id` int(10) unsigned DEFAULT NULL,
`mac_address` varchar(17) DEFAULT NULL,
`type` varchar(25) DEFAULT NULL,
`manufacturer` varchar(100) DEFAULT NULL,
`model_number` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `mac_address` (`mac_address`),
KEY `idx_mac_address` (`mac_address`),
KEY `fk_devices_clients1` (`client_id`),
CONSTRAINT `fk_devices_clients1` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=492 DEFAULT CHARSET=utf8
CREATE TABLE `requests` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`client_id` int(10) unsigned DEFAULT NULL,
`device_id` int(10) unsigned DEFAULT NULL,
`domain_id` int(10) unsigned DEFAULT NULL,
`ip_address` varchar(15) DEFAULT NULL,
`datetime` datetime DEFAULT NULL,
`gmt_offset` time DEFAULT NULL,
`request_method` varchar(15) DEFAULT NULL,
`url` text,
`http_protocol` varchar(20) DEFAULT NULL,
`http_status_code` varchar(20) DEFAULT NULL,
`request_size` int(10) unsigned DEFAULT '0',
`referer` text,
`user_agent` text,
`squid_cache_response` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_client_id` (`client_id`),
KEY `idx_datetime` (`datetime`),
KEY `idx_device_id` (`device_id`),
KEY `idx_domain_id` (`domain_id`),
KEY `idx_id` (`id`),
KEY `idx_request_size` (`request_size`),
KEY `inx_requests_deviceId_datetime_ip_address` (`device_id`,`datetime`,`ip_address`),
CONSTRAINT `fk_requests_clients` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_requests_devices1` FOREIGN KEY (`device_id`) REFERENCES `devices` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_requests_domains1` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=200523 DEFAULT CHARSET=utf8
【问题讨论】:
【参考方案1】:我建议:
ALTER TABLE requests ADD INDEX (device_id, client_id, datetime);
另外,请注意,您不应在 SELECT
子句中包含 Request.datetime
或 Request.ip_address
,因为它们不是 GROUP BY
子句的一部分。这是因为requests
中的几行可能具有相同的device_id
和client_id
,而为datetime
或ip_address
选择哪个值有些随机。
【讨论】:
【参考方案2】:我会在 ( Device_ID, ID ) 上的设备上有一个索引,我过去曾提供过这个索引,并且显然对其他人来说效果很好。日期可能是查询中的一个瓶颈,但整数很容易。
如果您仅对 Device AND MAX(ID) 进行预查询,这实质上将返回最新的日期/时间...由于 ID 是自动递增的,因此其日期/时间值将直接关联为最近的.. 所以它们是一回事。
一个例外情况是,如果您处于多台服务器的某种集群环境中,可能会添加自己的 auto-inc ID 列,这可能会导致错误的最大条目。如果是这种情况,则将索引更改为 (Device_ID, DateTime)。
也就是说,我会执行以下操作
SELECT STRAIGHT_JOIN
PerQuery.Device_ID,
PreQuery.LastRequestID as Request_ID,
RDtl.datetime,
d.client_id,
d.mac_address,
d.type,
d.manufacturer,
d.model_number,
c.id,
c.email_address,
r.ip_address
FROM
( select rq.Device_ID,
MAX( rq.ID ) as LastRequestID
from
livefi.Requests rq
group by
rq.Device_ID ) PreQuery
JOIN livefi.Request RDtl
ON PreQuery.LastRequestID = RDtl.ID
JOIN livefi.Clients c
ON RDtl.ClientID = c.ID
JOIN livefi.Devices d
ON PreQuery.Device_ID = d.ID
预查询是唯一具有 group by 的查询,并且会直接优化到索引...由于每个设备只会返回一条记录及其对应的“请求”ID,因此只会加入原始请求表“其他”详细信息、客户端和设备详细信息。
【讨论】:
以上是关于mysql查询优化的主要内容,如果未能解决你的问题,请参考以下文章