2014-01-21 5 views
3

У меня есть какая-то игровая лотерея, и у меня есть совет переключиться с MyISAM на InnoDB и начать использовать FOR UPDATE, поэтому лотерейный билет (от 1 до 16) может не продаются более одного раза.ВЫБЕРИТЕ ДЛЯ ОБНОВЛЕНИЯ с INSERT INTO

Теперь мне интересно, как это работает FOR UPDATE.

Я видел в Интернете, что это что-то вроде:

SELECT * FROM [table] WHERE [column] = [value] FOR UPDATE 
UPDATE [table] SET [column] = [new_value] 

Мой вопрос, однако в отношении следующее, будет также работать на INSERT?

Мой дизайн базы данных:

CREATE TABLE IF NOT EXISTS `Lottery` (
    `id` varchar(50) NOT NULL, 
    `preferedLotteryId` varchar(50) NOT NULL, 
    `winningTicketId` int(11) NOT NULL DEFAULT '-1', 
    `createdOn` datetime NOT NULL DEFAULT '1001-00-00 00:00:00', 
    `startedOn` datetime NOT NULL DEFAULT '1001-00-00 00:00:00', 
    `finishedOn` datetime NOT NULL DEFAULT '1001-00-00 00:00:00', 
    `active` tinyint(1) NOT NULL, 
    `deliveredOn` datetime NOT NULL DEFAULT '1001-00-00 00:00:00', 
    `preferedDeliverMethod` int(2) NOT NULL DEFAULT '-1', 
    `deliveredMethod` int(2) NOT NULL DEFAULT '-1', 
    `deliveredByAccountId` varchar(50) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

CREATE TABLE IF NOT EXISTS `LotteryBid` (
    `bidId` varchar(50) NOT NULL, 
    `accountId` varchar(50) NOT NULL, 
    `auctionId` varchar(50) NOT NULL, 
    `ticketId` int(50) NOT NULL, 
    `datetime` datetime NOT NULL DEFAULT '1001-00-00 00:00:00', 
    PRIMARY KEY (`bidId`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

Что означает эта конструкция потребовало бы FOR UPDATE подобное:

SELECT * FROM Lottery WHERE id = [id] FOR UPDATE 
INSERT INTO LotteryBids SET [LotteryBids.values & Lottery.id] 

или

SELECT * FROM Lottery WHERE id = [id] FOR UPDATE 
INSERT INTO LotteryBids, Lottery SET [LotteryBids.values] WHERE Lottery.id = [id] 

Однако, я понятия не имею ли таким образом из FOR UPDATE было бы даже немного возможно.

Я довольно новичок в этом массивном взаимодействии данных, и я понятия не имею, с чего начать.

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

Уважением, Larssy1


Я предполагаю, что следующие мысли переведет к возможному решению:

Мысль 1:

Пользователь покупает лотерейный билет с билет id 5. В настоящее время у этой лотереи есть 3 проданных билетов, поэтому я хочу заблокировать количество строк для этой лотереи , пока я не ave совершил/завершил эту вставку.

Мысль 2:

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


Используемые запросы

Выберите лотерею (в том числе дополнительной информации):

SELECT au.*, asp.* FROM Lottery au, LotteryPrefered asp 
WHERE au.preferedAuctionId = asp.id AND au.id = '" . $_auctionId . "' 

ИЛИ (за исключением дополнительной информации)

SELECT au.* FROM Lottery au 
WHERE au.id = '" . $_auctionId . "' 

Приобретите клеща и др:

INSERT INTO LotteryBids (bidId, accountId, auctionId, ticketId, datetime) 
VALUES ('" . $guid . "', '" . $_accountId . "', '" . $_auctionId . "', 
    '" . $_ticketId . "', NOW()) 
+0

Я думаю, что ваши инструкции 'INSERT' являются псевдокодами, но что будет делать предложение WHERE в инструкции' INSERT'? –

+0

Это в значительной степени только для того, чтобы дать небольшой «стук» в «FOR UPDATE», что произошло «обновление». Но я понятия не имею, как это работает. – larssy1

ответ

8

SELECT ... FOR UPDATE с UPDATE

Использование транзакций с InnoDB (автофиксация выключен), SELECT ... FOR UPDATE позволяет одной сессии временно заблокировать конкретную запись (или записи), чтобы ни одна другая сессия не могла его обновить. Затем в той же транзакции сеанс может фактически выполнить UPDATE на той же записи и совершить или отменить транзакцию. Это позволит вам заблокировать запись, чтобы ни один другой сеанс не мог ее обновить, в то время как, возможно, вы делаете другую бизнес-логику.

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

SELECT ... FOR UPDATE с INSERT

Однако использовать SELECT ... FOR UPDATE с INSERT, как вы фиксируете индекс для записи, которая еще не существует? Если вы используете уровень изоляции по умолчанию REPEATABLE READ, InnoDB также будет использовать зазор замки. Пока вы знаете id (или даже диапазон идентификаторов) для блокировки, тогда InnoDB может блокировать разрыв, поэтому никакая другая запись не может быть вставлена ​​в этот промежуток, пока мы не закончим с этим.

Если id колонки были колонки автоинкрементируемого, то SELECT ... FOR UPDATE с INSERT INTO будет проблематично, потому что вы не знаете, что новый id был до тех пор, пока она установлена. Однако, так как вы знаете id, который хотите вставить, SELECT ... FOR UPDATE с INSERT будет работать.

Пример

Смотрите, если ticketid5 доступен для аукциона 12:

SELECT * FROM LotteryBids 
WHERE auctionid = 12 AND ticketid = 5 
FOR UPDATE 

Если ни одна строка не возвращается, то он не существует. Вставьте его:

INSERT INTO LotteryBids (bidId, accountId, auctionId, ticketId, datetime) 
VALUES ('abcd-efgh-ijkl-mnop', 100, 12, 5, NOW()) 

Держите его просто

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

+0

Вижу, спасибо. Столбец ID не является автоматическим приращением, потому что это varchar, который содержит GUID, который создается с использованием класса PHP. Если бы я дал вам соответствующие данные, не могли бы вы создать мне пример такого 'FOR UPDATE'? Я знаю, что веб-сайт не используется в первую очередь для примеров, готовых для случая, «пусть сообщество создаст ваше программное обеспечение». Но пример, на котором я мог бы продвинуться вперед, был бы высоко оценен. Так что, если вы захотите, вам будет достаточно проверить данные? - вопрос также обновлен – larssy1

+0

Я еще не понимаю, что подразумевает «покупка лотерейного билета». Если вы добавите инструкции insert, я увижу, могу ли я продемонстрировать инструкции SELECT ... FOR UPDATE. –

+0

@MarcusAdams Ваш ответ в точности прав. Я мог бы добавить еще одну заметку о том, что «пробел» в этом случае * заблокирован *, поэтому будьте уверены, это то, что вы действительно хотите. Вы не сможете вставлять данные с любым другим значением во всем диапазоне, что может быть большой частью таблицы. Например, в таблице с 1, 2, 3, 4, 5, и вы блокируете 10, она фактически блокирует диапазон от 5 -> supremum, что означает, что вы не можете заблокировать любую клавишу> = 5. Это часто видно с добавочными клавишами. С идентификаторами GUID/UUID вы должны блокировать в основном произвольно большую часть таблицы. – jeremycole

0

Вы можете попробовать использовать тип блокировки/мьютекс с альтернативной таблицей: см http://blog.udby.com/archives/14 (Реализация простой семафор базы данных)

Перед началом работы над данным типом, вы блокируете «тип» с вкладышем + УДАЛИТЬ в таблице блокировки.