在 sybase 中,如何锁定正在执行的存储过程并更改存储过程 returns 的 table?
In sybase, how would I lock a stored procedure that is executing and alter the table that the stored procedure returns?
我有一个table如下:
id status
-- ------
1 pass
1 fail
1 pass
1 na
1 na
另外,我有一个存储过程 return 是一个 table,前 100 条记录的状态为 'na'。存储过程可以由环境中的多个节点调用,我不希望它们获取重复数据。所以,我想在存储过程执行的时候加锁,将从存储过程中获取的记录的状态设置为'In Progress'和return即table,然后释放锁,所以不同的节点不会获取相同的数据。我将如何做到这一点?
类似的问题在ms中已经有解决方案sql但是在sybase中使用时显示错误
我不是 100% 确定如何在 Sybase 中执行此操作。但是,思路如下。
首先,向 table 添加一个新列,表示用于更改数据的会话或连接。您将使用此列提供隔离。
然后,更新行:
update top (100) t
set status = 'in progress',
session = @session
where status = 'na'
order by ?; -- however you define the "top" records
然后,您可以 return 或处理给定连接的 "in progress" 的 100 个 ID。
- 创建另一个 table、proc_lock,它有一行
- 当控制进入存储过程时,启动事务并对 proc_lock 中的行执行 select 更新(参见 this link). If that doesn't work for Sybase, then you could try the technique from this answer 锁定行。
- 在过程退出之前,确保提交事务。
这将确保一次只有一个用户可以执行该过程。当第二个用户尝试执行 proc 时,它将阻塞直到第一个用户对 proc_lock 行的锁定被释放(例如,当提交事务时)
假设 Sybase ASE ...
您可能要考虑的更大问题是您是否希望单个进程在抓取前 100 行时锁定整个 table ,或者如果您希望其他进程仍然访问 table?
另一个问题是您是否希望多个进程同时从 table 中拉取 100 行而不相互阻塞?
我假设您 a) 不想锁定整个 table 并且 b) 您可能希望允许多个进程同时从 table 中提取行。
1 - 如果可能,确保 table 使用 datarows 锁定(默认通常是 allpages);这会将锁的粒度降低到行级别(与 allpages 的页级别相反); table 需要是 datarows 如果你想允许多个进程同时 find/update 行在 table
2 - 确保 table 上的锁定升级设置足够高,以确保单个进程的 100 行更新不会锁定 table(sp_setpglockpromote
for allpages, sp_setrowlockpromote
for datarows);这里的关键是确保您的 update
不会升级为 table-level 锁!
3 - 当需要获取 100 行的集合时,您会希望...在事务中...update
具有唯一值 status
的 100 行到您的会话,select 关联的 id
,然后再次将 status
更新为 'In Progress'
操作的要点如下所示:
declare @mysession varchar(10)
select @mysession = convert(varchar(10),@@spid) -- replace @@spid with anything that
-- uniquely identifies your session
set rowcount 100 -- limit the update to 100 rows
begin tran get_my_rows
-- start with an update so that get exclusive access to the desired rows;
-- update the first 100 rows you find with your @@spid
update mytable
set status = @mysession -- need to distinguish your locked rows from
-- other processes; if we used 'In Progress'
-- we wouldn't be able to distinguish between
-- rows update earlier in the day or updated
-- by other/concurrent processes
from mytable readpast -- 'readpast' allows your query to skip over
-- locks held by other processes but it only
-- works for datarows tables
where status = 'na'
-- select your reserved id's and send back to the client/calling process
select id
from mytable
where status = @mysession
-- update your rows with a status of 'In Progress'
update mytable
set status = 'In Progress'
where status = @mysession
commit -- close out txn and release our locks
set rowcount 0 -- set back to default of 'unlimited' rows
潜在问题:
如果您的 table 很大并且您在 status
上没有索引,那么您的查询可能需要比 运行 所需的更长的时间;通过确保锁升级足够高并且您正在使用 datarows 锁定(因此 readpast
有效)您应该看到其他进程的最小阻塞,无论需要多长时间找到所需的行
在 status
列上有一个索引,考虑到所有这些 update
都会强制执行大量索引更新,这可能会导致一些昂贵的 延迟更新
如果使用 datarows 并且您的锁升级太低,那么更新可能会查看整个 table,这会导致另一个(并发)处理 readpast
table 锁,发现没有行要处理
如果使用 allpages,您将无法使用 readpast
,因此并发进程将阻塞您的锁(即,它们不会无法阅读您的锁)
如果您在 status
上有一个索引,并且多个并发进程锁定 table 中的不同行,则可能会发生死锁(很可能在 status
列的索引的索引树中),这反过来又需要对 client/application 进行编码以预期并解决死锁
思考:
如果 table 相对较小,因此 table 扫描的成本不是很大,您可以删除 status
列上的任何索引,这应该减少 延迟更新 的性能开销(与更新索引相关)
如果您可以使用会话特定的 status
值(例如,'In Progress - @mysession'),那么您可以消除第二个 update
语句(可能会派上用场如果您在索引 status
列上进行 延迟 更新)
如果您在 table 中还有其他列可用于唯一标识会话的行(例如,last_updated_by_spid
= @@spid,last_updated_date
= @mydate - @mydate 最初设置为 getdate()
) 然后你的第一个 update
可以设置状态 = 'In Progress',select
将使用 @@spid和 @mydate 用于 where
子句,不需要第二个 update
[注意:实际上,这与 Gordon 在他的 session
专栏中试图解决的问题相同。]
假设您可以使用特定于会话的 status
值,请考虑使用允许您跟踪和修复孤立行(例如,行 status
保留'In Progress - @mysession'因为调用进程死了,再也没有回来(重新)设置状态)
如果您可以将 id
列表作为单个串接的 id
值传递回调用程序,您可以使用我在此 [=55] 中概述的方法=] 在第一次更新期间将 id
附加到 @variable 中,允许您在第一次更新中设置 status
= 'In Progress' 并允许您消除 select
和第二个 update
您如何判断哪些行已被孤立?您可能希望能够使用发布 update
时的 getdate()
更新(小)日期时间列;然后,如果您通常希望 status
在 5 分钟内更新,您可以有一个监视过程来查找孤立的ows 其中 status
= 'In Progress' 距离上次 update
已经超过 10 分钟了
如果 数据行 、readpast
、锁升级设置 and/or 死锁可能性太大,您可以忍受短暂 table-level table 上的锁,您可以让进程在执行 update
和 select
语句之前获得独占 table 级别的锁;排他锁需要在 user-defined 事务中获得,以便 'hold' 在您的工作期间获得锁;一个简单的例子:
begin tran get_my_rows
-- request an exclusive table lock; wait until it's granted
lock table mytable in exclusive mode
update ...
select ...
update ...
commit
我有一个table如下:
id status
-- ------
1 pass
1 fail
1 pass
1 na
1 na
另外,我有一个存储过程 return 是一个 table,前 100 条记录的状态为 'na'。存储过程可以由环境中的多个节点调用,我不希望它们获取重复数据。所以,我想在存储过程执行的时候加锁,将从存储过程中获取的记录的状态设置为'In Progress'和return即table,然后释放锁,所以不同的节点不会获取相同的数据。我将如何做到这一点?
类似的问题在ms中已经有解决方案sql但是在sybase中使用时显示错误
我不是 100% 确定如何在 Sybase 中执行此操作。但是,思路如下。
首先,向 table 添加一个新列,表示用于更改数据的会话或连接。您将使用此列提供隔离。
然后,更新行:
update top (100) t
set status = 'in progress',
session = @session
where status = 'na'
order by ?; -- however you define the "top" records
然后,您可以 return 或处理给定连接的 "in progress" 的 100 个 ID。
- 创建另一个 table、proc_lock,它有一行
- 当控制进入存储过程时,启动事务并对 proc_lock 中的行执行 select 更新(参见 this link). If that doesn't work for Sybase, then you could try the technique from this answer 锁定行。
- 在过程退出之前,确保提交事务。
这将确保一次只有一个用户可以执行该过程。当第二个用户尝试执行 proc 时,它将阻塞直到第一个用户对 proc_lock 行的锁定被释放(例如,当提交事务时)
假设 Sybase ASE ...
您可能要考虑的更大问题是您是否希望单个进程在抓取前 100 行时锁定整个 table ,或者如果您希望其他进程仍然访问 table?
另一个问题是您是否希望多个进程同时从 table 中拉取 100 行而不相互阻塞?
我假设您 a) 不想锁定整个 table 并且 b) 您可能希望允许多个进程同时从 table 中提取行。
1 - 如果可能,确保 table 使用 datarows 锁定(默认通常是 allpages);这会将锁的粒度降低到行级别(与 allpages 的页级别相反); table 需要是 datarows 如果你想允许多个进程同时 find/update 行在 table
2 - 确保 table 上的锁定升级设置足够高,以确保单个进程的 100 行更新不会锁定 table(sp_setpglockpromote
for allpages, sp_setrowlockpromote
for datarows);这里的关键是确保您的 update
不会升级为 table-level 锁!
3 - 当需要获取 100 行的集合时,您会希望...在事务中...update
具有唯一值 status
的 100 行到您的会话,select 关联的 id
,然后再次将 status
更新为 'In Progress'
操作的要点如下所示:
declare @mysession varchar(10)
select @mysession = convert(varchar(10),@@spid) -- replace @@spid with anything that
-- uniquely identifies your session
set rowcount 100 -- limit the update to 100 rows
begin tran get_my_rows
-- start with an update so that get exclusive access to the desired rows;
-- update the first 100 rows you find with your @@spid
update mytable
set status = @mysession -- need to distinguish your locked rows from
-- other processes; if we used 'In Progress'
-- we wouldn't be able to distinguish between
-- rows update earlier in the day or updated
-- by other/concurrent processes
from mytable readpast -- 'readpast' allows your query to skip over
-- locks held by other processes but it only
-- works for datarows tables
where status = 'na'
-- select your reserved id's and send back to the client/calling process
select id
from mytable
where status = @mysession
-- update your rows with a status of 'In Progress'
update mytable
set status = 'In Progress'
where status = @mysession
commit -- close out txn and release our locks
set rowcount 0 -- set back to default of 'unlimited' rows
潜在问题:
如果您的 table 很大并且您在
status
上没有索引,那么您的查询可能需要比 运行 所需的更长的时间;通过确保锁升级足够高并且您正在使用 datarows 锁定(因此readpast
有效)您应该看到其他进程的最小阻塞,无论需要多长时间找到所需的行在
status
列上有一个索引,考虑到所有这些update
都会强制执行大量索引更新,这可能会导致一些昂贵的 延迟更新如果使用 datarows 并且您的锁升级太低,那么更新可能会查看整个 table,这会导致另一个(并发)处理
readpast
table 锁,发现没有行要处理如果使用 allpages,您将无法使用
readpast
,因此并发进程将阻塞您的锁(即,它们不会无法阅读您的锁)如果您在
status
上有一个索引,并且多个并发进程锁定 table 中的不同行,则可能会发生死锁(很可能在status
列的索引的索引树中),这反过来又需要对 client/application 进行编码以预期并解决死锁
思考:
如果 table 相对较小,因此 table 扫描的成本不是很大,您可以删除
status
列上的任何索引,这应该减少 延迟更新 的性能开销(与更新索引相关)如果您可以使用会话特定的
status
值(例如,'In Progress - @mysession'),那么您可以消除第二个update
语句(可能会派上用场如果您在索引status
列上进行 延迟 更新)如果您在 table 中还有其他列可用于唯一标识会话的行(例如,
last_updated_by_spid
= @@spid,last_updated_date
= @mydate - @mydate 最初设置为getdate()
) 然后你的第一个update
可以设置状态 = 'In Progress',select
将使用 @@spid和 @mydate 用于where
子句,不需要第二个update
[注意:实际上,这与 Gordon 在他的session
专栏中试图解决的问题相同。]假设您可以使用特定于会话的
status
值,请考虑使用允许您跟踪和修复孤立行(例如,行status
保留'In Progress - @mysession'因为调用进程死了,再也没有回来(重新)设置状态)如果您可以将
id
列表作为单个串接的id
值传递回调用程序,您可以使用我在此 [=55] 中概述的方法=] 在第一次更新期间将id
附加到 @variable 中,允许您在第一次更新中设置status
= 'In Progress' 并允许您消除select
和第二个update
您如何判断哪些行已被孤立?您可能希望能够使用发布
update
时的getdate()
更新(小)日期时间列;然后,如果您通常希望status
在 5 分钟内更新,您可以有一个监视过程来查找孤立的ows 其中status
= 'In Progress' 距离上次update
已经超过 10 分钟了
如果 数据行 、readpast
、锁升级设置 and/or 死锁可能性太大,您可以忍受短暂 table-level table 上的锁,您可以让进程在执行 update
和 select
语句之前获得独占 table 级别的锁;排他锁需要在 user-defined 事务中获得,以便 'hold' 在您的工作期间获得锁;一个简单的例子:
begin tran get_my_rows
-- request an exclusive table lock; wait until it's granted
lock table mytable in exclusive mode
update ...
select ...
update ...
commit