Если вы DECLARE counter
, то вам НЕ следует использовать @counter
для ссылки на переменную.В хранимой функции MySQL объявленные переменные не имеют @
сигил.Переменные с символом @
являются определяемыми пользователем переменными .@counter
- это переменная, отличная от counter
, даже если они имеют одинаковое написание.
Безопасно ли это делать в многопоточном приложении?Конечно, если потоки увеличивают разные строки, используя разные значения id
, они не будут конфликтовать (при условии, что id
является уникальным ключом таблицы t
).
Даже если несколько потоков используют один и тот жеЗначение id
, и, следовательно, необходимо увеличить ту же строку в таблице t
, что произойдет, если вы попадете туда первым, получите блокировку строки и увеличите ее.Второй поток будет ожидать получения собственной блокировки, пока первый поток не завершит свою транзакцию.Затем второй поток продолжится и увидит увеличенное значение c
.
Так что это безопасно, но не позволит высокую пропускную способность.Потоки, которые находятся в конфликте за один и тот же id
, должны будут стоять в очереди и ждать, пока текущий держатель блокировки не завершит свою транзакцию.Если вы ожидаете, что несколько потоков будут выполнять свою работу параллельно, вы обнаружите, что это становится узким местом.
Вот почему существует AUTO_INCREMENT
, потому что он ненадолго блокирует счетчик для генерации нового значения, ноон немедленно снимает блокировку, не дожидаясь завершения транзакции вызывающей стороны.Это позволяет параллельным потокам продолжать работать и не ждать друг друга.
Нельзя смоделировать поведение AUTO_INCREMENT
с UPDATE
операциями, которые обязательно находятся в транзакциях.
Re ваши комментарии:
Извините, я забыл, что :=
не работает с локальными объявленными переменными.Я проверил его и нашел две альтернативы:
Альтернатива 1: не пытайтесь объявить локальную переменную, просто используйте пользовательскую переменную.
CREATE FUNCTION NEXT_ID(id INT)
RETURNS VARCHAR(15)
READS SQL DATA
BEGIN
UPDATE t SET c = (@counter := c +1) WHERE ID = id;
RETURN @counter;
END
Альтернатива 2: используйтелокальная переменная, но установите LAST_INSERT_ID()
в увеличенное значение.Затем SET
локальная переменная счетчика с этим значением.
CREATE FUNCTION NEXT_ID(id INT)
RETURNS VARCHAR(15)
READS SQL DATA
BEGIN
DECLARE counter BIGINT DEFAULT 0;
UPDATE t SET c = LAST_INSERT_ID(c +1) WHERE ID = id;
SET counter = LAST_INSERT_ID();
RETURN counter;
END;
Я протестировал обе альтернативы, и они работают.