1. Redis 中影响并行操作的阻塞 / 独占操作列表汇总
在 Redis 中,由于其单线程执行命令的特性(核心命令处理是单线程,后台持久化等是多线程),严格来说不存在传统数据库意义上的 “锁表” 概念。但某些操作会因为执行时间过长,阻塞整个 Redis 服务,导致其他命令无法并行执行(进入等待队列),这种 “阻塞效应” 可能被理解为 “锁表”。
以下是容易导致阻塞、影响并行操作的 Redis 操作:
| 操作类型 | 操作命令及说明 | 操作的数据类型 |
|---|---|---|
| 排他锁操作(显式锁) | 1. SET key value NX EX/PX:通过NX(不存在则设值)实现排他锁,EX/PX设过期时间;2. Redlock 相关命令:基于多 Redis 节点的分布式锁逻辑(如客户端实现的 Redlock 加锁 / 释放锁) |
无特定数据类型(锁键通常用 String 存储锁值) |
| 阻塞式数据读取操作 | 1. List:BLPOP(左侧阻塞弹出)、BRPOP(右侧阻塞弹出)、BRPOPLPUSH(阻塞弹出并插入另一键);2. Sorted Set: BZPOPMIN(阻塞弹出最小分数元素)、BZPOPMAX(阻塞弹出最大分数元素) |
List、Sorted Set |
| 长耗时原子批量操作 | 1. KEYS pattern:遍历所有键匹配 pattern,无数据类型限制,元素量大时极耗时;2. Set: SMEMBERS key(获取所有元素,元素量大时耗时);3. Hash: HGETALL key(获取所有字段 + 值,字段量大时耗时);4. List: LRANGE key start end(获取指定范围元素,覆盖全量元素时耗时) |
所有键(KEYS)、Set、Hash、List |
| 键级排他性操作(删除 / 重命名) | 1. DEL key [key...]:原子删除一个或多个键,执行期间锁定目标键;2. RENAME key newkey/RENAMENX key newkey:原子重命名键,防止并发修改目标键 |
所有数据类型 |
| 事务执行阶段(EXEC) | MULTI(开启事务)+ EXEC(执行事务):事务内所有命令在EXEC阶段原子执行,单线程模型下阻塞后续所有请求 |
所有参与事务的键(无特定数据类型) |
| 分布式锁相关客户端操作 | 基于 Redis 实现的分布式锁(如SET NX + EX加锁 + Lua 脚本原子释放锁):锁的获取 / 释放具有排他性,同一资源锁无法并行获取 |
无特定数据类型(锁键通常为 String) |
2. Redis 中影响并行操作的阻塞 / 独占操作列表详解
-
耗时的单 Key 阻塞命令:执行时会阻塞 Redis 主线程,导致所有请求(无论操作哪个 Key)需等待命令完成,无法并行处理。
KEYS pattern:全量遍历所有 Key,数据量越大阻塞时间越长。DEL key(删除大 Key):若 Key 对应的数据结构(如大哈希、大列表)占用内存大,释放内存过程耗时,阻塞主线程。HGETALL key/HKEYS key/HVALS key:若哈希表包含大量字段,全量获取数据会阻塞主线程。LRANGE key start end(全量获取长列表):若列表元素数量多(如百万级),全量读取会阻塞。SMEMBERS key(全量获取大集合):集合元素量大时,全量返回数据阻塞主线程。ZRANGE key start end(全量获取大有序集合):有序集合元素多时,全量读取阻塞主线程。
-
Redis 事务操作(MULTI/EXEC):事务执行阶段会独占主线程,无法并行处理其他请求。
- 命令通过
MULTI入队时不阻塞,但EXEC触发事务执行后,Redis 会批量串行执行所有入队命令,期间主线程被占用,其他客户端的请求需等待事务完成才能处理。
- 命令通过
-
基于 Redis 的分布式锁实现:同一资源的锁竞争会导致操作无法并行,仅持有锁的客户端可执行操作。
- 原生命令:
SET key value NX EX(获取锁),同一时刻仅一个客户端能成功获取锁,其他客户端获取失败或循环重试(阻塞自身)。 - 框架实现:Redisson 的
RLock、Jedis 的分布式锁等,锁持有期间,其他线程 / 客户端对同一锁的获取操作会被阻塞(直到锁释放或超时),无法并行执行目标操作。
- 原生命令:
-
同一 Key 的原子操作(串行执行,非全局阻塞):针对同一 Key 的原子命令会串行处理,无法并行操作该 Key(不影响其他 Key)。
- 如
INCR/DECR(计数器)、HINCRBY(哈希增量)、LPUSH/LPOP(列表操作)、SADD/SREM(集合操作)等。 - 因 Redis 单线程特性,同一 Key 的操作需排队执行,同一时刻仅一个操作处理该 Key,其他针对该 Key 的操作需等待。
- 如
-
阻塞式列表操作(客户端级阻塞):列表为空时阻塞客户端,同一列表的消费操作无法并行。
BLPOP key timeout/BRPOP key timeout:列表为空时阻塞当前客户端,直到列表有元素或超时;多个客户端阻塞等待同一列表时,元素到来仅唤醒一个客户端,其他继续阻塞。BRPOPLPUSH source destination timeout:功能类似,列表为空时阻塞客户端,同一源列表的操作无法并行消费。
-
Redis Cluster 中的槽位相关阻塞操作:涉及槽位的操作会锁定对应分片,影响该分片 Key 的并行处理。
- 槽位迁移(Resharding):迁移槽位期间,目标槽位会被临时锁定,针对该槽位内 Key 的读写操作会阻塞,直到迁移完成。
- 全集群扫描(如未指定槽位的 KEYS/SCAN):遍历所有分片时,各分片需依次执行扫描,过程中可能导致分片短暂阻塞,影响并行操作。
-
大 Value 的序列化 / 反序列化(间接阻塞):大对象处理耗时,延长命令执行时间,阻塞主线程。
- 若 Value 是大 JSON、大二进制数据(如几 MB 以上),执行
GET(反序列化)、SET(序列化)时,数据处理耗时较长,主线程被占用,其他请求需等待,间接影响并行性。
3. 为什么这些操作会 “阻塞并行”?
Redis 核心命令处理是单线程,所有命令按顺序在一个线程中执行。正常情况下,单个命令(如
GET/SET)执行时间极短(微秒级),看起来像 “并行”;但上述操作执行时间过长(毫秒甚至秒级),会导致后续命令排队等待,表现为 “锁表” 效果。4. 避免阻塞的原则
- 禁止在生产环境使用
KEYS,改用SCAN(渐进式遍历,分批返回,不阻塞)。 - 全量操作(如
FLUSHDB)优先用异步版本。 - 避免一次性获取大量数据(如
LRANGE限制返回数量,用HSCAN/SSCAN分批获取哈希 / 集合数据)。 - 控制 Lua 脚本和事务的复杂度,避免长时间执行。
- 监控 Redis 慢查询(通过
slowlog get),及时发现并优化耗时命令。
posted on
浙公网安备 33010602011771号