MySQL排他锁
MySQL exclusive lock
我们有以下 tables:
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;
下面是一些测试。我打开一个终端 window(终端#1)并执行以下语句:
START TRANSACTION;
SELECT * FROM journeys WHERE user_id = user_id_1 FOR UPDATE ;
然后我打开第二个终端(终端 #2)window 并执行以下语句:
START TRANSACTION;
SELECT * FROM journeys WHERE user_id = user_id_2 FOR UPDATE ;
2 号终端现在停止了,因为我们从未在 1 号终端上提交事务。
我的假设是,因为第一个终端 #1 上的条件与终端 #2 中的声明不同,所以第二个终端不会等待第一个终端提交。我的假设基于独占锁的定义,它表示独占锁可防止任何其他事务锁定同一行。这是一个错误的假设吗?如果是,如何实现锁定我们在第一个条件下的行?
好像我在condition里用主键的时候不一样。在下面的情况下,终端 #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 ;
当我们遇到不包含主键的条件时,独占锁究竟会发生什么?我们要锁定整个 table 吗?
是的,当您在 user_id
.
等未索引列上有条件时,您将锁定 table 中的所有行
锁定适用于所有 "examined" 行。您的条件 WHERE user_id = <user_id>
必须检查 所有 中的行 table,并逐一测试它们是否与 <user_id>
的值匹配。
两个查询都在检查整组行,即使它们正在搜索 <user_id>
的不同特定值,所以它们会发生冲突。
如果你在 user_id
列上有一个索引,那么 MySQL 将首先使用该索引找到匹配的行,然后只有匹配的行才会成为已检查的行,因此会被锁定.
这与事务隔离级别无关。这些类型的锁出现在所有事务隔离级别中。
我们有以下 tables:
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;
下面是一些测试。我打开一个终端 window(终端#1)并执行以下语句:
START TRANSACTION;
SELECT * FROM journeys WHERE user_id = user_id_1 FOR UPDATE ;
然后我打开第二个终端(终端 #2)window 并执行以下语句:
START TRANSACTION;
SELECT * FROM journeys WHERE user_id = user_id_2 FOR UPDATE ;
2 号终端现在停止了,因为我们从未在 1 号终端上提交事务。
我的假设是,因为第一个终端 #1 上的条件与终端 #2 中的声明不同,所以第二个终端不会等待第一个终端提交。我的假设基于独占锁的定义,它表示独占锁可防止任何其他事务锁定同一行。这是一个错误的假设吗?如果是,如何实现锁定我们在第一个条件下的行?
好像我在condition里用主键的时候不一样。在下面的情况下,终端 #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 ;
当我们遇到不包含主键的条件时,独占锁究竟会发生什么?我们要锁定整个 table 吗?
是的,当您在 user_id
.
锁定适用于所有 "examined" 行。您的条件 WHERE user_id = <user_id>
必须检查 所有 中的行 table,并逐一测试它们是否与 <user_id>
的值匹配。
两个查询都在检查整组行,即使它们正在搜索 <user_id>
的不同特定值,所以它们会发生冲突。
如果你在 user_id
列上有一个索引,那么 MySQL 将首先使用该索引找到匹配的行,然后只有匹配的行才会成为已检查的行,因此会被锁定.
这与事务隔离级别无关。这些类型的锁出现在所有事务隔离级别中。