9

这是我最初的问题:

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

答案原来很简单——

虽然无法触发显式锁,但任何 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
4

3 回答 3

7

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

于 2011-07-12T18:36:35.827 回答
6

SELECT向您的:添加锁定提示

SELECT COUNT(*) FROM artist WITH (TABLOCKX)

并把你INSERT的交易。

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

于 2011-07-12T16:00:42.740 回答
4

READ UNCOMMITTED当连接处于隔离级别时,没有直接的方法来强制锁定。

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

编辑:

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

于 2011-07-12T17:17:54.070 回答