2013-11-19 2 views
1

У меня есть таблица со следующей структурой:MySQL несколько объединений медлительности

db structure

Я получил все мои первичные ключи правильно и каждый Int & дата столбца имеет индекс, и (playerId, groupId) в player_groups - уникальный индекс. Все таблицы используют InnoDB, а отношения, которые вы видите, также являются ссылками в InnoDB.

Полный источник базы данных, с данными, примерно подобно примерам, я продемонстрирую ниже: https://www.dropbox.com/s/kzf89occlq4legy/bf4_performance.sql

запросов:

  • Запрос # 1: SELECT * FROM playerkills ORDER BY date DESC LIMIT 300;

  • Запрос # 2: SELECT p.playerName AS player, p2.playerName AS target, w.weaponName AS weapon, date, headshot, s.serverName AS server FROM playerkills JOIN players p ON playerkills.playerId = p.playerId JOIN players p2 ON playerkills.targetId = p2.playerId JOIN weapons w ON playerkills.weaponId = w.weaponId JOIN servers s ON playerkills.serverId = s.serverId ORDER BY date DESC LIMIT 300;

Производительность на моей собственной машине, которая имеет много ресурсов:

  • Запрос # 1: 0,0016 сек
  • Запрос # 2: 1,3050 сек

Производительность на живом сервере базы данных, которая имеет значительно меньше ресурсов:

  • Запрос # 1: 0,046 сек
  • Запрос # 2: 1,297 сек

Я боюсь, что что-то сильно влияет на производительность, так как отлично работает без каких-либо заявлений JOIN. Может ли кто-нибудь пролить свет на это?

ps (Редактировать). Также, возможно, важно добавить: эта база данных обновляется в реальном времени каждые несколько секунд, поэтому результаты запросов нельзя кэшировать так же, как запросы на статические данные.

EDIT 2: профилирование на статической версии (на localhost) дал мне эту картину:

db profile

Выход из EXPLAIN:

db explain

+0

тем больше столов вы присоединитесь тем медленнее он получает, может улучшить с помощью правильной индексации, но не так быстро, как одной таблицы выберите – Konstantin

+0

@ user2864740 Добавлены более подробные данные по запросу. – skiwi

+0

Вы присоединяетесь к 'p2' с' playerkills' на 'targetId', который не имеет индекса. Это означает, что необходимо выполнить полное сканирование таблицы. Попробуйте добавить индекс в 'playerkills' и снова отобразить вывод' EXPLAIN'. –

ответ

4

Это один из тех, случаи, когда подзаголовок может помочь вашему делу. Вместо того, чтобы присоединяться ко всем этим таблицам в полную таблицу игроков, вы можете перечислить свои последние 300 игроков, а затем присоединиться к ним на основе этого. Так что-то вроде:

SELECT 
    p.playerName AS player, 
    p2.playerName AS target, 
    w.weaponName AS weapon, 
    pk.date AS date, 
    pk.headshot AS headshot, 
    s.serverName AS server 
FROM 
(SELECT * FROM playkills ORDER BY date DESC LIMIT 300) AS pk 
JOIN players p 
    ON pk.playerId = p.playerId 
JOIN players p2 
    ON pk.targetId = p2.playerId 
JOIN weapons w 
    ON pk.weaponId = w.weaponId 
JOIN servers s 
    ON pk.serverId = s.serverId 
+0

Он нуждается в другом 'ORDER BY date DESC' в конце, но, кроме этого, это очень быстро! Большое спасибо, хотя я думал, что MySQL всегда будет делать оптимизацию для вас, учитывая, что вы сами делаете правильные индексы и т. Д. Может быть, это один из краевых случаев? – skiwi

+0

@skiwi Способ, которым MySQL генерирует план выполнения, может часто отличаться от того, что можно ожидать (особенно иногда игнорируя индексы, доступные для использования). Однако это не один из тех случаев, когда 'ORDER BY' и' LIMIT' в основном применяются к результату основного запроса. В исходном запросе это означало, что вам нужно было соединить все записи во всех таблицах, упорядочить их по дате и затем ограничить набор результатов. Хотя в целом подвыборы не очень хороши для производительности, есть такие случаи, когда ограничение набора данных перед присоединением является правильным подходом. –

+0

Это просто плохое планирование запросов на стороне MySQL. Порядок-by/limit * может быть перемещен по правилам RA. Однако я не могу спорить с практическим результатом. – user2864740

Смежные вопросы