多个存储过程如何同时更新同一行?

Posted

技术标签:

【中文标题】多个存储过程如何同时更新同一行?【英文标题】:How can multiple Stored Procedures update same row at same time? 【发布时间】:2010-08-07 02:49:29 【问题描述】:

我正在使用 SSIS 2008 在控制流中并行执行多个存储过程。 每个 SP 都应该最终更新表中的 1 行。

需要注意的一点是,每个 SP 都有明确的职责,只更新特定的列。

保证不同的SP不会更新彼此的列。因此要更新的列在各个 SP 之间划分,但根据设计,每个 SP 最终都应该在同一行上工作。

目前我的一些 SP 由于死锁而出错。我猜这可能是因为其他 SP 锁定了该行?

我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

您必须承认,这似乎是一件非常不寻常的事情。我想知道更新单独的表是否会更好,然后在最后有一个更新语句将各个表连接到最后一个表? (即update a set a.[1] = ... from a inner join b inner join c 等)。

但是,如果您想继续沿着这条路走下去,那么只需在每个存储过程中设置 READ UNCOMMITTED。这是最好的选择。

【讨论】:

我想,从长远来看,我会建议我们的团队将 Sp 的输出存储在不同的表中,然后再将其合并,即使这意味着更高的 IO 流量由每个 SP 临时读取/写入新表。【参考方案2】:

死锁可能不仅仅是因为另一个 SP 锁定了该行。在这种情况下,第一个过程将等到锁定 SP 释放锁定。这并不是说您的多个程序不会导致问题。不过还有更多内容。

您可能需要进行一些返工以避免该问题,但首先您应该了解有关死锁情况的更多信息。我怀疑您锁定了正在更新的行以外的对象。

有一些方法可以收集有关死锁的更多信息。这是link,您可以在其中了解死锁的详细信息。

【讨论】:

【参考方案3】:

简单:确保您不会将锁留在任何地方。这适用于由连接确定的事务隔离。通过适当的事务隔离,不会有锁,所以不会出现死锁。

更新不是问题。它从 READS 开始。转到 READ UNCOMMITED 以确保您不会在读取时留下锁,和/或使用 SELECT 语句中的 NOLOCK 选项单独强制它们在读取数据时将 NO LOCKS 留在原地(更可取)。 如果您随后确保 SP 在插入后(内部或外部)几乎立即提交,则应该不会出现死锁 - 写锁定将导致下一个 SP 等待第一个事务提交。

特别是当只下到一张表/一行时,仅使用更新语句 IIRC 是不可能出现死锁的。正是读锁将延迟锁(尽管延迟很小)变成了死锁。

http://www.eggheadcafe.com/software/aspnet/30976898/how-does-update-lock-prevent-deadlocks.aspx 有一些很好的死锁示例。所以,如果除了更新之外的所有东西都没有锁,那么最后死锁是不可能的。

【讨论】:

我认为在更新数据时避免锁定有点困难。 我阅读后发现支持 4 个锁 READ UNCOMMITTED READ COMMITTED REPEATABLE READ SERIALIZABLE 我还不确定,我应该在我的 SP 中使用哪一个来执行 UPDATE 来解决我的问题。跨度> 我添加了详细的描述。 下一次,我建议警告 NOLOCK 可能会产生不良影响:例如sqlblogcasts.com/blogs/tonyrogerson/archive/2006/11/10/… 鉴于给定的具体情况,我认为这样的警告是隐含的。用户特别指出,更新只发生在一个表/行,不同的字段。【参考方案4】:

行级锁定尽可能低,因此我认为您必须将更新后的表分解为几个可以相互独立更新的表。

【讨论】:

-1 了解避免低级锁定的标准机制。【参考方案5】:

不,两个会话不能同时更新同一行。行级别锁定尽可能低,因此当一个会话更新一行时,其他想要更新该记录的会话将等待。

至于死锁,SQL Server 的问题在于,默认情况下,如果 SELECT 语句请求当前正在更新的记录,它会阻塞。如果您不介意读取未提交的数据,可以使用 WITH (NOLOCK)。

所以,如果你有这样的事件顺序: 会话A

begin transaction

update t1
set c1 = 'x'
where c2 = 5

SessionB

begin transaction

update t2 
set c1 = 'y'
where c2 = 7

SessionA

select * from t1 where c2 = 5
<waits on SessionB>

SessionB
select * from t2 where c2 = 7
<waits on SessionA>...oops. Deadlock.

诀窍是只在必要的时间内锁定某些东西(不要为了释放锁定而分解一系列语句 - 确保构成逻辑事务的步骤保持为事务):

begin transaction

update t1
set c1 = 'x'
where c2 = 5

commit

或者(警告购买者)使用 nolock 指令:

SessionA

select c1 from t1 where c2 = 5 with (nolock)
<gets the new value of 'x'>

SessionB
select * from t2 where c2 = 7 with (nolock)
<gets the new value of 'y'>

【讨论】:

-1 显然对避免行锁定的方法一无所知。 好电话。我并没有试图避免行锁定,因为对我来说,保持数据完整性是最重要的。

以上是关于多个存储过程如何同时更新同一行?的主要内容,如果未能解决你的问题,请参考以下文章

mysql存储过程出现锁表锁行的情况怎么解决

hibernate的update() 更新延迟或者无法更新,导致同个service调用存储过程执行方法不精确

一个存储过程中更新多个表可以用一个COMMIT吗 ?

如何在pl / sql中同时在不同会话中执行存储过程

mysql 存储过程,游标总是多读一行,泪奔求助

mysql 存储过程