Простой запрос работает медленно - PullRequest
1 голос
/ 04 марта 2020

У меня есть следующий запрос:

SELECT `assignments`.`id`
FROM `assignments` 
WHERE
  `assignments`.`account_id` = 742 
  AND `assignments`.`method` != 'stray' 
  AND (
    `assignments`.`judge_id` = 2349724 
    OR (
      `assignments`.`role_id` IN (234, 8745) 
      AND `assignments`.`judge_id` IS null
    )
  );

Эта таблица в настоящее время имеет 6,6 миллиона записей и имеет довольно мало трафика c. Наш самый медленный запрос - тот, что приведен выше, и даже с индексом, который нацелен на account_id, method, jud_id и role_id, он выполняется приблизительно за 0,5 с.

Запрос использует предоставленный индекс, но, похоже, не выполняет дать ему большую поддержку.

Что я могу сделать здесь, чтобы улучшить запрос и сократить его до 100 мс? 6,6 миллиона записей на самом деле не так много = \

Я также хотел бы добавить, что если я просто ограничу запрос предложением account_id (который имеет свой собственный индекс), скорость будет примерно такой же , Так что я действительно озадачен.

Ниже приведен план выполнения только с использованием account_id:

EXPLAIN select `assignments`.id FROM `assignments`WHERE `assignments`.`account_id` = 374;
+----+-------------+-------------+------------+------+----------------------------------------------------------------------+------------------------------+---------+-------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys                                                        | key                          | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+----------------------------------------------------------------------+------------------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | assignments | NULL       | ref  | assignments_account_id_index,assignments_account_id_updated_at_index | assignments_account_id_index | 9       | const |  965 |   100.00 | Using index |
+----+-------------+-------------+------------+------+----------------------------------------------------------------------+------------------------------+---------+-------+------+----------+-------------+

Создать синтаксис таблицы:

CREATE TABLE `assignments` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `key` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `batch` int(10) unsigned NOT NULL,
  `account_id` bigint(20) unsigned DEFAULT NULL,
  `season_id` bigint(20) unsigned DEFAULT NULL,
  `judge_id` bigint(20) unsigned DEFAULT NULL,
  `role_id` bigint(20) unsigned DEFAULT NULL,
  `entry_id` bigint(20) unsigned NOT NULL,
  `score_set_id` bigint(20) unsigned NOT NULL,
  `slug` char(8) COLLATE utf8_unicode_ci DEFAULT NULL,
  `method` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `original_method` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `status` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'none',
  `locked` tinyint(1) NOT NULL DEFAULT '0',
  `conflict_of_interest` tinyint(1) NOT NULL DEFAULT '0',
  `raw_score` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `raw_total` double NOT NULL DEFAULT '0',
  `weighted_score` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `weighted_total` double NOT NULL DEFAULT '0',
  `weight_sum` decimal(8,2) NOT NULL DEFAULT '0.00',
  `progress` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `consensus` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `top_pick_preference` tinyint(3) unsigned DEFAULT NULL,
  `top_pick_winner` tinyint(1) NOT NULL DEFAULT '0',
  `top_pick_rank` int(11) DEFAULT NULL,
  `total_votes` bigint(20) unsigned NOT NULL DEFAULT '0',
  `scored_at` timestamp NULL DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `assignments_key_unique` (`key`),
  KEY `assignments_account_id_index` (`account_id`),
  KEY `assignments_judge_id_index` (`judge_id`),
  KEY `assignments_role_id_index` (`role_id`),
  KEY `assignments_entry_id_index` (`entry_id`),
  KEY `assignments_score_set_id_index` (`score_set_id`),
  KEY `assignments_season_id_index` (`season_id`),
  KEY `assignments_slug_index` (`slug`),
  KEY `assignments_status_index` (`status`),
  KEY `assignments_method_index` (`method`),
  KEY `assignments_original_method_index` (`original_method`),
  KEY `assignments_account_id_updated_at_index` (`account_id`,`updated_at`)
) ENGINE=InnoDB AUTO_INCREMENT=661994447 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

Ответы [ 4 ]

3 голосов
/ 04 марта 2020

Поскольку условия диапазона отрицательно влияют на способность MySQL использовать преимущества индексов, иногда может использоваться UNION (за счет некоторого дублирования синтаксиса запроса):

SELECT a.id 
FROM `assignments` AS a
WHERE a.`account_id` = 742 
   AND a.`judge_id` = 2349724 
   AND a.`method` != 'stray' 
UNION ALL
SELECT a.id 
FROM `assignments` AS a
WHERE a.`account_id` = 742 
   AND a.`judge_id` IS NULL AND a.`role_id` IN (234, 8745)
   AND a.`method` != 'stray'
;

Составной индекс на account_id, judge_id, method_id или account_id, judge_id, role_id будут очень полезны для выполнения вышеупомянутого запроса. ... и если я не ошибаюсь, первая из них может принести пользу первой половине, а вторая - второй (но есть и такая вещь, как перерасчет).

2 голосов
/ 04 марта 2020

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

Проверьте количество получаемых вами предметов

SELECT count(*)
FROM `assignments` 
WHERE
  `assignments`.`account_id` = 742 
  AND `assignments`.`method` != 'stray' 
  AND (
    `assignments`.`judge_id` = 2349724 
    OR (
      `assignments`.`role_id` IN (234, 8745) 
      AND `assignments`.`judge_id` IS null
    )
  );

сообщает нам, сколько записей Ваш конкретный c запрос должен завершиться.

SELECT `assignments`.`id`
FROM `assignments` 
WHERE
  `assignments`.`account_id` = 742;

сообщает, сколько заданий связано с данной учетной записью. Если счет значительно быстрее, чем фактический выбор, это может означать что-то. Кроме того, если имеется много записей, может потребоваться много времени, чтобы загрузить их в память и отправить по сети на другой компьютер.

Проверьте, не является ли что-либо быстрым для таблицы

SELECT `assignments`.`id`
FROM `assignments` limit 0, 100;

Если это медленно, то у вас могут быть проблемы с вашей сетью.

Создайте копию своей базы данных

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

Создание правильных индексов

Создание многомерных индексов в вашей таблице, используя поля, которые вы используете в качестве фильтров в предложении where, поскольку UUeerdo и GMB уже предложили в своих ответах.

1 голос
/ 04 марта 2020

Это может быть не полный ответ, но: у вас нет нужного индекса на месте. Составной индекс отличается от отдельных индексов в каждом столбце.

Вместо этого рассмотрим:

(account_id, judge_id, role_id, method, id)

Весь индекс может фактически не использоваться из-за AND / OR / IN, но это по крайней мере дает планировщику запросов шанс. Возможно, вы также захотите попробовать его с помощью запроса union all Ууэрдо (upvoted).

0 голосов
/ 04 марта 2020

Первое, что приходит мне в голову, это то, что вам не нужно иметь «назначения» везде в вашем запросе.

select id FROM `assignments`
WHERE `account_id` = 742
AND `method` != 'stray'
AND (`judge_id` = 2349724 
OR (`role_id` IN (234, 8745) 
AND `judge_id` IS null));

должно работать просто отлично. Хорошо, это не решит вашу проблему.

Может быть, вы можете сначала запросить идентификатор и метод после этого, например, так:

    select id FROM `assignments`
WHERE `account_id` = 742
    AND (`judge_id` = 2349724 
OR (`role_id` IN (234, 8745) 
AND `judge_id` IS null))
AND `method` != 'stray';

Просто идея.

...