需要优化的MySQL查询/表

时间:2019-01-29 15:21:37

标签: mysql sql database indexing query-optimization

我的查询花费了令人尴尬的长时间。尴尬〜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;

4 个答案:

答案 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_atWHERE中,因此索引从此开始,即使通常以“ 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 ..我们可能会采取不同的策略...