事务隔离 - INSERTS依赖于先前的记录值

时间:2017-07-05 15:17:59

标签: sql sql-server transaction-isolation

这个问题与另一件事的讨论相关/来自: What is the correct isolation level for Order header - Order lines transactions?

想象一下我们有通常的Orders_Headers和Orders_LineItems表的场景。让我们说我们有一个特殊的业务规则说:

  1. 每个订单都有折扣字段,该字段根据最后一次订单输入的时间计算

  2. 如果在最后Y小时内有超过X个订单,则特别计算每个下一个订单折扣字段。

  3. 如果最后10个订单的平均频率高于每分钟x,则会特别计算每个下一个订单折扣字段。

  4. 每个下一个订单折扣字段都是专门计算的

  5. 这里要指出的是,每个订单都依赖于之前的订单,隔离级别至关重要。

    我们有一个事务(只是所显示代码的逻辑):

    BEGIN TRANSACTION
    
    INSERT INTO Order_Headers...
    
    SET @Id = SCOPE_IDENTITY()
    
    INSERT INTO Order_LineItems...(using @Id)
    
    DECLARE @SomeVar INT
    
    --just example to show selecting previous x orders
    --needed to calculate Discount value for new Order
    SELECT @SomeVar = COUNT(*) Order_Headers
    WHERE ArbitraryCriteria
    
    UPDATE Order_Headers
    SET Discount= UDF(@SomeVar)
    WHERE Id = @Id
    
    COMMIT
    
    END TRANSACTION
    

    我们还有另一笔交易来阅读订单:

    SELECT TOP 10 * FROM Order_Headers
    ORDER BY Id DESC
    

    问题

    1. 第一个交易的SNAPSHOT隔离级别和第二个适当级别的READ COMMITED是什么?

    2. 有没有更好的方法来接近CREATE / UPDATE事务,还是这样做?

2 个答案:

答案 0 :(得分:2)

快照的问题与插入/读取无关(我假设您决定使用)。关于更新,你应该关注。

快照隔离级别正在使用行版本控制。这意味着每次插入/更新/删除行时,这些行都会在tempdb(版本存储,这些行的位置)中重复,并使用版本控制标记将其大小增加14个字节,以便新启动的事务可以读取最后一个已提交事务的行。请记住,这些已调整大小的行将保持原样,直到重建索引为止。

这应该是一个指标,如果你的表真的很忙,你的索引会更快地进行碎片整理,它会给你的temp增加一定的%开销。所以请记住这一点。

正如我所提到的,更重要的是更新。

每次插入/删除/更新行时,您将获得对这些行的独占锁定(稍后的对象),并且由于您的快照使用行版本控制,因此从另一个事务中插入会在新行上添加独占锁定,并且这不是问题。但是,如果您尝试更新现有行并且会话2尝试获取该行上的X锁定,它将失败,因为会话1已经对其进行了X锁定,这是您将收到此消息的位置:< / p>

enter image description here

Read Committed和Serializable已经很好地解决了这些问题,因此您可能希望采用该方法并在实际实施之前测试所有解决方案。请记住,所有事务都会导致更新阻塞,快照/读取编辑快照将失败。

我个人会使用读取提交的快照和更改过程,在catch块中重新运行N次,但是嘿也有缺陷!

答案 1 :(得分:1)

serializable选项:

通过updlockserializable表提示使用悲观锁定策略来获取由where条件指定的键范围锁(由支持索引支持以仅锁定查询所需的范围):

declare @Id int, @SomeVar int;
begin tran;

  select @SomeVar = count(OrderDate) 
  from Order_Headers with (updlock,serializable) 
  where OrderDate >= '20170101';

  insert into Order_Headers (OrderDate, SomeVar)
    select sysdatetime(), @SomeVar;

  set @Id = scope_identity();

  insert into Order_LineItems (id,cols)
    select @Id, cols
    from @TableValuedParameter;

commit tran;

updlockserializable表提示使用select来锁定键范围的原因和方法的良好指南,以及为什么需要两者,都在{ {3}}

参考: