我在制作中有某种游戏彩票,我有建议从myisam切换到InnoDB,然后开始使用FOR UPDATE
,因此彩票(范围为1-16))不能多次出售。
现在我想知道,此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的彩票。目前 彩票售出3张门票,所以我想为此锁定行计数 彩票直到我承诺/完成此插入为止。
思想2:
用户正在购买带有票证ID的彩票。目前 拍卖没有机票ID 5的机票,所以我想保留一个条目 对于此拍卖,具有类似的拍卖ID和票证ID。和 防止进一步添加,直到此查询完成为止。
用过的查询
选择彩票(包括其他信息):
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())
选择...用于更新
使用InnoDB的交易(Auto-Commit关闭),SELECT ... FOR UPDATE
允许一个会话暂时锁定特定的记录(或记录),以便没有其他会话可以对其进行更新。然后,在同一事务中,会话实际上可以在同一记录上执行UPDATE
,并提交或滚动交易。这将允许您锁定记录,以便在您执行其他业务逻辑时无法更新其他会话。
这是通过锁定完成的。InnoDB利用索引锁定记录,因此锁定现有记录似乎很容易 - 简单地锁定该记录的索引。
选择...用于使用insert
更新但是,要将SELECT ... FOR UPDATE
与INSERT
一起使用,您如何锁定尚不存在的记录索引?如果您使用的是REPEATABLE READ
的默认隔离级别,InnoDB也将使用 GAP 锁。只要您知道要锁定的id
(甚至ID范围),InnoDB就可以锁定差距,以便在我们完成之前都无法将其他记录插入该差距。
如果您的id
列是自动报重列,则使用INSERT INTO
的SELECT ... FOR UPDATE
是有问题的,因为您不知道新的id
在插入它之前是什么。但是,由于您知道要插入的id
,因此使用INSERT
的SELECT ... FOR UPDATE
将起作用。
示例
查看ticketid
5
是否可用于拍卖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())
保持简单
以这种方式处理并发问题可能很难理解,而且我经常看到人们过于复杂的事情。我建议问一个单独的问题,详细说明您要完成的工作,提供建议的解决方案并提出建议。
您可以尝试使用替代表的一种锁定/互惠件:请参阅http://blog.udby.com/archives/14(实现简单数据库信号量)
在开始使用给定类型之前,您在锁定表中使用插入 删除的"类型"锁定。