Mysql : производительность обновления с первичным ключом и дополнительными ключами будет ли она медленнее?

#mysql #sql-update #where-clause

Вопрос:

Допустим, у меня есть таблица следующего вида

 CREATE TABLE `Foo` (
  `id` int(10) unsigned NOT NULL,
  `bar1` int(10) unsigned NOT NULL,
  `bar2` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
);
 

И у меня есть два вопроса:

 update Foo set bar1=10 where id=5000;
 
 update Foo set bar1=10 where id=5000 and bar1=0;
 

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

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

Комментарии:

1. добавьте и проиндексируйте идентификатор и строку 1, чтобы сделать ее едва заметной.

2. Я предполагаю, что второй запрос не будет выполняться медленнее первого запроса , если все строки, подлежащие обновлению по 1-му запросу, совпадут bar1=0 , то оба запроса займут примерно одинаковое время (я сомневаюсь, что разница может быть обнаружена). Если нет, то 2 — й запрос выполняется быстрее-просто из-за меньшего количества записываемых строк. Но id это PK, будет обновлена только одна строка, и разницы нет — дополнительное сравнение слишком быстрое, чтобы его можно было обнаружить.

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

4. Спасибо всем вам за подтверждение.

5. @nbk — INDEX(id, bar1) замедлил бы это ! Это связано с тем, что индекс необходимо обновить после успешного обновления.

Ответ №1:

  1. Найдите нужный ряд. Оптимизатор посмотрит на возможные индексы (только PK) и решит начать с id=5000 них . Существует не более одного такого ряда.
  2. (для второго случая) убедитесь в этом bar1=0 . Если нет, то запрос завершен.
  3. Проверьте, есть ли что-нибудь, что нужно изменить- bar1 уже установлено значение 10? Если да, то заканчивайте.
  4. Выполните работу по обновлению-это включает сохранение существующей строки в случае a ROLLBACK , предварительное сохранение нового значения и т. Д. И т. Д. — Этот шаг, вероятно, будет самым дорогостоящим шагом.

Шаг 2-это единственное различие, и это довольно маленький шаг. Об этом не стоит беспокоиться, когда дело доходит до производительности.

С другой стороны, Шаг 2 означает, что два обновления отличаются друг от друга-Что должно произойти, если bar1=4567 ? Первое обновление изменило бы его, но второе-нет.

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

 BEGIN;
SELECT ... WHERE id = 5000  FOR UPDATE;
decide what to do -- which might include ROLLBACK and exit
UPDATE Foo SET bar1=10 WHERE id = 5000;
COMMIT;
 

Итог: Используйте транзакции, а не дополнительный код, для работы с параллелизмом.

Предостережение: Транзакция должна быть «быстрой» (менее нескольких секунд). Если вам нужна «длинная» транзакция (например, «корзина покупок», на завершение которой может потребоваться несколько минут), необходим другой механизм. Если вам нужна длительная транзакция, начните с нового вопроса, объясняющего ситуацию. (В текущем вопросе обсуждается производительность одного обновления.)

Комментарии:

1. Спасибо вам за подтверждение. На самом деле я использовал транзакцию, но она начинается после выбора, чтобы минимизировать время ожидания сервера. Я думал, что результат будет таким же, но я думаю, что исключение select из транзакции и добавление дополнительного обновления для регистрации приведет к более быстрому отклику, когда частота доступа очень высока. Действительно ли мое предположение верно?

2. @cr001 — Если вам нужно выполнить какую-либо логику, прежде чем принимать решение об обновлении, вам нужна транзакция. Если вы можете выполнить обновление вслепую, то достаточно автоматической фиксации=ON. Я поднял вопрос о транзакции, потому что вы упомянули Выбор перед обновлением. Обратите внимание, что FOR UPDATE это необходимо.

3. Разве это не заблокировало бы таблицу, когда вы выполняете выбор (так что другим людям придется ждать блокировки, когда начнется выбор)? Я предполагаю, что если я сделаю выбор вне транзакции, то при обновлении нужно будет только дождаться блокировки, и, следовательно, время ожидания можно сэкономить. Разве это не так на самом деле?

4. @cr001 — С ENGINE=InnoDB , строка заблокирована, а не вся таблица . Если два соединения блокируют один и тот же ряд, вам нужна блокировка; это того стоит. Если Выбор и Обновление находятся в отдельных транзакциях, кто-то может проникнуть между ними.

5. Действительно, я предполагал, что весь стол будет заперт. Большое вам спасибо за разъяснение.