Текущая реализация представляет собой один сложный запрос с несколькими объединениями и временными таблицами, но он слишком напрягает мой MySQL и занимает более 30 секунд для загрузки таблицы. Данные извлекаются PHP с помощью JavaScript-вызова Ajax и отображаются на веб-странице. Вот эти таблицы:
Table: table_companies
Columns: company_id, ...
Table: table_manufacture_line
Columns: line_id, line_name, ...
Table: table_product_stereo
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, ...
Table: table_product_television
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, warranty_expiry, ...
В одной компании может быть более 100 000 товаров, разделенных на две таблицы продуктов. Таблицы продуктов объединяются и фильтруются по line_name, затем упорядочиваются по Assembly_datetime и ограничиваются в зависимости от подкачки. Значение datetime также зависит от часового пояса и применяется как часть запроса (другая таблица JOIN + temp). line_name также является одним из возвращаемых столбцов.
Я думал о выделении фильтра line_name из запроса объединения продуктов. По сути, я бы определил идентификаторы строк, которые соответствуют фильтру, а затем выполнил бы запрос UNION с условием WHERE. WHERE line_id IN (<results from previous query>)
, Это избавило бы от необходимости объединений и временных таблиц, и я могу применить имя_строки к line_id и модификации часового пояса в PHP, но я не уверен, что это лучший способ добиться цели.
Я также рассмотрел возможность использования Redis, но большое количество отдельных продуктов приводит к такому же длительному времени ожидания при отправке всех данных в Redis через PHP (20-30 секунд), даже если они извлекаются непосредственно из Таблицы продуктов.
Я ценю любой вклад, который вы можете предоставить.
Редактировать:
Существующий запрос:
SELECT line_name,CONVERT_TZ(datetime,'UTC',timezone) datetime,... FROM (SELECT line_name,datetime,... FROM ((SELECT line_id,assembly_datetime datetime,... FROM table_product_stereos WHERE company_id=# ) UNION (SELECT line_id,assembly_datetime datetime,... FROM table_product_televisions WHERE company_id=# )) AS union_products INNER JOIN table_manufacture_line USING (line_id)) AS products INNER JOIN (SELECT timezone FROM table_companies WHERE company_id=# ) AS tz ORDER BY datetime DESC LIMIT 0,100
Здесь он отформатирован для удобства чтения.
SELECT line_name,CONVERT_TZ(datetime,'UTC',tz.timezone) datetime,...
FROM (SELECT line_name,datetime,...
FROM (SELECT line_id,assembly_datetime datetime,...
FROM table_product_stereos WHERE company_id=#
UNION
SELECT line_id,assembly_datetime datetime,...
FROM table_product_televisions
WHERE company_id=#
) AS union_products
INNER JOIN table_manufacture_line USING (line_id)
) AS products
INNER JOIN (SELECT timezone
FROM table_companies
WHERE company_id=#
) AS tz
ORDER BY datetime DESC LIMIT 0,100
Идентификаторы индексируются; Первичные ключи — это первый ключ для каждого столбца.
Давайте создадим этот запрос из его составных частей, чтобы посмотреть, что мы можем оптимизировать.
Замечание: вы выбираете 100 самых последних строк из объединения двух больших таблиц продуктов.
Итак, начнем с попытки оптимизировать подзапросы, извлекающие данные из таблиц продуктов. Вот один из них.
SELECT line_id,assembly_datetime datetime,...
FROM table_product_stereos
WHERE company_id=#
Но посмотрите, вам нужно только 100 новых записей здесь. Итак, давайте добавим
ORDER BY assembly_datetime DESC
LIMIT 100
на этот запрос. Кроме того, вы должны поместить составной индекс в эту таблицу следующим образом. Это позволит индексам удовлетворять поискам WHERE и ORDER BY.
CREATE INDEX id_date ON table_product_stereos (company_id, assembly_datetime)
Все те же соображения относятся к запросу от table_product_televisions
, Закажите его по времени, ограничьте до 100 и внесите в указатель.
Если вам нужно применить другие критерии выбора, вы можете поместить их в эти внутренние запросы. Например, в комментарии вы упомянули выборку, основанную на поиске подстроки. Вы можете сделать это следующим образом
SELECT t.line_id,t.assembly_datetime datetime,...
FROM table_product_stereos AS t
JOIN table_manufacture_line AS m ON m.line_id = t.line_id
AND m.line_name LIKE '%test'
WHERE company_id=#
ORDER BY assembly_datetime DESC
LIMIT 100
Далее вы используете UNION
объединить эти два набора результатов запроса в один. UNION
имеет функцию устранения дубликатов, что отнимает много времени. (Вы знаете, что у вас нет дубликатов, но MySQL нет.) Использование UNION ALL
вместо.
Собрав все это вместе, самый внутренний подзапрос становится этим. Нам нужно завершить подзапросы, потому что SQL сбит с толку UNION
а также ORDER BY
пункты на том же уровне запроса.
SELECT * FROM (
SELECT line_id,assembly_datetime datetime,...
FROM table_product_stereos
WHERE company_id=#
ORDER BY assembly_datetime DESC
LIMIT 100
) AS st
UNION ALL
SELECT * FROM (
SELECT line_id,assembly_datetime datetime,...
FROM table_product_televisions
WHERE company_id=#
ORDER BY assembly_datetime DESC
LIMIT 100
) AS tv
Это дает вам 200 строк. Он должен получить эти строки довольно быстро.
200 строк гарантированно будет достаточным, чтобы предоставить вам 100 самых последних предметов позже, после того как вы выполните ORDER BY ... LIMIT
операция. Но эта операция должна обрабатывать только 200 строк, а не 100K +, поэтому она будет намного быстрее.
Наконец, заверните этот запрос в материал внешнего запроса. Присоединяйся к table_manufacture_line
информация и исправить часовой пояс.
Если вы делаете индексацию и ORDER BY ... LIMIT
Операция ранее, этот запрос должен стать очень быстрым.
Диалог комментариев в вашем вопросе указывает мне, что у вас может быть несколько типов продуктов, а не только два, и что у вас есть сложные критерии выбора для вашего постраничного отображения. С помощью UNION ALL
при большом количестве строк производительность снижается: она преобразует несколько проиндексированных таблиц во внутренний список строк, которые просто невозможно эффективно найти.
Вы действительно должны подумать о том, чтобы поместить два вида данных о продукте в одну таблицу вместо того, чтобы UNION ALL
несколько таблиц продуктов. Установки, которые у вас сейчас есть, негибкие и не будут легко масштабироваться. Если вы структурируете свою схему с помощью основной таблицы продуктов и, возможно, некоторых атрибутивных таблиц для информации по конкретным продуктам, через два года вы окажетесь намного счастливее. Шутки в сторону. Пожалуйста, рассмотрите возможность внесения изменений.
Помните: индекс быстро, данные медленно. Используйте объединения над вложенными запросами. Вложенные запросы возвращают все поля данных, тогда как объединения просто учитывают фильтры (которые должны быть все проиндексированы — убедитесь, что существует уникальный индекс для table_product _ *. Line_id). Прошло много времени, но я уверен, что вы можете присоединиться к «ON company_id = #», что должно сократить результаты на ранней стадии.
В этом случае все результаты относятся к одной и той же компании (или к гораздо меньшему подмножеству), поэтому имеет смысл запускать этот запрос отдельно (и это делает запрос более понятным).
Таким образом, ваш источник данных будет:
(table_product_stereos as prod
INNER JOIN table_manufacture_line AS ml ON prod.line_id = ml.line_id and prod.company_id=#
UNION
table_product_televisions as prod
INNER JOIN table_manufacture_line as ml on prod.line_id = ml.line_id and prod.company_id=#)
Из которого вы можете выбрать прод. или мл. поля по мере необходимости.
PHP не является решением вообще …
Redis может быть решением.
Но главное, что я хотел бы изменить, — это создание индексов для таблиц (добавление отсутствующих индексов) … Если вы работаете с временными таблицами, вы плохо создавали индексы для таблиц. И 100 тысяч строк совсем немного.
Но я не могу помочь вам без каких-либо операторов создания таблиц, а также запросов, которые вы выполняете.
Убедитесь, что ваше «где часть» является частью вашего индекса слева направо.