我有一个许可证密钥/序列号的sql server表。 表结构就像;
[
RecordId int,
LicenceKey string,
Status int (available, locked, used, expired etc.)
AssignedTo int (customerId)
....
]
通过我的ASP.NET应用程序,当用户决定购买许可证时单击“接受”按钮,我需要为用户保留许可证密钥。 我的方法就像, 从KeysTable中选择top 1 licenceKey Where Status = available 更新KeysTable设置状态=已锁定 然后将密钥返回给应用程序。
我担心的是,如果两个asp.net线程访问相同的记录并返回相同的licencekey。 你认为做这种作业的最佳做法是什么?是否有一个众所周知的方法或这种问题的模式? 如果需要,可以在哪里使用lock()语句?
我正在使用Sql Server 2005,存储过程进行数据访问,DataLayer是BusinessLayer和Asp.Net GUI。
由于
答案 0 :(得分:4)
在这种情况下,可能不需要使用显式锁或事务。
在存储过程中,您可以使用OUTPUT
语句中的UPDATE
子句在单个原子操作中更新表并检索许可证密钥。
这样的事情:
UPDATE TOP (1) KeysTable
SET Status = 'locked'
OUTPUT INSERTED.LicenseKey
-- if you want more than one column...
-- OUTPUT INSERTED.RecordID, INSERTED.LicenseKey
-- if you want all columns...
-- OUTPUT INSERTED.*
WHERE Status = 'available'
答案 1 :(得分:1)
要实现您所说的内容,您需要使用可序列化的事务。为此,请遵循以下模式:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
--Execute select
--Execute update
COMMIT TRANSACTION
但是,为什么你有一张包含所有可能许可证密钥的表格?为什么不使用密钥生成算法,然后在用户购买时创建新密钥?
答案 2 :(得分:0)
我认为你应该在你查询它的同一个存储过程中将密钥标记为不可用,否则总是存在某种竞争条件。手动锁定表不是一个好习惯恕我直言。
如果您有两个阶段的流程(例如预订机票),您可以引入一个在指定时间段内保留密钥的概念(例如30分钟),这样当您查询新密钥时,保留它。
编辑:如果你可以保证只有一个进程会改变数据库,那么锁定业务逻辑可能会有效,但是在数据库级别上更好,最好是在一个存储过程中。要正确地执行此操作,您必须设置事务级别并在数据库中使用事务,就像@Adam Robinson在他的回答中所建议的那样。答案 3 :(得分:0)
除了事务之外,您还可以尝试使用锁(在SQL中),以验证一次只有一个线程可以访问。
我相信应用程序锁可能会有所帮助。