Why does SQL Server explicit predicate locking disallow INSERT statements outside of the predicate lock
假设我们有以下数据库表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | create table department ( id bigint not null, budget bigint not null, name varchar(255), primary key (id) ) create table employee ( id bigint not null, name varchar(255), salary bigint not null, department_id bigint, primary key (id) ) alter table employee add constraint FKbejtwvg9bxus2mffsm3swj3u9 foreign key (department_id) references department |
我们有 3 个
1 2 3 4 5 6 7 8 | insert into department (name, budget, id) values ('Department 1', 100000, 1) insert into department (name, budget, id) values ('Department 2', 75000, 2) insert into department (name, budget, id) values ('Department 3', 90000, 3) |
我们还有 3 个
1 2 3 4 5 6 7 8 | insert into employee (department_id, name, salary, id) values (1, 'CEO', 30000, 1) insert into employee (department_id, name, salary, id) values (1, 'CTO', 30000, 2) insert into employee (department_id, name, salary, id) values (2, 'CEO', 30000, 3) |
假设我们有两个并发用户:Alice 和 Bob。
首先,Alice 锁定所有属于第一个
1 2 3 4 | SELECT * FROM employee WITH (HOLDLOCK) WHERE department_id = 1 |
现在,与此同时,预计 Bob 不能使用相同的
1 2 | INSERT INTO employee WITH(NOWAIT) (department_id, name, salary, id) VALUES (1, 'Carol', 9000, 6) |
上面的插入语句以
但是,为什么下面的插入也被阻止了:
1 2 | INSERT INTO employee WITH(NOWAIT) (department_id, name, salary, id) VALUES (3, 'Dave', 9000, 7) |
这个插入语句使用了一个超出 Alice 谓词锁范围的
为什么 SQL Server
更新
通过向 FK 添加索引:
1 | create index IDX_DEPARTMENT_ID on employee (department_id) |
并将第一个和第二个
满足
在这个玩具示例中,仅在
Damien 说的是正确的..当您没有部门 id(谓词列)的索引时,范围会增加并且 HoldLock 意味着
HOLDLOCK means SERALIZABLE and therefore allows SELECTS, but blocks UPDATE and DELETES of the rows selected by T1, as well as any INSERT in the range selected by T1 .
所以在这种情况下,以下形式的索引会有所帮助,我的测试也证实了这一点
下面是我做的一个示例测试
在会话 1 中:
1 2 3 4 5 6 7 8 9 10 11 | create index nci_department_id on dbo.employee(department_id) include (id,name,salary) go begin tran SELECT * FROM employee WITH (HOLDLOCK) WHERE department_id = 1 |
在会话 2 中:
1 2 | INSERT INTO employee WITH(NOWAIT) (department_id, name, salary, id) VALUES (3, 'Dave', 9000, 7) |
现在上面的插入成功了
参考资料:
https://stackoverflow.com/a/7845331/2975396