Производительность базы данных MySQL на сверхбольших столах - PullRequest
0 голосов
/ 08 сентября 2018

У меня есть таблица данных о продажах, в которой в среднем ежедневно вставляется 1 329 415 строк. Я должен генерировать отчет из таблицы ежедневно в разных форматах. Но запрос из таблицы слишком медленный. Вот мой вывод команды SHOW CREATE TABLE.

CREATE TABLE `query_manager_table` (
  `mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `region_id` int(2) NOT NULL,
  `rtslug` varchar(10) DEFAULT NULL,
  `dsid` int(3) NOT NULL,
  `dpid` int(3) NOT NULL,
  `route_number` int(4) NOT NULL,
  `route_id` int(11) NOT NULL,
  `rtlid` int(11) NOT NULL,
  `retailer_code` varchar(16) DEFAULT NULL,
  `platform_code` varchar(16) DEFAULT NULL,
  `prid` int(4) NOT NULL,
  `skid` int(4) NOT NULL,
  `group` int(4) NOT NULL,
  `family` int(4) NOT NULL,
  `volume` float DEFAULT NULL,
  `value` float(7,2) DEFAULT NULL,
  `date` date NOT NULL DEFAULT '0000-00-00',
  `outlets` int(4) NOT NULL,
  `visited` int(4) NOT NULL,
  `channel` int(3) DEFAULT NULL,
  `subchannel` int(3) DEFAULT NULL,
  `tpg` int(4) DEFAULT NULL,
  `ioq` int(10) DEFAULT NULL,
  `sales_time` int(11) DEFAULT NULL,
  PRIMARY KEY (`dpid`,`route_id`,`rtlid`,`prid`,`skid`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50100 PARTITION BY LIST (YEAR(date) * 100 + QUARTER(date))
(PARTITION y2017q1 VALUES IN (201701) ENGINE = InnoDB,
 PARTITION y2017q2 VALUES IN (201702) ENGINE = InnoDB,
 PARTITION y2017q3 VALUES IN (201703) ENGINE = InnoDB,
 PARTITION y2017q4 VALUES IN (201704) ENGINE = InnoDB,
 PARTITION y2018q1 VALUES IN (201801) ENGINE = InnoDB,
 PARTITION y2018q2 VALUES IN (201802) ENGINE = InnoDB,
 PARTITION y2018q3 VALUES IN (201803) ENGINE = InnoDB,
 PARTITION y2018q4 VALUES IN (201804) ENGINE = InnoDB,
 PARTITION y2019q1 VALUES IN (201901) ENGINE = InnoDB,
 PARTITION y2019q2 VALUES IN (201902) ENGINE = InnoDB,
 PARTITION y2019q3 VALUES IN (201903) ENGINE = InnoDB,
 PARTITION y2019q4 VALUES IN (201904) ENGINE = InnoDB) */

Теперь я просто хочу узнать продажи розничных продавцов с 1 сентября по 9 сентября по следующему запросу -

SELECT
            query_manager_table.dpid,
            query_manager_table.route_id,
            query_manager_table.rtlid,
            query_manager_table.prid,
            SUM(query_manager_table.`volume`) AS sales,
            1 AS memos
        FROM
            query_manager_table
        WHERE
            query_manager_table.date BETWEEN '2018-09-01'
        AND '2018-09-08'    
GROUP BY
            query_manager_table.dpid,
            query_manager_table.rtlid,
            query_manager_table.date

Но это занимает около 500-700 сек. Я добавил dpid IN (1,2,.....) И prid IN (1,2,....), поскольку оба поля добавляются в качестве первичного ключа. Затем выход приходит через 300сек. Что я делаю не так?

+----+-------------+---------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
| id | select_type | table               | type | possible_keys | key  | key_len | ref  | rows      | Extra                                        |
+----+-------------+---------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
|  1 | SIMPLE      | query_manager_table | ALL  | PRIMARY       | NULL | NULL    | NULL | 129065467 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+

Когда я добавляю все dpid и prid в условие условия, тогда EXPAIN выглядит как

+----+-------------+---------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
    | id | select_type | table               | type  | possible_keys | key     | key_len | ref  | rows   | Extra                                        |
    +----+-------------+---------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
    |  1 | SIMPLE      | query_manager_table | range | PRIMARY       | PRIMARY | 4       | NULL | 128002 | Using where; Using temporary; Using filesort |
    +----+-------------+---------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+

Есть ли способ оптимизировать таблицу или запрос? Если я запускаю EXPLAIN PARTITIONS SELECT ... для первого, то получаю -

+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
| id | select_type | table               | partitions                                                                                      | type | possible_keys | key  | key_len | ref  | rows      | Extra                                        |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+
|  1 | SIMPLE      | query_manager_table | y2017q1,y2017q2,y2017q3,y2017q4,y2018q1,y2018q2,y2018q3,y2018q4,y2019q1,y2019q2,y2019q3,y2019q4 | ALL  | PRIMARY       | NULL | NULL    | NULL | 127129410 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+------+---------------+------+---------+------+-----------+----------------------------------------------+

За 2-й получаю -

+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| id | select_type | table               | partitions                                                                                      | type  | possible_keys | key     | key_len | ref  | rows   | Extra                                        |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
|  1 | SIMPLE      | query_manager_table | y2017q1,y2017q2,y2017q3,y2017q4,y2018q1,y2018q2,y2018q3,y2018q4,y2019q1,y2019q2,y2019q3,y2019q4 | range | PRIMARY       | PRIMARY | 4       | NULL | 153424 | Using where; Using temporary; Using filesort |
+----+-------------+---------------------+-------------------------------------------------------------------------------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

INDEXes используются для эффективности в SELECTs.

Значение PRIMARY KEY (в MySQL) по определению является уникальным INDEX. Он должен иметь минимальный набор столбцов, которые однозначно идентифицируют строку.

Любой уникальный индекс (включая PK) также является «ограничением уникальности» - это предотвращает вставку нескольких строк с одинаковым набором значений if.

Указатели используются «слева». То есть с INDEX(a,b), если a бесполезен, он не попадет на b.

PARTITION BY LIST практически бесполезен. Это редко, если вообще когда-либо, улучшает производительность. Вы показали нам пару запросов; давайте рассмотрим больше типичных запросов, чтобы мы могли помочь вам с индексами и секционированием.

    WHERE
        query_manager_table.date BETWEEN '2018-09-01'
                                     AND '2018-09-08'    

напрашивается на INDEX(date). В составном индексе столбцы после «диапазона» не будут достигнуты. То есть в INDEX(date, x, y) тестирование date для диапазона (например, 8 дней в WHERE) не позволит использовать x или y. С другой стороны, WHERE date = '2018-09-01' AND x=1 будет использовать больше индекса.

float(7,2) - не используйте параметр (m,n) для FLOAT или DOUBLE. Вместо этого переключитесь на DECIMAL.

INT всегда 4 байта. См. TINYINT (1 байт), SMALLINT (2 байта) и т. Д. Это само по себе может сократить размер таблицы вдвое.

Чтобы объяснить это:

PRIMARY KEY (`dpid`,`route_id`, ...
WHERE ... AND dpid IN (...) AND ...

удается использовать первое (помните: 'самый левый') для псевдодальности IN, но не может использовать что-либо еще в PK, так как route_id следующий.

Это объясняет, почему второй EXPLAIN имеет меньшие "Строки". Также обратите внимание на «4» в «key_len» - это число байтов в dpid.

После внесения некоторых из этих изменений вернитесь, чтобы мы могли обсудить использование сводных таблиц для ускорения процесса. Однако «изменить» может привести к сложности в этой оптимизации.

Сколько у вас оперативной памяти? Какое значение innodb_buffer_pool_size?

Не используйте GUID, если не обязаны; из-за случайности они замедляют действия на больших столах.

0 голосов
/ 12 сентября 2018

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

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

Что касается вашего запроса SELECT, вы, вероятно, захотите проиндексировать поля в предложении WHERE. В любом случае вы захотите ознакомиться с планами выполнения и поэкспериментировать с различными индексами и ключевыми структурами (возможно, это проще сделать на подмножестве ваших данных). Google "план выполнения mysql" для большого количества информации.

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