当前位置: 首页 > 面试题库 >

SELECT…ORDER BY XXX LIMIT 1 FOR UPDATE将锁定多少行?

公羊喜
2023-03-14
问题内容

我有以下结构的查询:

SELECT ..... WHERE status = 'QUEUED' ORDER BY position ASC LIMIT 1 FOR UPDATE;

这是InnoDB表上的单表SELECT语句。字段position(INT NOT NULL)上有一个索引。状态为ENUM,并且也已建立索引。

SELECT ... FOR UPDATE手册页说,它锁定了它读取的所有行。我是否正确理解,在这种情况下,只有一行会被锁定?或更确切地说,它将锁定整个表?

有可能确定哪些行将被EXPLAIN查询锁定吗?如果是,怎么办?对空表的查询解释如下:

1;'SIMPLE';'job';'index';<null>;'index_position';[34,...];<null>;1;'Using where'

问题答案:

这是一个很好的问题。InnoDB是行级锁定引擎,但必须设置其他锁定以确保二进制日志(用于复制;时间点恢复)的安全性。要开始对其进行解释,请考虑以下(朴素的)示例:

session1> START TRANSACTION;
session1> DELETE FROM users WHERE is_deleted = 1; # 1 row matches (user_id 10), deleted.
session2> START TRANSACTION;
session2> UPDATE users SET is_deleted = 1 WHERE user_id = 5; # 1 row matches.
session2> COMMIT;
session1> COMMIT;

由于语句仅在提交后才写入二进制日志,因此将在从属会话#2上首先应用该语句,并将产生不同的结果, 从而导致数据损坏

因此,InnoDB要做的是设置其他锁。如果is_deleted已建立索引,那么在session1提交之前,其他任何人都无法修改 或将 记录
插入到 where的记录 范围
is_deleted=1。如果上没有索引is_deleted,则InnoDB需要锁定整个表中的每一行,以确保重播顺序相同。您可以将其视为
锁定间隙这与直接从行级锁定掌握的概念不同


在这种情况下ORDER BY position ASC,InnoDB需要确保在最低键值和可能的“特殊”最低值之间不能修改任何新行。如果您做得ORDER BY position DESC很好,则没有人可以插入此范围。

解决方案如下:

  • 基于语句的二进制日志很烂。我真的很期待将来我们都切换到基于行的二进制日志记录(可从MySQL 5.1获得,但默认情况下未启用)。

  • 使用基于行的复制时,如果将隔离级别更改为“已提交读”,则仅需要锁定匹配的一行。

  • 如果要成为受虐狂,还可以使用基于语句的复制来打开innodb_locks_unsafe_for_binlog。

4月22日更新 :要复制并粘贴我改进的测试用例版本(它不是在空白中搜索):

session1> CREATE TABLE test (id int not null primary key auto_increment, data1 int, data2 int, INDEX(data1)) engine=innodb;
Query OK, 0 rows affected (0.00 sec)

session1> INSERT INTO test VALUES (NULL, 1, 2), (NULL, 2, 1), (5, 2, 2), (6, 3, 3), (3, 3, 4), (4, 4, 3);
Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

session1> start transaction;
Query OK, 0 rows affected (0.00 sec)

session1> SELECT id FROM test ORDER BY data1 LIMIT 1 FOR UPDATE;
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

session2> INSERT INTO test values (NULL, 0, 99); # blocks - 0 is in the gap between the lowest value found (1) and the "special" lowest value.

# At the same time, from information_schema:

localhost information_schema> select * from innodb_locks\G
*************************** 1. row ***************************
    lock_id: 151A1C:1735:4:2
lock_trx_id: 151A1C
  lock_mode: X,GAP
  lock_type: RECORD
 lock_table: `so5694658`.`test`
 lock_index: `data1`
 lock_space: 1735
  lock_page: 4
   lock_rec: 2
  lock_data: 1, 1
*************************** 2. row ***************************
    lock_id: 151A1A:1735:4:2
lock_trx_id: 151A1A
  lock_mode: X
  lock_type: RECORD
 lock_table: `so5694658`.`test`
 lock_index: `data1`
 lock_space: 1735
  lock_page: 4
   lock_rec: 2
  lock_data: 1, 1
2 rows in set (0.00 sec)

# Another example:
select * from test where id < 1 for update; # blocks


 类似资料:
  • 问题内容: 我有一个复杂的选择查询和一个巨大的表。 我正在运行此语句,同时有一条语句到达并尝试 更新 表。 恕我直言-更新需要 排他 锁-因此update语句将不得不 等待 select命令完成。 我对吗 ? 我该怎么才能做到:执行复杂的, 并且 也让命令运行( 目前我不在乎脏数据 ) 问题答案: 是的-在一定程度上。 多长时间,取决于事务的隔离级别上共享锁认为: -完全不获取共享锁-不被阻止 -

  • 假设表 和 都有一行,则查询 应该有两个行级锁(一个在 A 上,一个在 B 上)。是否有任何定义的顺序来获取锁?有没有办法要求表 b 中的锁在来自 a 的锁之前获得锁定(以避免与其他事务死锁)?

  • 问题内容: 在生产数据库中,我们每小时运行以下伪代码SQL批查询: 现在,此查询本身并不需要很快,但是我注意到它已经被锁定,即使它只是从中读取。这使得其他一些非常简单的查询需要大约25秒(这是其他查询所花费的时间)。 然后我发现InnoDB表实际上是由SELECT锁定的!https://www.percona.com/blog/2006/07/12/insert- into-select-perf

  • 问题内容: 我在这里思考:如果您有2个线程执行需要同步的FAST操作,那么非阻塞方法不是比阻塞/上下文切换方法更快/更好的方法吗? 非阻塞的意思是: while(true){如果(checkAndGetTheLock())中断;} 如果您有太多线程在锁中循环,我唯一想到的就是饥饿(CPU耗尽)。 如何平衡一种方法与另一种方法? 问题答案: 以下是 Java Concurrency in Pract

  • 我有下面的hql查询。我想选择而不锁定表部门,员工,以便其他查询并发从这些表中检索记录,即使以下查询正在运行/执行 我试过了,但还是有锁

  • 我有一些@keyframes动画和相关的CSS类名。例如,类似于: 如果我显示多个应用了.fade类的元素,有没有办法将它们锁定在一起,使它们在动画关键帧中共享相同的位置? 例如,如果我从一个开始,并且碰巧在第一个在动画中达到50%时添加了第二个,那么我希望第二个从50%开始,这样所有内容都会一起淡入淡出。