【问题标题】:MySQL exclusive lockMySQL排他锁
【发布时间】:2020-03-25 17:39:08
【问题描述】:

我们有以下表格:

mysql> desc journeys ;
+---------------+------------+------+-----+---------+-------+
| Field         | Type       | Null | Key | Default | Extra |
+---------------+------------+------+-----+---------+-------+
| journey_id    | char(36)   | NO   | PRI | NULL    |       |
| is_completed  | tinyint(1) | NO   |     | 0       |       |
| user_id       | char(36)   | NO   |     | NULL    |       |
| created_at    | datetime   | NO   |     | NULL    |       |
| updated_at    | datetime   | NO   |     | NULL    |       |
| pack_id       | char(36)   | YES  | MUL | NULL    |       |
| family_id     | char(36)   | YES  | MUL | NULL    |       |
+---------------+------------+------+-----+---------+-------+

mysql> desc packs ;
+---------------+------------+------+-----+---------+-------+
| Field         | Type       | Null | Key | Default | Extra |
+---------------+------------+------+-----+---------+-------+
| pack_id       | char(36)   | NO   | PRI | NULL    |       |
| is_published  | tinyint(1) | NO   |     | 0       |       |
| order         | int(11)    | NO   |     | NULL    |       |
| created_at    | datetime   | NO   |     | NULL    |       |
| updated_at    | datetime   | NO   |     | NULL    |       |
| family_id     | char(36)   | NO   | MUL | NULL    |       |
+---------------+------------+------+-----+---------+-------+

隔离级别为 REPEATABLE_READ。

根据这里的词汇表: https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_exclusive_lock

排他锁是一种防止任何其他事务锁定同一行的锁。根据事务隔离级别,这种锁可能会阻止其他事务写入同一行,也可能会阻止其他事务读取同一行。

我们的逻辑如下所示(user_id 采用不同的值):

START TRANSACTION;
SELECT * FROM journeys WHERE user_id = <user_id> FOR UPDATE ;
# COMMIT;

下面有一些测试。我打开一个终端窗口(终端 #1)并执行以下语句:

START TRANSACTION;
SELECT * FROM journeys WHERE user_id = user_id_1 FOR UPDATE ;

然后我打开第二个终端(终端#2)窗口并执行以下语句:

START TRANSACTION;
SELECT * FROM journeys WHERE user_id = user_id_2 FOR UPDATE ;

2 号终端现在停止,因为我们从未在 1 号终端上提交交易。

我的假设是因为第一个终端 #1 上的条件与终端 #2 中的语句不同,即第二个终端不会等待第一个终端提交。我的假设基于排他锁的定义,它说排他锁阻止任何其他事务锁定同一行。这是一个错误的假设吗?如果是,如何实现锁定我们在第一个条件下拥有的行?

在条件中使用主键时似乎有所不同。在下面的情况下,终端 #2 不等待终端 #1 提交。

terminal #1
START TRANSACTION;
SELECT * FROM journeys WHERE journey_id = journey_id_1 FOR UPDATE ;

2 号终端的声明

terminal #2
START TRANSACTION;
SELECT * FROM journeys WHERE journey_id = journey_id_2 FOR UPDATE ;

当我们的条件不包括主键时,排他锁究竟会发生什么?我们要锁定整张桌子吗?

【问题讨论】:

    标签: mysql transactions locking innodb transaction-isolation


    【解决方案1】:

    是的,当您对像user_id 这样的未索引列有条件时,您将锁定表中的所有行。

    锁定适用于所有“已检查”的行。您的条件WHERE user_id = &lt;user_id&gt; 必须检查所有表中的行,并一一测试它们是否与&lt;user_id&gt; 的值匹配。

    这两个查询都在检查整个行集,即使它们正在搜索 &lt;user_id&gt; 的不同特定值,所以它们会发生冲突。

    如果您在 user_id 列上有一个索引,那么 MySQL 将首先使用该索引来查找匹配的行,然后只有匹配的行会成为检查的行,因此会被锁定。

    这与事务隔离级别无关。这些类型的锁出现在所有事务隔离级别中。

    【讨论】:

    • 这对@Bill Karwin 很有帮助!因此,通过向 user_id 添加索引,我们可以解决我们的用例。
    • 如果您添加索引,并且 MySQL 决定使用它。在某些情况下,MySQL 优化器认为不值得使用索引。例如,如果您要搜索的值出现在 > 20% 的行中。
    猜你喜欢
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多