Подтвердить что ты не робот

Насколько плохо используется SELECT MAX (id) в MYSQL вместо mysql_insert_id() в PHP?

Справочная информация. Я работаю над системой, в которой разработчики, похоже, используют функцию, которая выполняет запрос 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"

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

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


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

4b9b3361

Ответ 1

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

Не похоже, что мы говорим об изменении вызова одной линии в монстра 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.

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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