【问题标题】:How to explicitly lock a table in Microsoft SQL Server (looking for a hack - uncooperative client)如何在 Microsoft SQL Server 中显式锁定表(寻找黑客 - 不合作的客户端)
【发布时间】:2011-10-03 18:37:36
【问题描述】:

这是我最初的问题:

我试图弄清楚如何在 SQL Server 中强制执行 EXCLUSIVE 表锁。我需要解决不合作的读者(超出我的控制,封闭源代码的东西),它们明确地将他们的隔离级别设置为未提交的阅读。效果是,无论我在执行插入/更新时指定了多少锁和哪种隔离,客户端只需要设置正确的隔离并返回读取我正在进行的垃圾。 p>

原来答案很简单——

虽然无法触发显式锁,但任何 DDL 更改都会触发我正在寻找的锁。

虽然这种情况并不理想(客户端阻塞而不是见证可重复读取),但它比让客户端覆盖隔离并读取脏数据要好得多。这是带有虚拟触发锁定机制的完整示例代码

中奖了!

#!/usr/bin/env perl 使用测试::更多; 使用警告; 使用严格; 使用 DBI; 我的 ($dsn, $user, $pass) = @ENV{ map { "DBICTEST_MSSQL_ODBC_$_" } qw/DSN USER PASS/ }; 我的@coninf = ($dsn, $user, $pass, { 自动提交 => 1, LongReadLen => 1048576, 打印错误 => 0, 提升错误 => 1, }); 如果(!叉子){ 我的 $reader = DBI->connect(@coninf); $reader->do('SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED'); warn "READER $$: 等待创建表"; 睡觉1; 对于 (1..5) { is_deeply ( $reader->selectall_arrayref ('SELECT COUNT(*) FROM Artist'), [ [ 0 ] ], “阅读器 $$:在 db 中看不到任何内容,正在休眠一秒钟”。时间, ); 睡觉1; } 出口; } 我的 $writer = DBI->connect(@coninf); eval { $writer->do('DROP TABLE Artist') }; $writer->do('CREATE TABLE 艺术家 (name VARCHAR(20) NOT NULL PRIMARY KEY)'); $writer->do(do('DISABLE TRIGGER _lock_artist ON 艺术家'); 睡觉1; is_deeply ( $writer->selectall_arrayref ('SELECT COUNT(*) FROM Artist'), [ [ 0 ] ], '没有行开头', ); $writer->开始工作; $writer->prepare("INSERT INTO Artist VALUES ('bupkus')")->execute; # 这就是我们锁定的方式 $writer->do('ENABLE TRIGGER _lock_artist ON 艺术家'); $writer->do('禁用触发 _lock_artist ON 艺术家'); is_deeply ( $writer->selectall_arrayref ('SELECT COUNT(*) FROM Artist'), [ [ 1 ] ], '作家看到插入的行', ); # 延迟阅读器 睡觉 2; $writer->回滚; # 不应影响阅读器 睡觉 2; is_deeply ( $writer->selectall_arrayref ('SELECT COUNT(*) FROM Artist'), [ [ 0 ] ], '没有承诺(作家)', ); 等待; 完成测试;

结果:

阅读器 27311:等待在 mssql_isolation.t 第 27 行创建表。 好的 1 - 阅读器 27311:在 db 中看不到任何东西,睡了一秒钟 1310555569 ok 1 - 没有行开始 ok 2 - Writer 看到插入的行 好的 2 - 阅读器 27311:在 db 中看不到任何东西,睡了一秒钟 1310555571 好的 3 - 阅读器 27311:在 db 中看不到任何东西,睡了一秒钟 1310555572 好的 3 - 没有承诺(作家) 好的 4 - 阅读器 27311:在 db 中看不到任何东西,睡了一秒钟 1310555573 好的 5 - 阅读器 27311:在 db 中看不到任何东西,睡了一秒钟 1310555574

【问题讨论】:

    标签: sql-server tsql table-locking


    【解决方案1】:

    执行此操作的一种 hack hack hack 方法是强制对表进行操作,该操作在表上采用 SCH-M 锁定,即使在未提交的 READ 隔离级别下也会阻止对表的读取。例如,在您的操作中执行 ALTER TABLE REBUILD 之类的操作(可能在特定的空分区上以减少性能影响)将阻止对表的所有并发访问,直到您提交为止。

    【讨论】:

    • 哇哦,现在这是我喜欢的东西。将在下班后进行试验,然后报告。
    • 这确实是怎么做的,而且它甚至不是太hacky。我什至会说非常优雅:) 请参阅原始问题的编辑以获取答案
    【解决方案2】:

    当连接处于READ UNCOMMITTED 隔离级别时,无法直接强制锁定。

    一种解决方案是在正在读取的表上创建提供READCOMMITTED 表提示的视图。如果您控制读者使用的表名,这可能非常简单。否则,您将有很多苦差事,因为您必须修改编写器以写入新表或在视图上创建 INSTEAD OF INSERT/UPDATE 触发器。

    编辑:

    Michael Fredrickson 正确地指出,简单地定义为从带有表提示的基表中选择的视图不需要任何可更新的触发器定义。如果您要重命名现有的有问题的表并用视图替换它们,那么第三方客户端应该不会更聪明。

    【讨论】:

    • 我同意...这是满足要求的一个很好的解决方案。将艺术家表重命名为 tblArtist,然后简单地在表 tblArtist 周围创建一个名为艺术家的视图,并在视图中使用锁定提示来覆盖客户端正在执行的操作。
    • @M. Fredrickson:客户端同时读取和写入所述表,视图也不可行。
    • @Peter:这并没有减少黑客攻击......但是这个视图将满足“可更新视图”的要求,并且可以插入/更新/删除。
    • +1 for Updatable View 回答,完全回答了这个问题,而不使用以后可能会改变的行为(DDL 改变)。在我使用的系统中,如果我们必须提供直接 SQL 访问,它只能通过视图来提供,并且我们确保我们有适当的触发器来在数据更新表之前验证数据。
    【解决方案3】:

    为您的SELECT 添加锁定提示:

    SELECT COUNT(*) FROM artist WITH (TABLOCKX)

    并将您的INSERT 放入事务中。

    如果您的初始语句在显式事务中,SELECT 将在处理之前等待锁定。

    【讨论】:

    • 由于他的问题在于隔离级别,只需使用WITH (READCOMMITTED) 作为提示就足够了。
    • @Peter - 你不能更改查询吗?
    • 封闭源第三方软件的哪一部分不清楚? :)))
    • +1 因为我正在寻找一种方法来模拟由锁定表引起的读取超时! (这是一个单元测试)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多