gpt4 book ai didi

mysql - 为什么 MySQL 显式谓词锁定不允许在谓词锁之外插入语句

转载 作者:行者123 更新时间:2023-11-29 01:36:20 26 4
gpt4 key购买 nike

假设我们有以下数据库表:

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 个部门:

insert into department (name, budget, id) 
values ('Hypersistence', 100000, 1)

insert into department (name, budget, id)
values ('Bitsystem', 10000, 2)

第一个部门有 3 个员工:

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,因此该行不应与我们为其获取谓词锁的 select 语句重叠。

为什么MySQL会阻止与第一个事务获取的谓词锁不重叠的第二个insert?

在 SQL Server 上也可以观察到相同的行为。

更新

当将隔离级别更改为 READ_COMMITTED 时,谓词锁不会阻止 Bob 发出的两个插入语句中的任何一个。

如果考虑到 this Percona blog post 中的以下陈述,就可以解释这一点:

In REPEATABLE READ every lock acquired during a transaction is heldfor the duration of the transaction.

In READ COMMITTED the locks that did not match the scan are releasedafter the STATEMENT completes.

但是,找出谓词锁定为何像在可重复读上那样工作仍然很有趣。

最佳答案

SELECT FOR UPDATE 锁定在 1 和 employee 表中的下一个值之间。由于没有下一个值,它一直锁定到 supremum pseudo-record。这可以在 information_schema.innodb_locks 中看到:

mysql> select * from innodb_locks;
+----------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+------------------------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+----------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+------------------------+
| 28275:1448:3:1 | 28275 | X | RECORD | `test`.`employee` | PRIMARY | 1448 | 3 | 1 | supremum pseudo-record |
| 28273:1448:3:1 | 28273 | X | RECORD | `test`.`employee` | PRIMARY | 1448 | 3 | 1 | supremum pseudo-record |
+----------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+------------------------+
2 rows in set, 1 warning (0.00 sec)

如果您稍微更改测试用例,以便在 dept-id=2 的员工中有一行,然后尝试为 dept-id=3 添加一个员工,它将起作用。示例:

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);


insert into department (name, budget, id)
values ('Hypersistence', 100000, 1);

insert into department (name, budget, id)
values ('Bitsystem', 10000, 2);

insert into department (name, budget, id)
values ('XX', 10000, 3);


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 (2, 'John Doe 2', 30000, 2);


start transaction;

SELECT *
FROM employee
WHERE department_id = 1
FOR UPDATE;


# new session

insert into employee (department_id, name, salary, id)
values (3, 'Dave', 9000, 5)

关于mysql - 为什么 MySQL 显式谓词锁定不允许在谓词锁之外插入语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42316848/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com