我有一个航班预订程序使用mssql ,对于预订航班,我想确定我应该使用隔离级别还是锁?
(这是一个示例代码,我的问题是这种情况的隔离级别不做预订)
我的数据库有一个库存表,如:
Inventory Table
------------------------
id (Pk),
FlightNumber,
Total,
Sold
现在如果有人想预订航班,我会在交易中使用此代码
Decalre @total int;
Decalre @sold int;
Select @total=Total,@sold=Sold From Inventory where FlightNumber='F3241b';
IF @total-@sold > 0
BEGIN
Update inventory set Sold=Sold+1 where FlightNumber='F3241b';
PRINT 'Reserve Complete'
END
ELSE
PRINT 'this flight is full'
我有这些问题:
Q1:我应该使用锁定还是隔离级别?使用锁定或隔离级别是否有任何好处?
Q2:根据Q1我应该使用哪种隔离级别或锁定
答案 0 :(得分:4)
如果您正在寻找哪种隔离级别可以使示例代码按原样运行,而不是解决示例代码所解决问题的最佳方法,那么您至少需要保证REPEATABLE READ
使用严格的两阶段锁定(S2PL)进行并发的数据库允许READ COMMITTED事务在每个语句完成时丢弃共享锁,甚至更早,因此在事务A检查可用性和声明席位的时间之间,其他人可以使用事务B并再次读取,而不会导致任何事务失败。交易A可能会暂时阻止交易B,但两者都会更新,您可能会被过度销售。
在使用多版本并发控制(MVCC)进行并发的数据库中,读取不阻止写入和写入不会阻止读取。在READ COMMITTED,每个语句根据已提交的内容使用数据库的新快照,并且至少在某些情况下(我知道在PostgreSQL中也是如此),并发写入的解决没有错误。因此,即使事务A正在更新已售出的计数,或者已经这样做而未提交,事务B也会看到旧计数并继续更新。当它尝试更新时,它可以阻止等待先前的更新,但是一旦提交,它将找到该行的新版本,检查它是否符合选择标准,如果是,则更新,如果没有则忽略该行,并且继续提交没有错误。所以,再次,你是过度销售。
如果您选择使用事务隔离,我猜这会回答Q2。通过修改示例代码以获取显式锁定,可以在较低的隔离级别解决该问题,但这通常会导致使用隔离级别更严重的阻塞,该隔离级别足以自动处理它。
答案 1 :(得分:2)
你的事情过于复杂。您的所有查询都可以替换为:
Update inventory
set Sold = Sold + 1
where FlightNumber = 'F3241b'
AND Total - Sold > 0 -- Important!
如果航班已满,则不会发生更新(不符合第二个条件),它将返回0
个已修改的行。如果是这种情况则表示航班已满。否则,查询会修改Sold
值并返回1
已修改的行。
在这种情况下,任何隔离级别都可以,因为单个查询始终是原子的。这有点类似于optimistic-locking。
BTW此查询可以轻松调整,以允许以原子方式进行任意数量的预订:
Update inventory
set Sold = Sold + @seats
where FlightNumber = 'F3241b'
AND Total - Sold >= @seats
答案 2 :(得分:0)
请参阅此链接以解释SQL Server中的SNAPSHOT ISOLATION级别。 http://msdn.microsoft.com/en-us/library/ms345124(v=sql.90).aspx
他们谈论汽车租赁应用程序。
如果您需要更严格的隔离级别,可以移至IsolationLevel Serializable。但请注意,这很容易锁定并可能影响您的表现。