Ускорьте MySQL запрос, содержащий более 300 тыс. Записей - PullRequest
2 голосов
/ 18 декабря 2009

Мне нужно посмотреть все мои товары (sku's), их последнее количество на складе.
У меня есть одна таблица (называемая «сток») с 315 тысячами записей +, содержащих эту информацию (каждый день добавляется новый пакет данных для большинства sku) Справочные данные находятся в другой таблице (называемой «складской файл»).

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

SELECT s1 . * , f1 . *
FROM stock s1
JOIN stockfile f1 ON ( s1.stockfileid = f1.stockfileid )
LEFT OUTER JOIN ( stock s2
JOIN stockfile f2 ON ( s2.stockfileid = f2.stockfileid )
) ON ( s1.sku = s2.sku
AND ( f1.date < f2.date
OR f1.date = f2.date
AND f1.stockfileid < f2.stockfileid) )
WHERE s2.sku IS NULL

Это определения таблиц

SHOW CREATE TABLE акции:

CREATE TABLE `stock` (
 `stockid` bigint(20) NOT NULL AUTO_INCREMENT,
 `sku` char(25) NOT NULL,
 `quantity` int(5) NOT NULL,
 `creationdate` datetime NOT NULL,
 `stockfileid` smallint(5) unsigned NOT NULL,
 `touchdate` datetime NOT NULL,
 PRIMARY KEY (`stockid`),
 KEY `stock_sku` (`sku`),
 KEY `stock_stockfileid` (`stockfileid`)
) ENGINE=MyISAM AUTO_INCREMENT=316039 DEFAULT CHARSET=latin1

SHOW CREATE TABLE склад:

CREATE TABLE `stockfile` (
 `stockfileid` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `filename` varchar(25) NOT NULL,
 `creationdate` datetime DEFAULT NULL,
 `touchdate` datetime DEFAULT NULL,
 `date` datetime DEFAULT NULL,
 `begindate` datetime DEFAULT NULL,
 `enddate` datetime DEFAULT NULL,
 PRIMARY KEY (`stockfileid`),
 KEY `stockfile_date` (`date`)
) ENGINE=MyISAM AUTO_INCREMENT=266 DEFAULT CHARSET=latin1

Без каких-либо дополнительных индексов это займет ... навсегда. Я добавил их, и они ускорились примерно до 250 секунд:

CREATE INDEX stock_sku ON stock(sku);
CREATE INDEX stock_stockfileid ON stock(stockfileid);
CREATE INDEX stockfile_date ON stockfile(date);

Это EXPLAIN в исходном запросе с этими индексами.

id  select_type  table   type    possible_keys               key        key_len  ref                     rows   Extra
1   SIMPLE       s1      ALL    stock_stockfileid           NULL       NULL     NULL                    316038
1   SIMPLE       f1      eq_ref  PRIMARY                     PRIMARY    2        kompare.s1.stockfileid  1     
1   SIMPLE       s2      ref    stock_sku,stock_stockfileid stock_sku  25       kompare.s1.sku          12     Using where
1   SIMPLE       f2      eq_ref  PRIMARY,stockfile_date      PRIMARY    2        kompare.s2.stockfileid  1

Есть ли другой способ ускорить процесс?

  • Спасибо Биллу Карвину за решение оригинального запроса!

Ответы [ 4 ]

4 голосов
/ 18 декабря 2009

Я не уверен, что правильно выполнил ваш запрос, но если можно с уверенностью предположить, что максимальная дата имеет также максимальный stockfileid (например, ваше условие ИЛИ наполовину подсказывает), возможно, что-то вроде этого запроса поможет

SELECT s1.*, f1.*
 FROM
  stock s1 JOIN stockfile f1 USING (stockfileid)
  JOIN (
   SELECT sku, max(date) AS maxdate, max(stockfileid) AS maxfileid
   FROM stock JOIN stockfile USING (stockfileid)
   GROUP BY sku
  ) AS dfi ON (s1.sku,f1.date,f1.stockfileid)=(dfi.sku,maxdate,maxfileid);

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

3 голосов
/ 18 декабря 2009

Значения по умолчанию в my.cnf обычно устанавливаются для систем с ОЧЕНЬ небольшим объемом памяти по современным стандартам. Если вы используете эти значения по умолчанию, это может быть единственное лучшее место для поиска прироста производительности. Убедитесь, что вы выделяете всю память, которую можете выделить для MySQL.

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

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

Ваш EXPLAIN указывает, что MySQL выполняет сканирование таблицы, чтобы удовлетворить WHERE s2.sku IS NULL до сужения поиска. Это очень дорого.

f1.date < f2.date
OR f1.date = f2.date 

должна быть в состоянии переписать как

f1.date <= f2.date

хотя я сомневаюсь, что это важно для оптимизатора.

Не могли бы вы объяснить простым языком, что вы пытаетесь сделать с запросом? Это может помочь пролить свет на то, как это можно упростить.

2 голосов
/ 18 декабря 2009

Я не уверен, что это то, что вы могли бы сделать с вашим приложением, но вместо вычисления количества для каждого sku каждый раз, когда вы запускаете запрос, было бы более эффективно хранить sku и количество в отдельном таблицу, а затем просто обновлять данные каждый раз, когда получен новый запасной файл. Таким образом, вы берете на себя затраты на его вычисление один раз для каждого файла результатов, а не один раз для запроса. Это довольно дорого обходится, но это значительно экономит ваше время.

2 голосов
/ 18 декабря 2009

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

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