Man*_*ong 5 sql t-sql sql-server sql-server-2008-r2
考虑以下情况:
公司中的某些用户可能会读取和更新数据库中的客户数据(来自前端应用程序)。
为避免同一客户重复更新,如果用户A开始修改特定客户的数据,则在用户A完成并更新数据库之前,没有人可以修改该客户的数据(用户A持有该客户数据的排他锁)。
如果另一个用户尝试修改相同的客户数据,系统将立即返回错误信息,表明用户 A 已经在修改该客户的数据。
对于这样的需求,我想在用户 A 开始编辑客户数据时sp_getapplock使用名称作为唯一客户 ID授予排他锁。然后当另一个用户尝试编辑时,系统将返回无法授予锁定,因为用户 A 持有它。
以下是我预期的 SQL:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRANSACTION
DECLARE @result INT
EXEC @result = sp_getapplock
@Resource = 'UniqueCustomerID', @LockMode = 'Exclusive', @LockTimeout = 0
IF @result = 0
--Successful grant the lock, perform the data update action here
ELSE
--The lock is holding by someone, return the SPID who holds the lock
COMMIT TRANSACTION
Run Code Online (Sandbox Code Playgroud)
如何检索持有锁的 SPID?我知道sys.sysprocesses并且sp_who可以通过SPID获取块,但在我的情况下,sql 语句需要立即返回而不阻塞,并获取谁持有它试图授予的锁。是否有可能以及如何做到这一点?我感谢任何建议和建议:-)
要获取持有应用程序锁的会话 ID,您可以查询 sys.dm_tran_locks
SELECT request_session_id
FROM sys.dm_tran_locks
WHERE resource_type = 'APPLICATION'
AND request_mode = 'X'
AND request_status = 'GRANT'
AND resource_description LIKE '%:\[UniqueCustomerID\]:%' ESCAPE '\'
Run Code Online (Sandbox Code Playgroud)
其中UniqueCustomerID最多 32 个字符(更多将被截断)。
但这需要VIEW SERVER STATE许可。因此,您可能需要将其封装在使用代码签名或EXECUTE AS临时提升权限的存储过程中。
按照 Mitch 的建议滚动您自己的方案不应该太棘手,并且可以避免这个问题。
你可以有一个表,每个客户一行,user_name然后一列,而不是调用sp_getapplock每个事务的开始尝试更新user_nameatSET LOCK_TIMEOUT 0;并且(如果失败)然后你可以在读取未提交时读取该行中包含的 user_name。我还会让每个事务在提交之前将其user_name列设置回NULL。如果 user_name 的读取返回,NULL您就会知道要重试。
需要对该表进行索引CustomerId以定位该行,而不会碰到其他客户的锁。
| 归档时间: |
|
| 查看次数: |
4075 次 |
| 最近记录: |