理论(下方有实操)
for update skip locked
- 官方文档:InnoDB Locking Reads
- 语法:在select语句后附加
for update skip locked
- 功能:若目标行未被其他会话锁定,则可对其加锁;若已被锁定,则跳过该行。
- 应用场景:主要用于减少锁资源竞争导致的阻塞。
- 常见于MySQL作为消息队列存储组件时,尤其是在有多个消费者(consumer)的情况下。多个工作进程(worker)同时检查任务队列中的任务,确保每个任务只被一个工作进程处理,同时避免其他工作进程因等待锁而阻塞。(这里的工作进程指的是不断循环查询队列任务的过程,使用毫秒级到分钟级的死循环检测表中是否有待处理的队列任务,加锁是为了防止多个消费者同时处理同一个队列任务,从而引发的并发问题)。
- 简而言之,加锁是为了避免多个消费者重复消费相同数据,而
skip locked
则是为了确保其他进程在每次扫描到被锁定的数据时能够跳过,避免因排它锁而阻塞,保证任务探测的快速执行。 - 注意事项:
- 不适用于需要在并发条件下通过加锁来保证数据准确性的场景,因为它会跳过已锁定的数据行。(官方文档指出:Queries that skip locked rows return an inconsistent view of the data. SKIP LOCKED is therefore not suitable for general transactional work. However, it may be used to avoid lock contention when multiple sessions access the same queue-like table.)。
- 即使使用了
skip locked
,没有使用skip locked
的排它锁(X锁或S锁)在遇到锁定的数据时仍然可能发生阻塞。 - 此特性仅在MySQL 8.0及以上版本中可用,MySQL 5.7会报语法错误:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'skip locked' at line 1。
nowait
- 官方文档:InnoDB Locking Reads
- 语法:在select语句后附加
nowait
- 功能:若目标行未被其他会话锁定,则可对其加锁;若已被锁定,则立即返回异常。
- 应用场景:与
skip locked
类似,但由于MySQL会返回异常,可能会导致编程语言层面的错误处理,因此应用场景较少。 - 注意事项:此特性仅在MySQL 8.0及以上版本中可用,MySQL 5.7会报语法错误:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'nowait' at line 1。
No BB,Show Code
成品用例
- 以消息队列为例,这是一个支持多渠道、多生产者、多消费者以及延时队列的任务记录表(对于记录失败任务的表,本文不作展开)。
``sql
jobs
CREATE TABLE(
idbigint unsigned NOT NULL AUTO_INCREMENT,
queuevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '不同渠道队列执行的优先级,可以是high,default,low等。',
payloadlongtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '队列进程所需要的数据,序列化后的json字符串,包含了框架所需要的序列化数据和业务数据。',
attemptstinyint unsigned NOT NULL COMMENT '记录任务已经被尝试执行的次数。每次任务失败后,attempts的值会递增。当attempts达到设定的最大重试次数时,任务将被标记为失败。',
reserved_atint unsigned DEFAULT NULL COMMENT '表示任务被锁定(reserved)的时间戳。当一个worker开始处理任务时,任务会被锁定,以防止其它 worker同时处理相同的任务。reserved_at存储的是记录被锁定时的时间戳。',
available_atint unsigned NOT NULL COMMENT '记录任务应该变为可执行状态的时间戳。将任务推送到队列时,可以选择延迟任务的执行时间。',
created_atint unsigned NOT NULL COMMENT '队列被创建的时间戳。',
id
PRIMARY KEY () USING BTREE,
jobs_queue_index
KEY(
queue`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW
转载请注明:MySQL for update skip locked 与 for update nowait | 胖虎的工具箱-编程导航