MySQL - Как этот запрос может быть оптимизирован? - PullRequest
5 голосов
/ 28 июня 2010

Следующий запрос работает, но он очень медленный для 10 записей (2 секунды). Профилирование говорит о создании таблицы tmp, но я не уверен, почему.

По сути, я присоединяюсь к текущему пользователю, к группам acl, чтобы получить все группы, в которые он входит, затем присоединяет группы к компаниям, получает все компании, в которых они находятся, затем присоединяет компании к заказам чтобы получить все заказы ..

Если я уберу эту строку

ORDER BY orders.created_on DESC 

затем запрос выполняется за 0,06 с (более приемлемо) ..

Помогите, есть идеи как оптимизировать? Большое спасибо:)

SELECT
    orders.uuid,
    companies.name as company_name
FROM
    users u
JOIN    
    users_acl_groups g on   u.uuid = g.user_uuid
JOIN
    users_acl acl on (acl.user_uuid = u.uuid or acl.group_uuid = g.group_uuid) 
JOIN 
    companies on acl.item_uuid = companies.uuid
JOIN
    orders on companies.uuid = orders.company_uuid
WHERE
    u.uuid = 'DDEC8073-5056-C000-01ED583A51CBCA32' and orders.status <> ''
ORDER BY orders.created_on DESC 

limit 0, 10;

ОБНОВЛЕНИЕ, объяснение запроса ..

1 ПРОСТЫЕ заказы ВСЕ 9403 Использование временный; Использование сортировки файлов

1 ПРОСТО acl ALL 1859 Использование где; Использование буфера соединения

1 SIMPLE g ALL 2005 Использование где; Использование буфера соединения

1 ПРОСТЫЕ компании eq_ref ПЕРВИЧНЫЙ ПЕРВИЧНЫЙ 52 table.orders.company_uuid 1

1 ПРОСТО u ALL 33595 Использование где; Четкая; Использование буфера соединения

Ответы [ 5 ]

2 голосов
/ 28 июня 2010

Рассматривали ли вы создание таблицы фактов в качестве шага денормализации?

По сути, это таблица пересечений "многие ко многим", например:

CREATE TABLE user_order_fact (
  user_uuid ...
  order_uuid ...
  order_created_on ...
  order_status ...
  company_name ...,
  primary key (user_uuid, order_uuid),
  key (user_uuid, order_status, order_created_on, order_uuid, company_name)
);

... fill with data ...

SELECT
    order_uuid,
    company_name
FROM
    user_order_fact
WHERE
    user_uuid = 'DDEC8073-5056-C000-01ED583A51CBCA32' and order_status <> ''
ORDER BY order_created_on DESC 

limit 0, 10;

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

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

0 голосов
/ 28 июня 2010

Мало идей:

Вы фактически не выбираете orders.created_on в своем запросе. Так что нет смысла сортировать по этому столбцу. Возможно, его выбор (SELECT orders.created_on ...) поможет улучшить производительность (просто дикая догадка - я не понимаю, о чем я здесь говорю).

Вы всегда можете отсортировать в своем приложении - если по вашему запросу не возвращено огромное количество записей.

Иногда целесообразнее использовать N маленьких запросов вместо одного большого SQL-запроса. Псевдокод:

user_id = get_one("SELECT uuid FROM users WHERE ...");
group_ids = get_many("SELECT uuid FROM groups WHERE user_uuid = " + user_id);
comps_ids = get_many("SELECT DISTINCT item_uuid FROM acls WHERE user_uuid = " + user_id + " OR group_uuid IN " + groups_ids.to_q());
orders = get_many("SELECT * FROM orders WHERE company_uuid IN " + comps_ids.as_q() + " WHERE status <> '' ORDER BY created_on");
0 голосов
/ 28 июня 2010

Я не уверен, что может быть точной причиной для того, чтобы это заняло 2 секунды. Что невозможно для этого запроса, извлекающего 10 записей, но то, что вы видите здесь:

  1. acl.user_uuid = u.uuid or acl.group_uuid = g.group_uuid

    Соединение на основе UID, может быть, вы тоже используя его в качестве первичного ключа ответил выше.

  2. ORDER BY orders.created_on. Использование Order by на date не будет столь же оптимальным, как использование PK или любого целочисленного значения более уместно.

  3. orders.status <> '' Если использовать какие-либо индексы для таблиц, то нет индекса может быть использован в этом запросе, потому что НЕ оператор и лайк Оператор не использует индексы при использовании в любом запросе.

  4. Объем записей, представленных в таблице, может быть другой причиной, но только из-за вышеуказанных факторов. В противном случае он мог бы обработать и большой объем.

Основным фактором, который я считаю, является UID, используемый в соединениях Таким образом, в запросе можно увидеть все три условия, позволяющие избежать их, которые могут сделать ваш запрос ленивым

0 голосов
/ 28 июня 2010

Трудно ответить, не зная много о существующих индексах или объеме каждой таблицы.

Кроме того, без особой информации о модели ... возвращает ли запрос все результаты?

Все ли пользователи входят в группу? Похоже, что нет ... и запрос не вернет пользователей вне группы.

Может ли группа принадлежать группе, требующей рекурсивного запроса?

0 голосов
/ 28 июня 2010

убедитесь, что у "orders.created_on" есть индекс ... Если это так, то подход Билла наверху будет лучшим, однако потребует немного работы.

...