mysql 5.7 create TEMPORARY table
Разрабатываю приложение в котором один из модулей должен выбрать свободные ресурсы из пула.
Для простоты будем считать что надо забронировать билеты в кинотеатре и надо избежать повторной продажи билетов.
Запросы при этом идут(или могут идти) параллельно
Вот работающий код, который решает эту задачу. Собственно использована инструкция select for update из документации.
Мне надо бронировать по 3 билета - поэтому понадобилась временная таблица. Поскольку запросы идут паралельно - то я не могу написать просто create TEMPORARY table T1 потому что T1 будет доступна и для второго запроса.
На выходе возвращаются забронированные места.
Поэтому пришлось городить на ровном месте такой не читаемый код.
Собственно ВОПРОС: можно ли что то придумать чтобы улучшить читаемость кода ? Например в MySQL 8 появились переменные типа table которые эту проблему решают
create table ticket_test
(
seat int primary key,
count int default 0 null
);
Будем считать что count отображает занято место или нет (т.е. он должен быть только 0 или 1)
DELIMITER //
CREATE PROCEDURE testSelForUpdate2()
BEGIN
SELECT CONCAT('gayrat_tmp_',REPLACE(TIME(NOW()),':','_')) INTO @tbl;
SET @str=CONCAT("create TEMPORARY table ",@tbl," (seat int)");
PREPARE stmt FROM @str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
START TRANSACTION;
SET @str=CONCAT("INSERT INTO ",@tbl," (SELECT seat FROM ticket_test where count=0 limit 3 for update)");
PREPARE stmt FROM @str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DO SLEEP(10);
SET @str=CONCAT("update ticket_test inner join ",@tbl," TT on ticket_test.seat = TT.seat set count=count+1;");
PREPARE stmt FROM @str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
SET @str=CONCAT("select * from ",@tbl);
PREPARE stmt FROM @str;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
Ответы (1 шт):
Вместо temporary table надо применить обычную таблицу вида:
create table if not exists ggvlasey_avito.ticket_test_tmp
(
jobId varchar(50) null,
seat int not null,
timestamp timestamp default CURRENT_TIMESTAMP null
);
Для каждого запроса при бронировании места создаем случайную переменную
select HEX(RANDOM_BYTES(5)) as uuid4 into curUUID;
Так как сверху я написал Mysql 5.7 - то встроенная функциия UUID() работает на основе времени и чисто теоретически два запроса могут прийти в один и тот же момент времени - в этом случае UUID() будет иметь одинаковое значение - например если Вы запустите такой запрос - то значения будут совершенно одинаковыми
select UUID(),UUID()
Запрос select HEX(RANDOM_BYTES(5)) ,HEX(RANDOM_BYTES(5)) выдаст разные значения
На SO попался код в котором предлагают применить функцию sha2() - но здесь она совершенно лишняя - так как просто вычисляет хеш - но не генерирует случайное число.
Обновленная версия процедуры выглядит так. DO SLEEP(10) исползуется для того чтобы я успел запустить запрос в другой консоли.
DELIMITER //
CREATE PROCEDURE testSelForUpdate3()
BEGIN
DECLARE curUUID varchar(60);
select HEX(RANDOM_BYTES(5)) as uuid4 into curUUID;
# удалить старые записи из временной таблицы
delete from ticket_test_tmp where timestamp < timestamp(DATE_SUB(NOW(), INTERVAL 5 MINUTE));
START TRANSACTION;
INSERT INTO ticket_test_tmp( jobId, seat)
(SELECT curUUID, seat FROM ticket_test where count=0 limit 3 for update);
DO SLEEP(10);
update ticket_test
inner join ticket_test_tmp TT
on ticket_test.seat = TT.seat and TT.jobId=curUUID
set count=count+1;
COMMIT;
select seat from ticket_test_tmp where jobId=curUUID;
END //
DELIMITER ;