SQL-запрос: оптимизация внутренних соединений между большими таблицами - PullRequest
6 голосов
/ 04 февраля 2009

У меня есть 3 следующие таблицы в БД MySQL 4.x:

  • хосты: (300 000 записей)
    • id (НЕ ПОДПИСАНО INT) ПЕРВИЧНЫЙ КЛЮЧ
    • имя (VARCHAR 100)
  • путей: (6.000.000 записей)
    • id (НЕ ПОДПИСАНО INT) ПЕРВИЧНЫЙ КЛЮЧ
    • имя (VARCHAR 100)
  • URL: (7.000.000 записей)
    • host (UNSIGNED INT) PRIMARY KEY <--- ссылки на hosts.id </li>
    • путь (UNSIGNED INT) ПЕРВИЧНЫЙ КЛЮЧ <--- ссылки на paths.id </li>

Как видите, схема действительно проста, но проблема заключается в количестве данных в этих таблицах.

Вот запрос, который я выполняю:

SELECT CONCAT(H.name, P.name)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id;

Этот запрос прекрасно работает, но для его выполнения требуется 50 минут. Кто-нибудь знает, как я могу ускорить этот запрос?

Заранее спасибо. Nicolas

Ответы [ 14 ]

0 голосов
/ 06 февраля 2009

Поскольку я не большой поклонник MySQL, я бы спросил, пробовали ли вы PostgreSQL. В этой БД вы хотели бы убедиться, что ваша настройка work_mem была достаточно высокой, но вы можете установить ее для каждого соединения с БД, например, SET work_mem = 64 МБ.

Еще одно предложение - изучить использование повторяющихся записей пути. - это множество URL-адресов с общими путями.

Еще одна вещь, которая может помочь, а может и не помочь, - это использовать текстовые поля фиксированной длины вместо varchars. Раньше это делало разницу в скорости, но я не уверен насчет современных двигателей DB.

Если вы используете PostgreSQL, это позволит вам использовать JOIN USING, но даже в MySQL мне это нравится больше: имя поля идентификатора должно быть одинаковым в каждой таблице. Вместо id в hosts и host в urls, назовите его host_id в обоих местах.

Теперь еще несколько комментариев. :) Этот макет данных, который у вас есть, очень полезен, когда вы выбираете небольшой набор строк, возможно, каждый URL из одного домена. Это также может помочь lot , если ваши запросы часто требуют последовательного сканирования таблицы URL для других данных, хранящихся там, потому что сканирование может пропускать большие текстовые поля (если это не имеет значения, потому что БД хранит текст через указатели на связанную таблицу в любом случае).

Однако, если вы почти всегда выбираете все данные домена и пути, то имеет смысл хранить их в одной таблице.

0 голосов
/ 04 февраля 2009

Не могу точно сказать о mySQL, но в SQL Server я знаю, что первичные ключи создают индекс автоматически, а внешние ключи - нет. Убедитесь, что в ваших полях внешнего ключа есть индекс.

0 голосов
/ 04 февраля 2009

Я понимаю, что вам нужен полный список URL-адресов - 7 миллионов записей. Возможно, как предложено Mitch , вам следует рассмотреть возможность использования предложения WHERE для фильтрации ваших результатов. Возможно, сроки в основном связаны с задержкой отображения записей

проверить время для этого запроса

select count(*)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id

Если это все еще медленно, я бы пошел и проверил время выберите количество (*) из URL

1010 * тогда *

select count(*) 
from urls u 
inner join hosts h on u.host = h.id

тогда

select count(*) 
from urls u 
inner join hosts h on u.host = h.id
inner join paths p on u.path = p.id

просто чтобы найти источник замедления

Также иногда может помочь изменение порядка запросов

SELECT CONCAT(u.host, u.path)
from urls u 
inner join hosts h on u.host = h.id
inner join paths p on u.path = p.id
0 голосов
/ 04 февраля 2009

Конкат определенно замедляет вас. Можем ли мы увидеть результаты MySQL объяснить? Документация Ссылка

Самое большое, что нужно сделать, это попытаться получить только те данные, которые вам нужны. Если вы можете получить меньше записей, это ускорит вас так же, как и все остальное. Но объяснение mysql должно помочь нам понять, помогут ли какие-либо индексы.

...