这不是一个完整/正确的MySQL查询只有伪代码:
Select *
from Notifications as n
where n.date > (CurrentDate-10 days)
limit by 1
FOR UPDATE
Run Code Online (Sandbox Code Playgroud)
http://dev.mysql.com/doc/refman/5.0/en/select.html声明:如果对使用页锁或行锁的存储引擎使用FOR UPDATE,则查询检查的行将被写入锁定,直到当前交易结束
这里只有一个由MySQL锁定的记录或它必须扫描的所有记录才能找到单个记录吗?
Fra*_*ans 98
我们为什么不试试呢?
设置数据库
CREATE DATABASE so1;
USE so1;
CREATE TABLE notification (`id` BIGINT(20), `date` DATE, `text` TEXT) ENGINE=InnoDB;
INSERT INTO notification(id, `date`, `text`) values (1, '2011-05-01', 'Notification 1');
INSERT INTO notification(id, `date`, `text`) values (2, '2011-05-02', 'Notification 2');
INSERT INTO notification(id, `date`, `text`) values (3, '2011-05-03', 'Notification 3');
INSERT INTO notification(id, `date`, `text`) values (4, '2011-05-04', 'Notification 4');
INSERT INTO notification(id, `date`, `text`) values (5, '2011-05-05', 'Notification 5');
Run Code Online (Sandbox Code Playgroud)
现在,启动两个数据库连接
连接1
BEGIN;
SELECT * FROM notification WHERE `date` >= '2011-05-03' FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
连接2
BEGIN;
Run Code Online (Sandbox Code Playgroud)
如果MySQL锁定所有行,则以下语句将被阻止.如果它只锁定它返回的行,则不应该阻塞.
SELECT * FROM notification WHERE `date` = '2011-05-02' FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
确实它确实阻止了.
有趣的是,我们也无法添加可读的记录,即
INSERT INTO notification(id, `date`, `text`) values (6, '2011-05-06', 'Notification 6');
Run Code Online (Sandbox Code Playgroud)
块也是!
我不能确定在这一点上MySQL是否会继续并在锁定一定百分比的行时锁定整个表,或者确保SELECT ... FOR UPDATE查询结果永远不会被另一个事务更改时实际上非常智能(用INSERT,UPDATE或DELETE),而锁被保持.
小智 31
该线程很旧,只是为了分享我关于@Frans 执行的上述测试的两分钱
连接 1
BEGIN;
SELECT * FROM notification WHERE `date` >= '2011-05-03' FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
连接 2
BEGIN;
SELECT * FROM notification WHERE `date` = '2011-05-02' FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
并发事务 2 肯定会被阻塞,但原因不是事务 1 持有整个表的锁。以下解释了幕后发生的事情:
首先,InnoDB 存储引擎的默认隔离级别是Repeatable Read. 在这种情况下,
1- 当 where 条件中使用的列未编入索引时(如上例):
引擎有义务执行全表扫描以过滤掉不符合条件的记录。每一行已经扫描锁定在首位。稍后,MySQL 可能会释放那些与 where 子句不匹配的记录上的锁。这是对性能的优化,但是,这种行为违反了 2PL 约束。
如前所述,当事务 2 开始时,它需要为检索到的每一行获取 X 锁,尽管只有一条记录 (id = 2) 与 where 子句匹配。最终事务 2 将等待第一行的 X 锁(id = 1),直到事务 1 提交或回滚。
2- 当 where 条件中使用的列是主索引时
只有满足条件的索引条目才会被锁定。这就是为什么在评论中有人说某些测试没有被阻止。
3 - 当 where 条件中使用的列是索引但不唯一时
这个案子比较复杂。1) 索引条目被锁定。2) 一个 X 锁附加到相应的主索引。3) 两个间隙锁被附加到匹配搜索条件的记录之前和之后的不存在的条目上。
con*_*cat 20
我知道这个问题很老了,但是我想分享一些相关测试的结果,我已经用索引列完成了这些测试产生了一些非常奇怪的结果.
表结构:
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`notid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)
插入12行INSERT INTO t1 (notid) VALUES (1), (2),..., (12).在连接1上:
BEGIN;
SELECT * FROM t1 WHERE id=5 FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
在连接2上,以下语句被阻止:
SELECT * FROM t1 WHERE id!=5 FOR UPDATE;
SELECT * FROM t1 WHERE id<5 FOR UPDATE;
SELECT * FROM t1 WHERE notid!=5 FOR UPDATE;
SELECT * FROM t1 WHERE notid<5 FOR UPDATE;
SELECT * FROM t1 WHERE id<=4 FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
奇怪的部分是,SELECT * FROM t1 WHERE id>5 FOR UPDATE;在不堵塞,也不是任何的
...
SELECT * FROM t1 WHERE id=3 FOR UPDATE;
SELECT * FROM t1 WHERE id=4 FOR UPDATE;
SELECT * FROM t1 WHERE id=6 FOR UPDATE;
SELECT * FROM t1 WHERE id=7 FOR UPDATE;
...
Run Code Online (Sandbox Code Playgroud)
我还想指出,当来自连接1的查询中的条件与非索引行匹配时,似乎整个表都被锁定.例如,当连接1执行时,阻止所有选择查询和来自连接2的查询.WHERESELECT * FROM t1 WHERE notid=5 FOR UPDATEFOR UPDATEUPDATE
- 编辑 -
这是一个相当具体的情况,但它是我能找到的唯一表现出这种行为:
连接1:
BEGIN;
SELECT *, @x:=@x+id AS counter FROM t1 CROSS JOIN (SELECT @x:=0) b HAVING counter>5 LIMIT 1 FOR UPDATE;
+----+-------+-------+---------+
| id | notid | @x:=0 | counter |
+----+-------+-------+---------+
| 3 | 3 | 0 | 9 |
+----+-------+-------+---------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
从连接2:
SELECT * FROM t1 WHERE id=2 FOR UPDATE; 被封锁;
SELECT * FROM t1 WHERE id=4 FOR UPDATE;是不是堵塞.
El *_*obo 10
您发布的文档页面中的以下链接提供了有关锁定的更多信息.在这个页面中
SELECT ... FOR UPDATE读取最新的可用数据,在其读取的每一行上设置独占锁.因此,它设置搜索的SQL UPDATE将在行上设置的相同锁.
这似乎很清楚,它必须扫描所有行.
小智 6
从mysql官方文档:
锁定读取,UPDATE或DELETE通常会对在处理SQL语句时扫描的每个索引记录设置记录锁定。语句中是否存在排除行的条件并不重要。
对于Frans的答案中讨论的情况,所有行均被锁定,因为在sql处理期间进行了表扫描:
如果没有适合您的语句的索引,并且MySQL必须扫描整个表以处理该语句,则表的每一行都将被锁定,从而阻塞其他用户对表的所有插入。创建良好的索引很重要,这样您的查询就不必不必要地扫描很多行。
在此处检查最新文档:https : //dev.mysql.com/doc/refman/8.0/zh-CN/innodb-locks-set.html