Как найти причину разницы во времени выполнения запроса для разных баз данных? - PullRequest
0 голосов
/ 07 июня 2018

У меня есть две базы данных с одинаковыми схемами.Одна база данных от производства, другая - тестовая база данных.Я делаю запрос к одной таблице из базы данных.В рабочей таблице запрос занимает около 4,3 секунды, а в тестовой базе данных - около 130 мс.,Тем не менее, рабочая таблица содержит менее 50 000 записей, а я заполнил тестовую таблицу более чем 100 000.Я сравнил две таблицы, и у обеих одинаковые индексы.Мне кажется, что проблема в данных.Во время посева я пытался генерировать как можно более случайные данные, чтобы я мог имитировать условия производства, но все равно не смог воспроизвести медленный запрос.

Я посмотрел результаты EXPLAIN для двух запросов.Они имеют значительные различия в двух последних столбцах.

Производство:

+-------+-------------------------+
| rows  | Extra                   |
+-------+-------------------------+
| 24459 | Using where             |
| 46    | Using where; Not exists |
+-------+-------------------------+

Тест:

+------+------------------------------------+
| rows | Extra                              |
+------+------------------------------------+
| 3158 | Using index condition; Using where |
| 20   | Using where; Not exists            |
+------+------------------------------------+

Оператор создания для таблицы на производстве:

CREATE TABLE `usage_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `operation` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  `check_time` datetime NOT NULL,
  `check_in_log_id` int(11) DEFAULT NULL,
  `daily_usage_id` int(11) DEFAULT NULL,
  `duration_units` decimal(11,2) DEFAULT NULL,
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `facility_id` int(11) NOT NULL,
  `notes` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `mac_address` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '00:00:00:00:00:00',
  `login` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_usage_logs_on_user_id` (`user_id`),
  KEY `index_usage_logs_on_check_in_log_id` (`check_in_log_id`),
  KEY `index_usage_logs_on_facility_id` (`facility_id`),
  KEY `index_usage_logs_on_check_time` (`check_time`),
  KEY `index_usage_logs_on_mac_address` (`mac_address`),
  KEY `index_usage_logs_on_operation` (`operation`)
) ENGINE=InnoDB AUTO_INCREMENT=145147 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

в то же время в тестовой базе данных:

CREATE TABLE `usage_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `operation` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  `check_time` datetime NOT NULL,
  `check_in_log_id` int(11) DEFAULT NULL,
  `daily_usage_id` int(11) DEFAULT NULL,
  `duration_units` decimal(11,2) DEFAULT NULL,
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `facility_id` int(11) NOT NULL,
  `notes` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `mac_address` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '00:00:00:00:00:00',
  `login` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_usage_logs_on_check_in_log_id` (`check_in_log_id`),
  KEY `index_usage_logs_on_check_time` (`check_time`),
  KEY `index_usage_logs_on_facility_id` (`facility_id`),
  KEY `index_usage_logs_on_mac_address` (`mac_address`),
  KEY `index_usage_logs_on_operation` (`operation`),
  KEY `index_usage_logs_on_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=104001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

Полный запрос:

SELECT `usage_logs`.*
FROM `usage_logs`
LEFT OUTER JOIN usage_logs AS usage_logs_latest ON usage_logs.facility_id = usage_logs_latest.facility_id
AND usage_logs.user_id = usage_logs_latest.user_id
AND usage_logs.mac_address = usage_logs_latest.mac_address
AND usage_logs.check_time < usage_logs_latest.check_time
WHERE `usage_logs`.`facility_id` = 5
  AND `usage_logs`.`operation` = 'checkIn'
  AND (usage_logs.check_time >= '2018-06-08 00:00:00')
  AND (usage_logs.check_time <= '2018-06-08 11:23:05')
  AND (usage_logs_latest.id IS NULL)

Я выполняю запрос на одном компьютере для двух разныхбазы данных, поэтому я не думаю, что другие процессы вмешиваются в результат.

Что означает этот результат и какие дальнейшие шаги я могу предпринять, чтобы выяснить причину большой разницы во времени выполнения

1 Ответ

0 голосов
/ 08 июня 2018

Какие версии MySQL вы используете?

Есть много факторов, которые приводят к тому, что Оптимизатор принимает решение о

  • , с какой таблицы начинать;(мы не видим, отличаются ли они)
  • какой индекс (ы) использовать;(мы не видим)
  • и т. д.

Некоторые факторы:

  • распределение значений индекса на данный момент,
  • версия MySQL,
  • фаза луны.

Это также может привести к различным числам (оценкам) в EXPLAIN, что может привести к различнымпланы запросов.

Также другие действия на сервере могут влиять на доступность CPU / IO / и т. д.В частности, кэширование данных может легко показать разницу в 10 раз.Вы запускали каждый запрос дважды?Кеш запросов отключен?innodb_buffer_pool_size это то же самое?ОЗУ одинакового размера?

Я вижу Using index condition и никаких «составных» индексов.Часто производительность может быть улучшена путем предоставления подходящего составного индекса. Подробнее

Я должен увидеть запрос!

Посев

Случайный или не очень- случайно строки могут влиять на выбор оптимизатором того, какой индекс (и т. д.) использовать.Это может привело к выбору лучшего способа выполнить запрос на 'test'.

Нам нужно увидеть EXPLAIN SELECT ..., чтобы обсудить этот угол далее.

Составные индексы

Они могут помочь на обоих серверах:

INDEX(facility_id, operation,   -- either order
      check_time)               -- last
INDEX(facility_id, user_id, max_address, check_time,  -- any order
      id)                       -- last

Есть быстрое улучшение.Вместо того чтобы находить все более поздние строки, но не использовать их содержимое, используйте 'semi-join', который запрашивает несуществование из any такие строки:

SELECT  `usage_logs`.*
    FROM  `usage_logs`
    WHERE  `usage_logs`.`facility_id` = 5
      AND  `usage_logs`.`operation` = 'checkIn'
      AND  (usage_logs.check_time >= '2018-06-08 00:00:00')
      AND  (usage_logs.check_time <= '2018-06-08 11:23:05')
      AND NOT EXISTS ( SELECT 1 FROM  usage_logs AS latest 
             WHERE  usage_logs.facility_id = latest.facility_id
               AND  usage_logs.user_id     = latest.user_id
               AND  usage_logs.mac_address = latest.mac_address
               AND  usage_logs.check_time  < latest.check_time )

(те же индексы будут в порядке.)

Похоже, что запрос получает "все, кроме самой последней";это то, что вы хотели?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...