在sybase中,如何锁定正在执行的存储过程并更改该存储过程返回的表?

时间:2019-06-28 11:23:16

标签: sql stored-procedures jdbc sybase rdbms

我有一张桌子,如下:

id    status
--    ------
1     pass
1     fail
1     pass
1     na
1     na

此外,我有一个存储过程,该存储过程返回一个表,其中前100条记录的状态为“ na”。该存储过程可由环境中的多个节点调用,我不希望它们获取重复的数据。因此,我想在执行过程中锁定存储过程,并将从存储过程中获取的记录的状态设置为“进行中”,然后返回该表,然后释放该锁,以使不同的节点不会获取相同的内容数据。我该怎么办?

已经存在针对ms sql中类似问题的解决方案,但在sybase中使用时会显示错误。

3 个答案:

答案 0 :(得分:1)

我不确定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。

答案 1 :(得分:1)

  1. 创建另一个具有一行的表proc_lock
  2. 当控件进入存储过程时,启动事务并在proc_lock中的行上进行选择以进行更新(请参见this链接)。如果该方法不适用于Sybase,则可以尝试使用this answer中的技术来锁定行。
  3. 在过程退出之前,请确保提交事务。

这将确保一次仅一个用户可以执行该proc。当第二个用户尝试执行proc时,它将阻塞直到释放第一个用户对proc_lock行的锁定(例如,提交事务时)

答案 2 :(得分:1)

假设使用Sybase ASE ...

您可能要考虑的更大的问题是,在获取前100行时,是要单个进程锁定整个,还是希望其他进程仍然访问表?

另一个问题是,您是否希望多个进程同时从表中拉出100行而不互相阻塞?

我将假设您a)不想锁定整个表,并且b)您可能希望允许多个进程同时从表中提取行。

1-如果可能,请确保该表正在使用 datarows 锁定(默认通常为 allpages );这样可以将锁定的粒度减小到行级别(与 allpages 的页面级别相对);如果要允许多个进程同时查找/更新表中的行,则该表将是数据行

2-确保表上的锁定升级设置足够高,以确保单个进程的100行更新不会锁定表(所有页,{{1 }}用于数据行);关键是要确保您的sp_setpglockpromote不会升级为表级锁!

3-到了要抓取100行的集合时,您希望...在交易中... sp_setrowlockpromote带有update值的100行是您唯一的会话中,选择关联的update,然后再次将status更新为“进行中”

操作要点如下:

id

潜在问题:

  • 如果表很大,并且在status上没有索引,则查询所花的时间可能比运行查询所需的时间长;通过确保锁升级足够高并且您正在使用 datarows 锁(因此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列上有索引,请考虑所有这些readpast都将强制执行许多索引更新,这可能会导致某些昂贵的推迟的更新

  • 如果使用 datarows ,并且您的锁升级过低,则更新可能会查找整个表,这将导致另一个(并发)进程status锁定表并找不到要处理的行

  • 如果使用 allpages ,您将无法使用update,因此并发进程将阻塞您的锁(即,它们将无法读取您的锁锁定)

  • 如果您在readpast上有一个索引,并且有多个并发进程锁定了表中的不同行,则可能会发生死锁(可能是在索引的索引树中readpast列),这反过来又要求对您的客户端/应用程序进行编码以预期并解决死锁

考虑:

  • 如果表相对较小,以至于表扫描成本不高,则可以在status列上删除任何索引,这将减少延迟更新的性能开销< / strong>(与更新索引有关)

  • 如果您可以使用特定于会话的status值(例如'In Progress-@mysession'),则可以消除第二条status语句(如果您使用此语句可能会派上用场在索引的status列上发生了推迟的更新)

  • ,如果表中还有其他列可用于唯一标识会话的行(例如update = @@ spid,status = @mydate-其中@mydate最初设置为last_updated_by_spid),然后您的第一个last_updated_date可以将status设置为'In Progress',getdate()将对{{1}使用@@ spid和@mydate }子句,不需要第二个update [注意:实际上,这是戈登试图在其select列中解决的同一问题。]

  • 假设您可以使用特定于会话的where值,请考虑使用一些可以跟踪和修复孤立行的东西(例如,行update仍为“进行中- @mysession”,因为调用过程终止,并且再也没有恢复(重置)状态)

  • 如果您可以将session列表作为连接的status值的单个字符串传递回调用程序,则可以使用我在此answer中概述的方法来附加将status转换为@variable,在第一次更新中,您可以在第一次更新中将id ='In Progress'设置,还可以删除id和第二次更新id

  • 您如何知道哪些行被孤立了?您可能希望能够使用发布status时的select来更新(small)datetime列;那么,如果您通常希望update在5分钟之内得到更新,则可以有一个监视过程来查找孤立行,其中getdate() ='In Progress',并且(例如,距离上一个update

  • 10分钟)

如果数据行status,锁升级设置和/或潜在的死锁可能性太大,并且您可以在表上使用短暂的表级锁,则可以在执行statusupdate语句之前,进程获取排他表级锁;排他锁需要在用户定义的事务中获取,以便在工作期间“持有”该锁;一个简单的例子:

readpast