在 sybase 中,如何锁定正在执行的存储过程并更改存储过程返回的表?
Posted
技术标签:
【中文标题】在 sybase 中,如何锁定正在执行的存储过程并更改存储过程返回的表?【英文标题】:In sybase, how would I lock a stored procedure that is executing and alter the table that the stored procedure returns? 【发布时间】:2019-06-28 11:23:16 【问题描述】:我有一张如下表:
id status
-- ------
1 pass
1 fail
1 pass
1 na
1 na
另外,我有一个存储过程,它返回一个表,其中前 100 条记录的状态为“na”。存储过程可以由环境中的多个节点调用,我不希望它们获取重复数据。所以,我想在存储过程执行时锁定存储过程,并将从存储过程获取的记录的状态设置为“进行中”并返回该表然后释放锁,这样不同的节点就不会获取相同的数据。我将如何做到这一点?
ms sql 中已经有类似问题的解决方案,但是在 sybase 中使用时显示错误。
【问题讨论】:
已经有两个很好的答案,但请确保您的错误处理和事务管理正确。否则,您最终可能会锁定应用程序(在声明和释放锁定之间发生错误)。更强大的解决方案是使用消息队列。 哪个 Sybase RDBMS(ASE?IQ?SQLAnywhere?Advantage?)和版本? 【参考方案1】:假设 Sybase ASE ...
您可能需要考虑的更大问题是,您是希望单个进程在获取前 100 行时锁定整个表,还是希望其他进程仍然访问表?
另一个问题是您是否希望多个进程同时从表中拉出 100 行而不相互阻塞?
我将假设您 a) 不想锁定整个表,并且 b) 您可能希望允许多个进程同时从表中提取行。
1 - 如果可能,确保表使用 datarows 锁定(默认通常是 allpages);这会将锁的粒度降低到行级别(与 allpages 的页面级别相反);如果您想允许多个进程同时查找/更新表中的行,则该表需要是 datarows
2 - 确保表上的锁定升级设置足够高,以确保单个进程的 100 行更新不会锁定表(sp_setpglockpromote
for allpages,sp_setrowlockpromote
for 数据行);这里的关键是确保您的update
不会升级为表级锁定!
3 - 当需要获取 100 行的集合时,您会想要...在事务中...update
具有 status
值的 100 行对于您的会话来说是唯一的,选择关联id
,然后再次将status
更新为“进行中”
操作要点如下:
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
潜在问题:
如果您的表很大并且在status
上没有索引,那么您的查询可能需要比运行所需更长的时间;通过确保锁定升级足够高并且您正在使用 datarows 锁定(因此readpast
有效),无论查找所需行需要多长时间,您都应该看到其他进程的阻塞最小
在status
列上有索引,考虑到所有这些update
s 将强制进行大量索引更新,这可能会导致一些昂贵的延迟更新强>
如果使用 datarows 并且您的锁升级太低,则更新可能会查看整个表,这将导致另一个(并发)进程 readpast
表锁并找不到要处理的行
如果使用 allpages,您将无法使用 readpast
,因此并发进程将阻塞您的锁(即,它们将无法读取您的锁)
如果您在 status
上有一个索引,并且多个并发进程锁定了表中的不同行,则可能会发生死锁(可能在987654337@ 列),这反过来又需要对您的客户端/应用程序进行编码以预期和解决死锁
想一想:
如果表相对较小,因此表扫描的成本并不高,您可以删除status
列上的任何索引,这应该会降低延迟更新的性能开销>(与更新索引有关)
如果您可以使用特定于会话的 status
值(例如,'In Progress - @mysession'),那么您可以消除第二条 update
语句(如果您遇到 延迟索引status
列的更新)
如果表中有其他列可用于唯一标识会话的行(例如,last_updated_by_spid
= @@spid、last_updated_date
= @mydate - 其中 @mydate 最初是设置为getdate()
) 那么你的第一个update
可以设置状态='In Progress',select
将使用@@spid 和@mydate 作为where
子句,而第二个update
不会需要 [注意:实际上,这与 Gordon 在他的 session
专栏中试图解决的问题相同。]
假设您可以使用特定于会话的 status
值,请考虑使用允许您跟踪和修复孤立行的东西(例如,行 status
仍然是“进行中 - @mysession”,因为调用进程死了,再也没有回来(重新)设置状态)
status
= 'In Progress',还允许您消除select
和第二个update
如何判断哪些行是孤立的?您可能希望能够使用发布update
时的getdate()
更新(小)日期时间列;然后,如果您通常希望 status
在 5 分钟内更新,您可以有一个监视进程来查找孤立行,其中 status
= 'In Progress' 并且它超过了,比如说,10距离上次update
如果 datarows、readpast
、锁升级设置和/或死锁可能性太大,并且您可以忍受表上的简短表级锁,您可以让进程获取执行update
和select
语句之前的独占表级锁;排他锁需要在用户定义的事务中获得,以便在您的工作期间“持有”锁;一个简单的例子:
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
【讨论】:
【参考方案2】:我不能 100% 确定如何在 Sybase 中执行此操作。但是,想法如下。
首先,向表中添加一个新列,该列表示用于更改数据的会话或连接。您将使用此列来提供隔离。
然后,更新行:
update top (100) t
set status = 'in progress',
session = @session
where status = 'na'
order by ?; -- however you define the "top" records
然后,您可以返回或处理给定连接“正在进行”的 100 个 ID。
【讨论】:
【参考方案3】:-
创建另一个表 proc_lock,它有一行
当控制进入存储过程时,启动一个事务并对 proc_lock 中的行进行更新选择(参见this 链接)。如果这对 Sybase 不起作用,那么您可以尝试 this answer 中的技术来锁定行。
在程序退出之前,请确保提交事务。
这将确保一次只有一个用户可以执行 proc。当第二个用户尝试执行 proc 时,它将阻塞,直到第一个用户在 proc_lock 行上的锁被释放(例如,当事务被提交时)
【讨论】:
以上是关于在 sybase 中,如何锁定正在执行的存储过程并更改存储过程返回的表?的主要内容,如果未能解决你的问题,请参考以下文章
将sybase存储过程作为链接服务器过程sql server 2008执行