Оптимизация заказа с лимитом в MySql - PullRequest
0 голосов
/ 20 марта 2019

У меня есть таблица с 3 миллионами записей, которая называется "транзакции".

CREATE TABLE transactions(
  id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  lookupAId int(6) NOT NULL,
  .....
  updateDate TIMESTAMP
)

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

select * from transactions
   join lookupA on (well indexed columns) 
   .. ( 12 lookup table joins) 
order by updateDate limit 500

Без порядка по предложению запрос выполняется в миллисекундах, но с порядком по это занимает около минуты. Прогнозируется увеличение таблицы до 12-15 миллионов записей.

  1. Мой SLA должен получить результаты менее чем за секунду, возможно ли это в MySql?
  2. Как я могу оптимизировать порядок по пунктам, чтобы это выполнялось.

Я запускаю MySql 5.7 в экземпляре RDS, оптимизированном для памяти xLarge, в AWS

UPDATE 1 updateDate имеет компонент времени и индексируется (B-дерево, неуникально)

Обновление 2 Это сработало, хотя я не знаю почему

SELECT * FROM (select * from transactions order by updateDate) transactions
   join lookupA on (well indexed columns) 
   .. ( 12 lookup table joins) 
   limit 500

Ответы [ 4 ]

1 голос
/ 20 марта 2019

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

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

SELECT * FROM (select * from transactions order by updateDate limit 500) transactions
   join lookupA on (well indexed columns) 
   .. ( 12 lookup table joins) 
1 голос
/ 20 марта 2019

Если у вас его еще нет, ORDER BY определенно выиграет от индекса:

create index ix1 on transactions (updateDate);
0 голосов
/ 10 апреля 2019

Оптимизатор не знает, что таблица - это простая таблица поиска.Он должен быть готов найти 0 или более 1 строки.

Случай 1: вы знаете , что в каждой из таблиц поиска (JOINed) ровно 1 строка:

Случай 2. Вы знаете , что в каждой справочной таблице не более 1 строки.

В обоих этих случаях следующий эффективный способ переписать запрос:

SELECT  t.a, t.b, ...
        ( SELECT name FROM LU1 WHERE id = t.name_id ) AS name, 
        ( SELECT foo  FROM LU1 WHERE id = t.foo_id ) AS foo, 
        ...
    FROM transactions AS t
    ORDER BY t.OrderDate
    LIMIT ...

и

INDEX(OrderDate)
INDEX(id)  -- for each LU table, unless there is already `PRIMARY KEY(id)`

Эта формулировка запроса будет сосредоточена на прохождении ровно 500 строк, отсортированных по OrderDate, при поиске 12 объектовдля каждой строки.

Это семантически эквивалентно случаю 2 (LEFT JOIN), поскольку он дает NULL для name (и т. д.), когда нет сопоставления.

Технически, случай1 не то же самое.Если поиск не удался, JOIN не сможет сосчитать строку, но моя переформулировка сохранит строку, показывая NULL.

0 голосов
/ 10 апреля 2019

Обычный метод решения этой проблемы:

SELECT ... JOIN ...
    LIMIT ...

заключается в следующем:

  1. Выполните минимальный объем работы, чтобы найти PRIMARY KEY значения строк, которые учитываютв LIMIT строк.
  2. Введите эти идентификаторы в JOINs, чтобы получить остальную информацию.

Когда ваш запрос остается в силе, оптимизатор вскидывает руки ипросто выполняет все JOIN (оптимизируя каждый как можно лучше), генерируя большую (много строк, много столбцов) промежуточную таблицу, затем применяет ORDER BY (сортировка множества строк по многим столбцам) и LIMIT (доставканекоторые из этих строк).

С INDEX(OrderDate) (и этот столбец находится в таблице, с которой он решает начать JOINing), Оптимизатор может по крайней мере рассмотреть возможность использования индекса.Но это может быть наихудший случай: что, если не будет 500 строк?в любом случае, он сделает всю работу!

...