Mysql Queries в большой таблице данных - PullRequest
0 голосов
/ 27 июня 2018

У меня проблема с таблицей базы данных mysql. У меня в таблице более 20 миллионов строк. Структура таблицы показана ниже. Основная проблема заключается в том, что выполнение запросов занимает очень много времени (некоторые запросы занимают более 20 секунд). Я использую индексы там, где могу, однако во многих запросах используется диапазон дат, а с диапазоном дат мои индексы не работают. Также в запросах я использую почти каждый столбец. Что мне нужно изменить в моей таблице данных, чтобы повысить эффективность?

`history` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `barcode` varchar(100) DEFAULT NULL,
  `bag` varchar(100) DEFAULT NULL,
  `action` int(10) unsigned DEFAULT NULL,
  `place` int(10) unsigned DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `old_price` decimal(10,2) DEFAULT NULL,
  `user` int(11) DEFAULT NULL,
  `amount` int(10) DEFAULT NULL,
  `rotation` int(10) unsigned DEFAULT NULL,
  `discount` decimal(10,2) DEFAULT NULL,
  `discount_type` tinyint(2) unsigned DEFAULT NULL,
  `original` int(10) unsigned DEFAULT NULL,
  `was_in_shop` int(10) unsigned DEFAULT NULL,
  `cate` int(10) unsigned DEFAULT NULL COMMENT 'grupe',
  `sub_cate` int(10) unsigned DEFAULT NULL,
  `comment` varchar(255) DEFAULT NULL,
  `helper` varchar(255) DEFAULT NULL,
  `ywd` varchar(255) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `deleted_at` timestamp NULL DEFAULT NULL
)

PRIMARY KEY (`id`),
KEY `barcode` (`barcode`) USING BTREE,
KEY `action` (`action`) USING BTREE,
KEY `original` (`original`) USING BTREE,
KEY `created_at` (`created_at`) USING BTREE,
KEY `bag` (`bag`) USING BTREE

ENGINE=InnoDB

Некоторые из моих запросов:

select SUM(amount) as amount, 
       SUM(comment) as price, 
       cate 
  from `history` 
 where (    `action` = '4' 
        and `place` = '28' 
        and `created_at` >= '2018-04-01 00:00:00'
        and `created_at` <= '2018-04-30 23:59:59'
       ) 
   and `history`.`deleted_at` is null 
group by `cate`;

select cate, 
       SUM(amount) AS kiekis, 
       SUM(IF(discount>0,(price*amount)-discount,(price*amount))) AS suma, 
       SUM(IF(discount>0,IF(discount_type=1,(discount*price)/100,discount),0)) AS nuolaida 
  from `history` 
 where (    `history`.`action` = '4' 
        and `history`.`created_at` >= '2018-01-01 00:00:00'
        and `history`.`created_at` <= '2018-01-23 23:59:59'
       ) 
   and LENGTH(barcode) > 7
   and `history`.`deleted_at` is null 
 group by `cate`;

Ответы [ 3 ]

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

Ваш первый запрос лучше записать как:

select SUM(h.amount) as amount, 
       SUM(h.comment) as price, 
       h.cate 
from history h
where h.action = 4 and 
      h.place = 28 and
      h.created_at >= '2018-04-01' and
      h.created_at < '2018-05-01' and
      h.deleted_at is null 
group by h.cate;

Почему?

  • place и action являются числами. Сравнение должно быть с числом. Смешивание типов может препятствовать использованию индексов.
  • Компонент времени не используется для сравнения дат.
  • Уточнение всех имен столбцов - хорошая идея.

Тогда для этого запроса разумный индекс - history(action, place, created_at, deleted_at).

Итак, я бы начал с многостолбцовых индексов.

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

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

INDEX(a), INDEX(b) служит для некоторых целей, но «составной» INDEX(a,b) лучше подходит для некоторых запросов.

 where (    `action` = '4' 
        and `place` = '28' 
        and `created_at` >= '2018-04-01 00:00:00'
        and `created_at` <= '2018-04-30 23:59:59'
       ) 
   and `history`.`deleted_at` is null 

Потребность

INDEX(action, place, -- first, but in either order
      deleted_at,
      created_at)    -- last

Я предпочитаю писать диапазон дат так:

        and `history`.`created_at` >= '2018-04-01'
        and `history`.`created_at`  < '2018-04-01' + INTERVAL 1 MONTH

Это гораздо проще, чем справиться с високосным годом, концом года и т. Д. И он работает "правильно" для DATE, DATETIME, DATETIME(6), TIMESTAMP и TIMESTAMP(6).

Для этого

 where (    `history`.`action` = '4' 
        and `history`.`created_at` >= '2018-01-01 00:00:00'
        and `history`.`created_at` <= '2018-01-23 23:59:59'
       ) 
   and LENGTH(barcode) > 7
   and `history`.`deleted_at` is null 

Я бы попробовал это как наиболее вероятное:

INDEX(action, deleted_at, created_at)  -- in this order

У нет есть отдельные таблицы для разных лет. Если вы будете удалять старые данные, рассмотрите PARTITION BY RANGE(TO_DAYS(...)), чтобы получить скорость DROP PARTITION. (Но это другое обсуждение.)

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

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

Так как это таблица истории, можно ли включить часть имени в имя?

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

  • history_2014
  • history_2015
  • history_2016
  • history_2017
  • history_2018
  • и т.д.

Тогда вы можете искать по таблице, которая относится к вашему диапазону дат.

Если вам нужна дата из диапазона, охватывающего таблицы, вы можете использовать запрос UNION для объединения двух наборов результатов в один.

...