MySql GROUP BY с использованием файловой сортировки - оптимизация запросов

У меня есть такая таблица:

CREATE TABLE `purchase` (
  `fact_purchase_id` binary(16) NOT NULL,
  `purchase_id` int(10) unsigned NOT NULL,
  `purchase_id_primary` int(10) unsigned DEFAULT NULL,
  `person_id` int(10) unsigned NOT NULL,
  `person_id_owner` int(10) unsigned NOT NULL,
  `service_id` int(10) unsigned NOT NULL,
  `fact_count` int(10) unsigned NOT NULL DEFAULT '0',
  `fact_type` tinyint(3) unsigned NOT NULL,
  `date_fact` date NOT NULL,
  `purchase_name` varchar(255) DEFAULT NULL,
  `activation_price` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
  `activation_price_total` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
  `renew_price` decimal(7,2) unsigned DEFAULT '0.00',
  `renew_price_total` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
  `activation_cost` decimal(7,2) unsigned DEFAULT '0.00',
  `activation_cost_total` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
  `renew_cost` decimal(7,2) unsigned DEFAULT '0.00',
  `renew_cost_total` decimal(7,2) unsigned NOT NULL DEFAULT '0.00',
  `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`fact_purchase_id`),
  KEY `purchase_id_idx` (`purchase_id`),
  KEY `person_id_idx` (`person_id`),
  KEY `person_id_owner_idx` (`person_id_owner`),
  KEY `service_id_idx` (`service_id`),
  KEY `fact_type_idx` (`fact_type`),
  KEY `renew_price_idx` (`renew_price`),
  KEY `renew_cost_idx` (`renew_cost`),
  KEY `renew_price_year_idx` (`renew_price_year`),
  KEY `renew_cost_year_idx` (`renew_cost_year`),
  KEY `date_created_idx` (`date_created`),
  KEY `purchase_id_primary_idx` (`purchase_id_primary`),
  KEY `fact_count` (`fact_count`),
  KEY `renew_price_year_total_idx` (`renew_price_total`),
  KEY `renew_cost_year_total_idx` (`renew_cost_total`),
  KEY `date_fact` (`date_fact`) USING BTREE,
  CONSTRAINT `purchase_person_fk` FOREIGN KEY (`person_id`) REFERENCES `person` (`person_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `purchase_person_owner_fk` FOREIGN KEY (`person_id_owner`) REFERENCES `person` (`person_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `purchase_service_fk` FOREIGN KEY (`service_id`) REFERENCES `service` (`service_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Я запускаю этот запрос:

SELECT 
    purchase.date_fact,
    UNIX_TIMESTAMP(purchase.date_fact),
    COUNT(DISTINCT purchase.purchase_id) AS Num
FROM
    purchase
WHERE
    purchase.date_fact >= '2017-01-01'
    AND purchase.date_fact <= '2017-01-31'
    AND purchase.fact_type = 3
    AND purchase.purchase_id_primary IS NULL
GROUP BY purchase.date_fact

Таблица содержит в общей сложности 5,629,670 записей и работает EXPLAIN По запросу я получаю эти результаты:

  • rows = 2.814.835
  • possible_keys знак равно fact_type_idx,purchase_id_primary_idx,date_fact
  • key знак равно fact_type_idx
  • key_len = 1
  • ref знак равно const
  • filtered = 25,00
  • Extra знак равно Using index condition;Using where;Using filesort

Выполнение запроса занимает 30-35 секунд. Это слишком долго ждать.

Проблема в том, что GROUP BY вызывает применение сортировки файлов. применение ORDER BY NULL на запрос ничего не меняет.

Я мог бы использовать индекс покрытия, но мне просто нужно date_fact в этом запросе: какие поля я могу использовать?

Как я могу избежать сортировки файлов на GROUP BY? Как я могу оптимизировать запрос, чтобы сделать его быстрее?

Я использую эту таблицу для целей статистики (OLAP). Может быть, есть для этого лучшая СУБД?

Я использую MySql Server 5.7.17.

Спасибо

1 ответ

Решение

Для этого запроса:

SELECT p.date_fact, UNIX_TIMESTAMP(p.date_fact),
       COUNT(DISTINCT p.purchase_id) AS Num
FROM purchase p
WHERE p.date_fact >= '2017-01-01' AND
      p.date_fact <= '2017-01-31' AND
      p.fact_type = 3 AND
      p.purchase_id_primary IS NULL
GROUP BY p.date_fact;

Я бы порекомендовал составной индекс на (fact_type, purchase_id_primary, date_fact, purchase_id), Первые два ключа имеют условия равенства в WHERE, Третий имеет неравенство, а четвертый позволяет индексу "покрывать" запрос (все столбцы в запросе находятся в индексе).

Я также добавил бы: если вам не нужно COUNT(DISTINCT)тогда не используйте его. purchase_id может быть уже уникальным в purchase,

Другие вопросы по тегам