Sql 事务隔离-根据以前的记录值插入

Sql 事务隔离-根据以前的记录值插入,sql,sql-server,transaction-isolation,Sql,Sql Server,Transaction Isolation,这个问题与另一件事有关/来自讨论: 想象一下这样一个场景:我们有普通的Orders\u标题和Orders\u LineItems表。我们也可以说,我们有一个特殊的业务规则,其中规定: 每个订单都有折扣字段,该字段根据从上次输入的订单经过的时间计算 如果在过去Y小时内有超过X个订单,则专门计算每个下一个订单折扣字段 如果最近10个订单的平均频率高于每分钟x,则专门计算每个下一个订单折扣字段 每个下订单折扣字段都是专门计算的 这里要说明的是,每个订单都依赖于以前的订单,隔离级别至关重要 我们有一个

这个问题与另一件事有关/来自讨论:

想象一下这样一个场景:我们有普通的Orders\u标题和Orders\u LineItems表。我们也可以说,我们有一个特殊的业务规则,其中规定:

  • 每个订单都有折扣字段,该字段根据从上次输入的订单经过的时间计算

  • 如果在过去Y小时内有超过X个订单,则专门计算每个下一个订单折扣字段

  • 如果最近10个订单的平均频率高于每分钟x,则专门计算每个下一个订单折扣字段

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

  • 这里要说明的是,每个订单都依赖于以前的订单,隔离级别至关重要

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

    我们还有另一个用于读取订单的事务:

    SELECT TOP 10 * FROM Order_Headers
    ORDER BY Id DESC
    
    问题

  • 第一个事务的快照隔离级别和第二个事务的读取提交级别是否合适

  • 是否有更好的方法来处理创建/更新事务,或者这是一种方法


  • 快照的问题不在于插入/读取(我假设您决定使用它)。这是关于更新的,你应该关注它

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

    这应该是一个指标,如果您的表真的很忙,那么您的索引将更快地进行碎片整理,这将在您的临时表上增加一定数量的%开销。所以请记住这一点

    正如我提到的,这里更令人担忧的是更新

    无论何时插入/删除/更新行,都会在这些行上获得排他锁(稍后为对象),并且由于快照正在使用行版本控制,因此从另一个事务进行的插入会在新行上添加排他锁,这不是问题。但是,如果您尝试更新现有行,而会话2尝试获取该行上的X锁,它将失败,因为会话1上已经有X锁,您将在此处收到以下消息:

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


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

    可序列化的
    选项:

    通过
    updlock
    serializable
    表提示使用悲观锁定策略,获取由
    where
    条件指定的密钥范围锁(由支持索引支持,仅锁定查询所需的范围):

    中介绍了使用
    updlock
    serializable
    表提示以
    select
    锁定密钥范围的原因和方法,以及为什么需要这两种方法

    参考:


    不要表现得粗鲁。。。您的数据库是否设置为允许快照隔离?你知道那是什么吗?我不知道你对此了解多少,也不知道你在谷歌搜索答案时是否看到过这个关键词。是,已将其设置为允许快照隔离。我知道那是什么。不是专家,但我知道它背后的基本思想。这正是为什么我要问如何正确地做到这一点的原因,因为我从理论上理解了每个隔离级别的含义,但没有像这样的实例的真实世界经验,也不知道不同方法的所有警告。希望这有意义?如果没有,请指出什么是没有意义的,以便我可以进一步澄清。在阅读了您对上一个问题的评论后,我建议您在该帖子上回复@DanGuzman,并附上此问题的链接,以便让他有机会进一步阐述自己的评论。谢谢您的建议。另外,如果你对这个问题有你的想法/解决方案,我很想听听。谢谢你的回答。首先,我要说的是我还没有决定什么。在我看来,由于幻象读取,我无法绕过快照/序列化隔离级别。如果有其他的方法,我很想听听。此外,我不确定我是否正确阅读了您的答案,但您似乎不喜欢快照的方向(我的印象可能在这里是错误的),我不清楚为什么。有一件事我没有提到,在讨论中的表上,选择的数量级比插入/更新的数量级多很多。我不认为他们插入,使它短。如果您使用snapshot并同时运行其中的两个事务,那么稍后一个事务将失败,您将得到一个错误。我建议使用read committed snapshot,这与read committed不同,它没有幻象读取,它将导致死锁,您可以通过重新运行它来解决它,而不是获得错误。或者,您可以简单地使用serializable并防止任何此类情况发生(但并发性较低)。非常感谢Sam Saffron的upsert read!这么多有用的信息在相当短的文章。相对于死锁,UPDLOCK看起来确实是更安全的解决方案。我认为这是我将使用的方向。还感谢您在回答中添加更多参考资料!如果丹·古兹曼(Dan Guzman)在之前的评论中添加了一些内容,我也会等一等,但到目前为止,你的
    SELECT TOP 10 * FROM Order_Headers
    ORDER BY Id DESC
    
    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;