【问题标题】:Can I lock 2 tables at the same time?我可以同时锁定两张桌子吗?
【发布时间】:2013-12-06 01:54:20
【问题描述】:

我需要在我的应用程序中实现一个锁定方案。如果对话框/表单没有被锁定,用户只能打开它。另一方面,如果当前没有打开对应于该锁的对话框,则只能设置锁。

到目前为止我的方法: 我有一个带有列 lockname 的 sql server 表 tbl_lock,我在其中维护锁和一个表 tbl_dialog (user, dialogname),每当用户打开对话框时,我都会在其中创建一行。

要创建一个锁,我执行以下操作: 1) 开始交易 2) select * from tbl_dialog with (SERIALIZABLE) where dialogname=@somename 3) 如果 prio select 没有返回记录,则在 tbl_lock 中为对话框 @somename 插入一个锁定记录 4) 提交事务

问题在于,我还想防止有人在其他用户尝试设置锁定时尝试打开对话框。我可以重复上述 4 个步骤并选择 tbl_lock with serializable 并在 tbl_dialog 中插入一条记录,但我担心如果 2 个并发事务执行步骤会同时锁定两个表 tbl_dialog 和 tbl_lock,我会遇到死锁。

我可以在第 2 步中同时锁定两个表吗?有没有更聪明的方法来解决这个问题?

最好的问候, 萨沙

编辑: 根据 Bogdan 的评论,我将实施以下 SP 来设置锁定:

Create procedure CreateLock 
    @dialogname nvarchar(10),
    @lockname nvarchar(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @result int;
    DECLARE @locksuccess int = 0;

    begin tran;

    EXEC @result = sp_getapplock @Resource = 'myapplockmanagement', @LockMode = 'Exclusive', @LockTimeout=3000;
    if @result>=0
    BEGIN
        -- Ensure no user has opened the dialog @lockname
        if not EXISTS(select 1 from tbl_dialog where dialogname=@dialogname)
        BEGIN
            if not EXISTS(select 1 from tbl_lock where lockname=@lockname)
            BEGIN
                insert into tbl_lock (lockname) values (@lockname);
                set @locksuccess = 1;
            END
        END
        commit;
    END
    ELSE
    BEGIN
        rollback;
    END

    return @locksuccess;
END

如果我正确理解了 sp_getapplock,myapplockmanagement 将被独占锁定,从而锁定所有其他线程。如果可以获取应用程序锁,我可以安全地检查我的 tbl_dialog 并最终在 tbl_lock 中插入一个新行,对吗?

所以下一个程序是:

Create procedure RegisterDialogUsage 
    @dialogname nvarchar(10),
    @lockname nvarchar(10)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @result int;
    DECLARE @registersuccess int = 0;

    begin tran;

    EXEC @result = sp_getapplock @Resource = 'myapplockmanagement', @LockMode = 'Exclusive', @LockTimeout=3000;
    if @result>=0
    BEGIN
        -- Ensure that no lock is set.
        if not EXISTS(select 1 from tbl_lock where lockname=@lockname)
        BEGIN
            insert into tbl_dialog (dialogname) values (@dialogname);
            set @registersuccess = 1;
        END
        commit;
    END
    ELSE
    BEGIN
        rollback;
    END

    return @registersuccess;
END

所以,如果我没记错的话,这两个 SP 完全按照我的喜好行事:

  1. 锁只能在tbl_lock中设置,如果tbl_dialog中没有注册用户
  2. 如果 tbl_lock 中没有设置锁,则只能在 tbl_dialog 中注册对话使用
  3. 感谢 sp_getapplock,不会出现并发问题。

对吗? (相应的用于删除 tbl_lock 和 tbl_dialog 中的条目的 SP 必须实现两个......)

萨沙

【问题讨论】:

  • 这个应用程序在哪里运行?在服务器上还是在多个客户端机器上?您打算如何处理废弃的锁,例如当应用程序意外关闭时?
  • 对,我不够具体。这些应用程序是一个运行在多个客户端计算机上的 .Net 应用程序,所有客户端计算机都连接到同一个 sql 服务器。废弃的锁没问题,因为我用tbl_lock中的条目保存了sql连接id。因此,我可以通过 sys.dm_exec_sessions 检查连接是否仍然存在,如果是,则不会放弃锁。
  • @Sascha:我会使用一个表:dbo.Lock(DialogName, LockName)。在这种情况下,我将在(DialogName, LockName) 上创建一个唯一/主要约束。因此,对于每个对话框,我不能有两个或多个具有相同LockName 的锁。在这种情况下,[1] 这些应用程序锁将防止重复密钥,[2] 您只需要一个程序而不是两个。此外,您需要释放这些锁。

标签: sql-server tsql locking


【解决方案1】:

【讨论】:

  • 我在原始帖子中添加了一个示例实现。你是这样说的吗?
猜你喜欢
  • 2015-06-20
  • 1970-01-01
  • 2022-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-31
相关资源
最近更新 更多