Насколько плохо использование SELECT MAX (id) в MYSQL вместо mysql_insert_id () в PHP? - PullRequest
12 голосов
/ 18 августа 2010

Справочная информация: я работаю в системе, в которой разработчики, похоже, используют функцию, которая выполняет запрос MYSQL, например "SELECT MAX(id) AS id FROM TABLE", всякий раз, когда им нужно получить идентификатор последней вставленной строки (таблица со столбцом auto_increment) .

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

"Oh okay, we'll only face this problem when we have 
(a) a lot of users, or 
(b) it'll only happen when two people try doing something
    at _exactly_ the same time"

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

Есть математическое понимание этого? Опять же, я ЗНАЮ, что это ужасная практика, я просто хочу понять переменные в этой ситуации ...


Обновление: спасибо за комментарии - мы движемся в правильном направлении и исправляем код!

Ответы [ 5 ]

5 голосов
/ 18 августа 2010

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

Мы не говорим об изменении однострочного вызова функции на монстра в 5000 строк, чтобы иметь дело с удаленно возможным краевым случаем. Мы говорим о том, чтобы сократить вызов до более читаемого и более корректного использования.

Я вроде бы согласен с @Mark Baker, что есть некоторая оценка производительности, но, поскольку id является первичным ключом, запрос MAX будет очень быстрым. Конечно, LAST_INSERT_ID() будет быстрее (поскольку он просто читает из переменной сеанса), но только на тривиальную величину.

И вам не нужно много пользователей, чтобы это произошло. Все, что вам нужно, это много одновременных запросов (даже не так много). Если время между началом вставки и началом выбора составляет 50 миллисекунд (при условии, что ядро ​​БД безопасно для транзакций), то вам нужно всего лишь 20 запросов в секунду, чтобы начать последовательно решать проблему с этим. Дело в том, что окно для ошибки нетривиально. Если вы говорите 20 запросов в секунду (что на самом деле не так уж много) и предполагаете, что средний человек посещает одну страницу в минуту, вы говорите только с 1200 пользователями. И это для того, чтобы это происходило регулярно. Это может произойти только с двумя пользователями.

И прямо из MySQL документации по теме :

You can generate sequences without calling LAST_INSERT_ID(), but the utility of 
using the function this way is that the ID value is maintained in the server as 
the last automatically generated value. It is multi-user safe because multiple 
clients can issue the UPDATE statement and get their own sequence value with the
SELECT statement (or mysql_insert_id()), without affecting or being affected by 
other clients that generate their own sequence values.
1 голос
/ 18 августа 2010

Вместо использования SELECT MAX(id) вы должны сделать, как указано в документации :

Вместо этого используйте внутреннюю функцию SQL MySQL LAST_INSERT_ID () в запросе SQL

Тем не менее, ни SELECT MAX(id), ни mysql_insert_id() не являются "потокобезопасными", и у вас все еще может быть состояние гонки.Лучший вариант - заблокировать таблицы до и после ваших запросов.Или даже лучше использовать транзакции.

0 голосов
/ 18 августа 2010

В дополнение к риску получения неправильного значения идентификатора, есть также дополнительные издержки запроса к базе данных SELECT MAX (id), и это на самом деле больше кода PHP, чем простой mysql_insert_id (). Зачем намеренно кодировать что-то медленное?

0 голосов
/ 18 августа 2010

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

По крайней мере, использование LAST_INSERT_ID () получит последний идентификатор для конкретного ресурса, поэтому не имеет значения, сколькомежду ними были добавлены новые записи.

0 голосов
/ 18 августа 2010

У меня нет математики для этого, но я хотел бы отметить, что ответ (а) немного глупо. Разве компания не хочет много пользователей? Разве это не цель ? Этот ответ подразумевает, что они скорее решат проблему дважды, возможно, с большими затратами во второй раз, чем решат ее один раз правильно в первый раз.

...