![](/img/trans.png)
[英]How would I lock a stored procedure that is executing and alter the table that the stored procedure returns?
[英]In sybase, how would I lock a stored procedure that is executing and alter the table that the stored procedure returns?
我有一張桌子,如下所示:
id status
-- ------
1 pass
1 fail
1 pass
1 na
1 na
另外,我有一個存儲過程,該過程返回一個表,其中前100條記錄的狀態為“ na”。 該存儲過程可由環境中的多個節點調用,我不希望它們獲取重復的數據。 因此,我想在執行過程中鎖定存儲過程,並將從存儲過程中獲取的記錄的狀態設置為“進行中”,然后返回該表,然后釋放該鎖,以使不同的節點不會獲取相同的內容數據。 我將如何完成?
在ms sql中已經有針對類似問題的解決方案,但在sybase中使用時會顯示錯誤。
我不確定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。
假設Sybase ASE ...
您可能要考慮的更大的問題是,是要在獲取前100行時使用單個進程來鎖定整個表 ,還是希望其他進程仍然訪問該表?
另一個問題是,您是否希望多個進程同時從表中拉出100行而不互相阻塞?
我將假設您a)不想鎖定整個表,並且b)您可能希望允許多個進程同時從表中提取行。
1-如果可能,請確保該表正在使用數據行鎖定(默認通常是allpages ); 這將減小鎖定到行級別的粒度(而不是所有頁面的頁面級別); 如果要允許多個進程同時查找/更新表中的行,則該表將需要是數據行
2 -確保鎖升級桌子上設置足夠高,以確保單個進程的100行的更新不鎖表( sp_setpglockpromote
所有頁 , sp_setrowlockpromote
的數據行 ); 這里的關鍵是確保您的update
不會升級到表級鎖!
3-到時候要抓取100行的集合時...要在事務內...用會話唯一的status
值update
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
索引,那么查詢所花的時間可能比運行查詢所需的時間長; 通過確保鎖升級足夠高並且您正在使用數據行鎖定(因此readpast
有效),無論查找所需行需要多長時間,您都應該看到對其他進程的最小阻塞
在status
列上有一個索引,請考慮所有這些update
將強制執行許多索引更新,這可能會導致某些昂貴的延遲更新
如果使用數據行並且鎖升級太低,則更新可能會查看整個表,這將導致另一個(並發)進程重新readpast
表鎖,並且找不到要處理的行
如果使用所有頁面 ,則將無法使用readpast
因此並發進程將阻塞您的鎖(即,它們將無法讀取您的鎖)
如果您具有status
的索引,並且有多個並發進程鎖定表中的不同行,則可能會發生死鎖(可能在status
列的索引的索引樹中),這又需要您編碼客戶端/應用程序以預期並解決死鎖
想一下:
如果表相對較小,以至於表掃描成本不高,則可以在status
列上刪除任何索引,這將減少延遲更新的性能開銷(與更新索引有關)
如果您可以使用特定於會話的status
值(例如,“進行中-@mysession”),則可以消除第二條update
語句(如果在索引status
列上進行延遲更新,可以派上用場)
如果表中還有另一列可用於唯一標識會話的行(例如, last_updated_by_spid
= @@ spid, last_updated_date
= @mydate-其中@mydate最初設置為getdate()
),則第一次update
可以將status設置為'In Progress',則select
會將@@ spid和@mydate用作where
子句,並且不需要第二次update
[注意:實際上,這是Gordon試圖解決的相同問題他的session
專欄。]
假設您可以使用特定於會話的status
值,請考慮使用一些可以跟蹤和修復孤立行的方法(例如,行status
保持為“進行中-@mysession”,因為調用進程已死亡且從未返回到(重新)設置狀態)
如果您可以將id
列表作為連接的id
值的單個字符串傳遞回調用程序,則可以使用此答案中概述的方法在第一次更新期間將id
附加到@variable中,以設置status
=第一次更新中為“進行中”,還可以消除select
和第二次update
您如何知道哪些行被孤立了? 您可能希望能夠使用發布update
時的getdate()
來更新(small)datetime列; 然后,如果您通常希望status
在5分鍾之內更新,則可以進行監視,以查找status
為'In Progress'且距上一個status
已超過10分鍾的孤立行。 update
如果數據行 , 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.