我的查询花费了令人尴尬的长时间。尴尬〜7分钟。我真的很感谢您的帮助。缺少索引?重写查询?以上所有?
非常感谢
适用于Linux(x86_64)的mysql Ver 14.14 Distrib 5.7.25
查询如下:
SELECT COUNT(*) AS count_all, name
FROM api_events ae
INNER JOIN products p on p.token=ae.product_token
WHERE (ae.created_at > '2019-01-21 12:16:53.853732')
GROUP BY name
这是两个表定义
api_events 拥有约31条一百万记录
CREATE TABLE `api_events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`api_name` varchar(200) NOT NULL,
`hostname` varchar(200) NOT NULL,
`controller_action` varchar(2000) NOT NULL,
`duration` decimal(12,5) NOT NULL DEFAULT '0.00000',
`view` decimal(12,5) NOT NULL DEFAULT '0.00000',
`db` decimal(12,5) NOT NULL DEFAULT '0.00000',
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`product_token` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `product_token` (`product_token`)
) ENGINE=InnoDB AUTO_INCREMENT=64851218 DEFAULT CHARSET=latin1;
和 产品只有12条记录
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(30) NOT NULL,
`name` varchar(100) NOT NULL,
`description` varchar(2000) NOT NULL,
`token` varchar(50) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=latin1;
答案 0 :(得分:1)
您可以改善连接性能添加索引
create index idx1 on api_events(product_token, created_at);
create index idx2 on products(token);
您还可以尝试反转api_events的列
create index idx1 on api_events(created_at, product_token);
并尝试向产品索引添加冗余
create index idx2 on products(token, name);
答案 1 :(得分:1)
对于所述查询,您需要
api_events: INDEX(created_at, product_token)
products: INDEX(token, name)
由于WHERE
提到了api_events,因此优化程序很可能从该表开始。 created_at
在WHERE
中,因此索引从此开始,即使通常以“ range”开头也是错误的。在这种情况下,这对是“遮盖的”。
然后,INDEX(token, name)
也正在“覆盖”。
“覆盖”索引的性能提高幅度很小,但差异很大。
答案 2 :(得分:0)
如果您按令牌而不是name
分组怎么办?
SELECT ae.product_token, COUNT(*) AS count_all
FROM api_events ae
WHERE ae.created_at > '2019-01-21 12:16:53.853732')
GROUP BY ae.product_token;
对于此查询,api_events(created_at, product_token)
上的索引可能会有所帮助。
如果速度更快,则可以使用子查询输入名称。
答案 3 :(得分:0)
created_at
上的条件似乎具有很高的选择性(仅查看过去7天?)。这呼喊探索具有索引created_at
作为一个领先的列中。
该查询还引用了同一张表中的product_token
列,因此我们可以将该列包括在索引中,以使其成为覆盖索引。
api_events_IX ON api_events ( created_at, product_token )
使用该索引,我们可以避免查看3,100万行中的绝大多数,并迅速缩小我们实际需要查看的行的子集。
使用索引,查询仍将需要执行“使用文件排序”操作才能满足GROUP BY。
(在这里,我的猜测是,加入到产品中的12个行不排除很多行......这对绝大多数的行api_event
{的{1}}指product_token
中存在的行。
使用的MySQL product
,查看查询执行计划。
进一步的改进(以测试其性能)可能是在嵌入式视图中进行一些聚合:
EXPLAIN
如果关于所述假设 SELECT SUM(s.count_all) AS count_all
, p.name
FROM ( SELECT COUNT(*) AS count_all
, ae.product_token
FROM api_events ae
WHERE ae.created_at > '2019-01-21 12:16:53.853732'
GROUP
BY ae.product_token
) s
JOIN products p
ON p.token = s.product_token
GROUP
BY p.name
的误导,如果有大量的行中product_token
具有api_event
中不引用行值product_token
..我们可能会采取不同的策略...