Как оптимизировать следующий запрос SELECT - PullRequest
2 голосов
/ 14 февраля 2020

У нас есть следующая таблица

id  # primary key

device_id_fk 

auth # there's an index on it

old_auth  # there's an index on it

И следующий запрос.

$select_user = $this->db->prepare("
        SELECT device_id_fk 
        FROM wtb_device_auths AS dv 
        WHERE (dv.auth= :auth OR dv.old_auth= :auth) 
        LIMIT 1
");

объяснить, я не могу добраться до сервера основного клиента, но вот еще один клиент с меньше данных

enter image description here

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

Если вы удаляете индекс из auth, тогда запрос на выборку записывается в журнал медленных запросов, но не обновление, если вы добавляете индекс к device_id_fk, это не имеет значения.

Я попытался переписать запрос, используя объединение вместо или, но мне сказали, что процессор все еще остался, и запрос на выборку все еще записывается в журнал медленных запросов

$select_user = $this->db->prepare("
    (SELECT device_id_fk 
     FROM wtb_device_auths 
     AS dv WHERE dv.auth= :auth) 
    UNION ALL
    (SELECT device_id_fk 
     FROM wtb_device_auths AS dv 
     WHERE dv.old_auth= :auth)
     LIMIT 1"
    );
");

Объясните

enter image description here

Чаще всего это единственный запрос в журнале медленных запросов. Есть ли более оптимальный способ написания запроса? Есть ли более оптимальный способ добавления индексов? Клиент использует старую версию MariaDB, эквивалентную MYSQL 5.5, на сервере centos 6 под управлением LAMP

Дополнительная информация

enter image description here

enter image description here

Запрос на обновление, который регистрируется в журнале медленных запросов при добавлении индекса в auth, равен

$update_device_auth = $this->db->prepare("UPDATE wtb_device_auths SET auth= :auth WHERE device_id_fk= :device_id_fk");

Ответы [ 2 ]

1 голос
/ 15 февраля 2020

Ваши несколько индексов не должны замедлять ваши обновления.

Вам нужны два индекса, чтобы и ваше обновление, и выбор работали хорошо. Я думаю, у вас никогда не было обоих одновременно.

UPDATE wtb_device_auths SET auth= :auth WHERE device_id_fk= :device_id_fk

Вам нужен индекс на device_id_fk, чтобы это обновление работало хорошо. И независимо от его индекса он должен быть объявлен внешним ключом.

    SELECT device_id_fk 
    FROM wtb_device_auths AS dv 
    WHERE (dv.auth= :auth OR dv.old_auth= :auth) 
    LIMIT 1

Вам нужен один объединенный индекс на auth, old_auth, чтобы этот запрос работал хорошо.

Отдельные auth и old_auth индексы также должны работать хорошо, если не будет слишком много дубликатов. MySQL объединит результаты по индексам , и это объединение должно быть быстрым ... если не совпадает много строк.

Если вы также ищете только old_auth, добавьте index для old_auth.

И, как уже отмечали другие, запрос select может вернуть одно из нескольких соответствующих устройств с совпадающим auth или old_auth. Это наверное плохо. Если auth и old_auth предназначены для идентификации устройства, добавляет уникальное ограничение .


В качестве альтернативы вам необходимо реструктурировать данные. Несколько столбцов, содержащих одно и то же значение, помечены красным флагом. Это может привести к увеличению количества индексов, а также ограничить количество версий, которые вы можете хранить. Вместо этого используйте только одну аутентификацию на строку и разрешите каждому устройству иметь несколько строк.

create table wtb_device_auths (
  id serial primary key,
  device_id bigint not null references wtb_devices(id),
  auth text not null,
  created_at datetime not null default current_timestamp,

  index(auth)
);

Теперь вам нужно искать только один столбец.

select device_id from wtb_device_auths where auth = ?

Теперь одно устройство может иметь много Строки wtb_device_auths. Если вам нужна текущая авторизация для устройства, найдите новейшую.

select device_id
from wtb_device_auths
where device_id = ?
order by created_at desc
limit 1

Поскольку каждое устройство будет иметь только несколько аутентификаций, вероятно, это будет достаточно быстро с одним индексом device_id; сортировка нескольких строк для устройства будет быстрой.

Если нет, вам может понадобиться дополнительный комбинированный индекс, такой как created_at, device_id. Это включает в себя поиск и сортировку только по created_at, а также поиск и сортировку запросов по created_at и device_id.

0 голосов
/ 22 февраля 2020

OR обычно приводит к медленному сканированию полной таблицы. Этот трюк UNION вместе с соответствующими INDEXes намного быстрее:

( SELECT  device_id_fk
    FROM  wtb_device_auths AS dv
    WHERE  dv.auth= :auth
    LIMIT  1 )
UNION ALL
( SELECT  device_id_fk
    FROM  wtb_device_auths AS dv
    WHERE  dv.old_auth= :auth
    LIMIT  1 )
LIMIT 1

И имеют эти «составные» индексы:

INDEX(auth, device_id)
INDEX(old_auth, device_id)

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

Обратите внимание, что у меня было 3 LIMITs; у вас было только 1.

что UNION ALL включает временную таблицу. Вы должны обновить до 5.7 (как минимум); эта версия оптимизирует временную таблицу.

A LIMIT без ORDER BY дает случайную строку; это нормально?

Пожалуйста, предоставьте весь текст записи медленного журнала для этого одного запроса - в нем есть информация, которая может быть полезна. Если «Rows_examined» больше 2 (или, возможно, 3), то происходит что-то странное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...