Mysql Queries в большой таблице данных

0

У меня проблема с моей таблицей базы данных 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';
  • 2
    пожалуйста. показать запрос в посте тоже
  • 0
    Нам нужно знать, с какими типами запросов у вас возникли проблемы, чтобы иметь возможность дать какое-либо содержательное руководство. Я собираюсь предположить, что вы никогда не используете id в запросах, а это просто суррогатный ключ? Если это так, то вы можете вместо этого сделать id уникальным ограничением и изменить свой первичный ключ на что-то, что поможет больше при извлечении данных обратно. Первичный ключ определяет, в каком порядке физически хранятся данные (кластеризованный индекс), поэтому, если вы всегда запрашиваете штрих-код, то может быть, имеет смысл использовать его в качестве первичного ключа?
Показать ещё 2 комментария
Теги:
mariadb
innodb

3 ответа

1

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. (Но это еще одна дискуссия.)

1

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

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.

-1

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

Поскольку это таблица истории, можно ли включить часть даты в название?

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

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

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

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

  • 0
    моя таблица истории содержит данные только за один год, я уже разделил старые данные в разные таблицы
  • 2
    Это очень плохой совет. Разделение таблицы на несколько разных таблиц - плохая и плохая идея.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню