【发布时间】:2017-07-08 02:18:53
【问题描述】:
假设我们有以下数据库表:
create table department (
id bigint not null,
budget bigint not null,
name varchar(255),
primary key (id)
) ENGINE=InnoDB
create table employee (
id bigint not null,
name varchar(255),
salary bigint not null,
department_id bigint, primary key (id)
) ENGINE=InnoDB
alter table employee
add constraint FK_department_id
foreign key (department_id)
references department (id)
我们有 2 个departments:
insert into department (name, budget, id)
values ('Hypersistence', 100000, 1)
insert into department (name, budget, id)
values ('Bitsystem', 10000, 2)
还有 3 个employees 在第一部门:
insert into employee (department_id, name, salary, id)
values (1, 'John Doe 0', 30000, 0)
insert into employee (department_id, name, salary, id)
values (1, 'John Doe 1', 30000, 1)
insert into employee (department_id, name, salary, id)
values (1, 'John Doe 2', 30000, 2)
假设我们有两个并发用户:Alice 和 Bob。
首先,Alice 锁定属于第一个 department 的所有员工,并获得该特定 department 的工资总和:
SELECT *
FROM employee
WHERE department_id = 1
FOR UPDATE
SELECT SUM(salary)
FROM employee
where department_id = 1
现在,与此同时,预计 Bob 不能使用相同的 department_id 插入新的 employee:
insert into employee (department_id, name, salary, id)
values (1, `Carol`, 9000, 4)
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:
Lock wait timeout exceeded; try restarting transaction
因此,锁阻止了 Bob 针对同一谓词发出插入。
但是,即使 Bob 尝试在不同的 department 中插入 employee,也会引发相同的异常:
insert into employee (department_id, name, salary, id)
values (2, `Dave`, 9000, 5)
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:
Lock wait timeout exceeded; try restarting transaction
最后一个插入语句正在使用第二个department_id,因此该行不应与我们为其获取谓词锁的选择语句重叠。
为什么 MySQL 会阻止与第一个事务获取的谓词锁不重叠的第二个插入?
在 SQL Server 上也可以观察到相同的行为。
更新
将隔离级别更改为 READ_COMMITTED 时,谓词锁不会阻止 Bob 发出的两个插入语句中的任何一个。
如果考虑到this Percona blog post 的以下陈述,这可以解释:
在 REPEATABLE READ 中,事务期间获得的每个锁都被持有 在交易期间。
在 READ COMMITTED 中,与扫描不匹配的锁被释放 在 STATEMENT 完成后。
但是,找出为什么谓词锁定像在 REPEATABLE READ 上那样工作仍然很有趣。
【问题讨论】:
标签: mysql concurrency transactions locking