MySQL Query / Table нуждается в оптимизации - PullRequest
0 голосов
/ 29 января 2019

У меня есть запрос, который занимает смущающе много времени.~ 7 минут неловко.Я был бы очень признателен за помощь.Отсутствующие индексы?Перепишите запрос?Все вышеперечисленное?

Большое спасибо

mysql Ver 14.14 Distrib 5.7.25, для Linux (x86_64)

Запрос выглядит так:

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;

и products имеет только 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 голосов
/ 30 января 2019

Для запроса, как указано, вам нужно

api_events:  INDEX(created_at, product_token)
products:    INDEX(token, name)

Поскольку в WHERE упоминается api_events, оптимизатор может начинаться с этой таблицы.created_at находится в WHERE, поэтому индекс начинается с этого, даже если начинать с «диапазона» обычно неправильно.В этом случае пара «покрывает».

Тогда INDEX(token, name) также является «покрывающим».

«Покрывающие» индексы дают небольшое, но широко варьируемое, улучшение производительности.

0 голосов
/ 29 января 2019

Что произойдет, если вы сгруппируете по токену вместо 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), вероятно, поможет.

Если этобыстрее, тогда вы можете ввести имя с помощью подзапроса.

0 голосов
/ 29 января 2019

Кажется, что критерии на created_at очень избирательны (глядя только на последние 7 дней?).Нам не терпится исследовать индекс с created_at в качестве ведущего столбца.

Запрос также ссылается на столбец product_token из той же таблицы, поэтому мы можем включить этот столбец в индекс, чтобы сделать егоиндекс покрытия.

  api_events_IX ON api_events ( created_at, product_token )

Используя этот индекс, мы, вероятно, можем избежать рассмотрения подавляющего большинства из 31 миллиона строк и быстро сузить подмножество строк, на которые мы действительно должны смотреть.

Используя индекс, для запроса все равно потребуется операция «Использование сортировки файлов», чтобы удовлетворить GROUP BY.

(Я предполагаю, что соединение с 12 строками в продукте не исключаетмного строк ... что в подавляющем большинстве строк в api_event product_token относится к строке, существующей в product.


Используйте MySQL 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 значений, которые не ссылаются на строку в product ... мы могли бы пойти другим путем ...

0 голосов
/ 29 января 2019

Вы можете улучшить производительность соединения, добавив индекс

create index  idx1 on api_events(product_token, created_at);

create index  idx2 on products(token);

Вы также можете попробовать инвертировать столбцы ofr api_events

 create index  idx1 on api_events(created_at, product_token); 

и попробовать добавить избыточность в индекс продукта

create index  idx2 on products(token, name);
...