使用INSERT INTO选择更新

时间:2014-01-21 14:41:54

标签: mysql select insert transactions innodb

我正在制作某种游戏彩票,我有建议从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())

2 个答案:

答案 0 :(得分:11)

SELECT ... FOR UPDATE with UPDATE

使用与InnoDB的交易(自动提交已关闭),SELECT ... FOR UPDATE允许一个会话暂时锁定特定记录(或记录),以便其他会话无法更新它。然后,在同一事务中,会话实际上可以在同一记录上执行UPDATE并提交或回滚事务。这将允许您锁定记录,以便其他会话无法更新它,而您可能会执行其他业务逻辑。

这是通过锁定完成的。 InnoDB使用索引来锁定记录,因此锁定现有记录似乎很简单 - 只需锁定该记录的索引即可。

选择... FOR INSDATE更新

但是,要将SELECT ... FOR UPDATEINSERT一起使用,如何锁定尚不存在的记录的索引?如果您使用的默认隔离级别为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。