我正在制作某种游戏彩票,我有建议从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 5的彩票。目前这个 彩票已售出3张票,所以我想为此锁定行数 彩票,直到我提交/完成此插入。
思想2:
用户正在购买带有机票ID 5的彩票。目前这个 拍卖没有票号为5的票,所以我想保留一个条目 对于具有类似拍卖ID和票证ID的拍卖。和 在此查询完成之前阻止进一步添加。
二手查询
选择抽奖(包括其他信息):
SELECT au.*, asp.* FROM Lottery au, LotteryPrefered asp
WHERE au.preferedAuctionId = asp.id AND au.id = '" . $_auctionId . "'
OR(不包括其他信息)
SELECT au.* FROM Lottery au
WHERE au.id = '" . $_auctionId . "'
购买机票:
INSERT INTO LotteryBids (bidId, accountId, auctionId, ticketId, datetime)
VALUES ('" . $guid . "', '" . $_accountId . "', '" . $_auctionId . "',
'" . $_ticketId . "', NOW())
答案 0 :(得分:11)
SELECT ... FOR UPDATE with UPDATE
使用与InnoDB的交易(自动提交已关闭),SELECT ... FOR UPDATE
允许一个会话暂时锁定特定记录(或记录),以便其他会话无法更新它。然后,在同一事务中,会话实际上可以在同一记录上执行UPDATE
并提交或回滚事务。这将允许您锁定记录,以便其他会话无法更新它,而您可能会执行其他业务逻辑。
这是通过锁定完成的。 InnoDB使用索引来锁定记录,因此锁定现有记录似乎很简单 - 只需锁定该记录的索引即可。
选择... FOR INSDATE更新
但是,要将SELECT ... FOR UPDATE
与INSERT
一起使用,如何锁定尚不存在的记录的索引?如果您使用的默认隔离级别为REPEATABLE READ
,InnoDB还将使用 gap 锁。只要您知道要锁定的id
(或甚至是id的范围),InnoDB就可以锁定间隙,因此在我们完成之前,不能在该间隙中插入其他记录。
如果您的id
列是自动增量列,那么带有SELECT ... FOR UPDATE
的{{1}}会出现问题,因为您不知道新的INSERT INTO
是什么插入它。但是,由于您知道要插入的id
,因此id
SELECT ... FOR UPDATE
将有效。
示例强>
查看INSERT
ticketid
是否可以拍卖5
:
12
如果没有返回任何行,则它不存在。插入:
SELECT * FROM LotteryBids
WHERE auctionid = 12 AND ticketid = 5
FOR UPDATE
保持简单
以这种方式处理并发问题可能很难理解,而且我经常看到人们过于复杂。我建议问一个单独的问题,详细说明你要完成的工作,提出你提出的解决方案,并征求建议。
答案 1 :(得分:0)
你可以尝试使用一种带有备用表的锁/ MUTEX: 请参阅http://blog.udby.com/archives/14(实现简单数据库信号量)
在开始处理给定类型之前,您可以锁定"类型"在锁定表中使用INSERT + DELETE。