如何为多用户应用程序实现简单的锁定机制?

时间:2014-01-22 13:29:49

标签: sql-server sql-server-2005 multi-user

我真的不想在这里重新发明,所以我要求在多用户数据库应用程序中实现简单(行)锁机制的想法。

假设我有一个名为Products的表,当然有一个ID(PK),还有一个rowversion列(目前尚未使用),我只想允许一个用户可以编辑特定行。

虽然此用户正在编辑记录(在“签入”之后),但其他用户只能查看此记录(只读),但不能修改或删除它。当用户完成并保存记录(“结账”)时,该记录将再次可供其他用户编辑/删除。

我有一些想法(例如添加“状态”列,或者可能创建“锁定”表),但是如果“锁定用户”长时间持有记录也会有所考虑(假设他去了一个周末,让他的电脑在编辑模式下打开)。如果程序在客户端计算机上崩溃/系统断电,如何解锁记录...

我想知道是否有一个好的相对简单的模式(可能包括SQL-Server功能)?

BTW,我的客户端应用程序是Delphi / ADO(不是很相关)。

2 个答案:

答案 0 :(得分:4)

我在应用程序中实现的简单解决方案......

CREATE TABLE RecordLocks(
[RecordId] [varchar](8) NOT NULL,
[UserName] [varchar](100) NOT NULL,
[datetimestamp] [smalldatetime] NOT NULL,
[PC] [varchar](100) NOT NULL

)
GO

datetimestamp的默认值为GetDate() RecordIdVARCHAR,因为我锁定的表中的主键(不是我的选择)。此表也有明显的索引

CREATE PROCEDURE usp_LockRecord @RecordId VARCHAR(8), @UserName VARCHAR(100), @ComputerName VARCHAR(100)
AS
BEGIN
BEGIN TRAN; 
DELETE FROM RecordLocks WHERE DATEDIFF(HOUR, datetimestamp, GETDATE()) > 2; 
IF NOT EXISTS (Select * from RecordLocks WHERE RecordId = @RecordId) 
    INSERT INTO RecordLocks (RecordId, username, PC) VALUES (@RecordId, @UserName, @ComputerName); 

Select * from RecordLocks WHERE RecordId = @RecordId; 
COMMIT TRAN;
END
GO

首先删除并记录超过2小时(更改为适合)

检查是否已有锁定要锁定的记录,如果没有,则插入锁定。

选择我们感兴趣的RecordId记录。

然后在调用代码中检查锁定是否成功。如果从select中返回的用户名和PC匹配刚刚在锁中传递的数据成功。如果用户名匹配,但PC没有,则同一用户在另一台计算机上打开记录。如果用户名与其他用户不匹配则已打开。如果用户失败,我会向用户显示一条消息.E此记录当前由JoeB在工作站XYZ上锁定。

当用户保存记录或导航时,只需删除记录锁定。

我确定还有其他方法,但这对我有用。

<强>更新

只有在不存在记录时才会插入记录。以下选择将返回记录。如果用户名和/或pc与您尝试插入的数据不同,则该记录已被另一个用户(或另一台计算机上的同一用户)锁定。所以一个电话就可以了(可以这么说)。因此,如果我拨打电话Exec usp_LockRecord(1234, 'JoeB', 'Workstation1')并且我收到的记录与该数据匹配,那么我已成功锁定该记录。如果我回来的用户名和/或PC不同,则记录已被锁定。然后,我可以向用户显示一条消息,告知记录被锁定,使字段只读,禁用保存按钮,如果我愿意,告诉他们谁有锁。

答案 1 :(得分:2)

有了时间戳,你可以逃脱“作弊”。工作流程看起来像这样:

  1. 将行(包括时间戳)读入内存
  2. 让用户进行一些编辑,跟踪旧值和新值
  3. 用户点击“保存”
  4. 再次读取行的时间戳(在防止不可重复读取的隔离级别)
    1. 如果最近读取的时间戳与旧时间戳相同,则使用用户更改的值更新行并提交
    2. 如果最近读取的时间戳与旧时间戳不同,则可以尝试“合并”更改(即,如果完全不相交的属性集发生更改,则两个更改是兼容的)。如果有任何重叠,请警告用户并禁止保存
  5. 应该这样做。