【问题标题】:How can I safely exit a DBMS when records are locked?当记录被锁定时,如何安全地退出 DBMS?
【发布时间】:2010-11-15 11:48:02
【问题描述】:

我是一个业余程序员,但已经做了一段时间了。我正在编写一个用于工作的小型文档管理应用程序(在 c# 中)。当用户打开一条记录时,我会对其进行更新以表明它当前已被锁定以进行编辑。

我不确定的是如何确保在应用程序不安全退出(例如计算机意外关闭)时更新数据库?另外,当应用程序通过用户关闭的计算机退出时,我应该如何更新它?我只是想确保在没有人查看记录时不会将记录标记为锁定。

【问题讨论】:

    标签: c# sql-server database


    【解决方案1】:

    以下是通常使用 SQL Server 完成此操作的方式。开发人员发布的“记录锁”与客户端-服务器架构无关。您将共享文件架构与客户端-服务器架构混淆了。

    确保表有时间戳列(由数据库引擎自动更新)。

    读取您要编辑的行。将行中的时间戳放入变量中。

    更新语句如下所示:

    update myTable
    set col = {some value}
    where id = {your id}
    AND
    timestampcolumn = {the timestamp the row had when you read it in}
    

    如果有人在您读入后更改了该行,它将具有不同的时间戳,并且没有记录与您的 WHERE 子句条件匹配,因此您的更新将失败。然后,您可以决定要做什么。

    当您使用 SQL-Server(或 Oracle 或任何真正的客户端-服务器架构)时,您可以在客户端 PC 上拔下插头,而不会对服务器产生任何不利影响。

    【讨论】:

    • PS 这叫做“乐观锁定”,如果你想用谷歌搜索的话。
    • 乐观并发是一回事,但有真正需要公开和跟踪“锁”的场景(注意:不是 RDMBS 锁)在记录级别。此外,IIRC“rowversion”现在被称为“timestamp”(尽管在 SQL Server 中它们是同义词)
    • @ShellShock - 这是乐观的并发 - 没有那里有任何“锁定”。
    • 谢谢蒂姆。这完全有道理。我似乎总是在寻找解决问题的最难方法:)
    • @Marc:我同意你的观点,有时需要行级悲观锁定。但是行级悲观锁定(不同于页级悲观锁定)在 SQLServer、AFAIK 中实际上是不可能的;在繁忙的多用户场景中,用户争用相同的页面,如果不是相同的行,页面锁定是不行的,因为太多的人必须等待。 stackoverflow.com/questions/1483725/…
    【解决方案2】:

    在 C# 中,您可以使用 try-catch-finally 块,并在 finally 中进行整理。 (无论如何都应该执行)。

    您可以通过创建一个实现IDisposable 的类来实现大致相同的目的,该类获取锁,并在调用 disposing 方法时释放它。然后,每当您使用该类(获得锁)时,放入 using 块

    using (RecordLockingThing myThing = new RecordLockingThing())
    {
        //DoStuff
    }
    //Now myThing is out of scope, and will have been disposed.
    

    只要确保你的RecordLockingThing在disposing方法中正确安全地释放锁。

    另一种策略可能不是将记录标记为在打开时锁定它们,而是将它们标记为已编辑(或增加修订号)。然后您可以允许多人打开记录。当有人提交编辑时,让他们也提交修订号,如果匹配,提交编辑并增加修订,如果没有,报告“空中冲突”,或者丢弃编辑(不是很友好),或者尝试并让用户合并记录。

    如果与读取相比,编辑很少见,则第二种策略在实践中会更有用,因为您永远不会阻止用户至少查看记录,并且不存在孤立锁的风险。

    【讨论】:

      【解决方案3】:

      为了处理意外关闭的问题,我通常会给每个逻辑锁一个到期时间;这可能意味着(例如)您需要一个 LockOwnerId 和一个 LockExpiry 列,但这通常不是问题。如果用户仍在屏幕中,您的应用程序始终可以延长锁定,但这意味着如果机器只是断开网络,记录将在几分钟内隐式变为可用。另一种选择是允许关键用户砸锁。

      重新用户终止应用;只需跟踪您拥有的锁并删除它们;p

      【讨论】:

        【解决方案4】:

        我在 DMS 应用程序中遇到了类似的问题,在服务器端我使用了一个会话对象,一个活跃用户的集合,每个客户端每 5 分钟更新一次会话对象。因此,如果一行被锁定并且锁定它的用户不再在会话对象中,那么我将其释放。对于这个解锁,我在服务器端运行一个后台线程,每分钟扫描一次锁定的行。

        【讨论】:

          【解决方案5】:

          我很惊讶似乎没有人提到sp_getapplock,而且是亲戚。如果您保持对服务器的单个连接打开(并且锁由 Session 拥有,而不是由 Transaction 拥有),则将保持锁。如果连接断开(例如客户端机器崩溃),那么与 SQL Server 中的所有内部锁一样,这些锁将被释放。

          本质上,这是让 SQL Server 使用它在内部用于应用程序目的的相同锁定机制的一种方式。

          不过,正如我所说,一个小问题是您必须保持与服务器的连接打开。所以也许更适合,比如说,最多 50 个客户,而不是你有 1000 个客户参与。我还要补充一点——我还没有使用这些设施构建生产系统。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-07-27
            • 1970-01-01
            • 1970-01-01
            • 2019-01-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多