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 中影响并行操作的阻塞 / 独占操作列表详解

  1. 耗时的单 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(全量获取大有序集合):有序集合元素多时,全量读取阻塞主线程。
  2. Redis 事务操作(MULTI/EXEC):事务执行阶段会独占主线程,无法并行处理其他请求。
    • 命令通过MULTI入队时不阻塞,但EXEC触发事务执行后,Redis 会批量串行执行所有入队命令,期间主线程被占用,其他客户端的请求需等待事务完成才能处理。
  3. 基于 Redis 的分布式锁实现:同一资源的锁竞争会导致操作无法并行,仅持有锁的客户端可执行操作。
    • 原生命令:SET key value NX EX(获取锁),同一时刻仅一个客户端能成功获取锁,其他客户端获取失败或循环重试(阻塞自身)。
    • 框架实现:Redisson 的RLock、Jedis 的分布式锁等,锁持有期间,其他线程 / 客户端对同一锁的获取操作会被阻塞(直到锁释放或超时),无法并行执行目标操作。
  4. 同一 Key 的原子操作(串行执行,非全局阻塞):针对同一 Key 的原子命令会串行处理,无法并行操作该 Key(不影响其他 Key)。
    • INCR/DECR(计数器)、HINCRBY(哈希增量)、LPUSH/LPOP(列表操作)、SADD/SREM(集合操作)等。
    • 因 Redis 单线程特性,同一 Key 的操作需排队执行,同一时刻仅一个操作处理该 Key,其他针对该 Key 的操作需等待。
  5. 阻塞式列表操作(客户端级阻塞):列表为空时阻塞客户端,同一列表的消费操作无法并行。
    • BLPOP key timeout/BRPOP key timeout:列表为空时阻塞当前客户端,直到列表有元素或超时;多个客户端阻塞等待同一列表时,元素到来仅唤醒一个客户端,其他继续阻塞。
    • BRPOPLPUSH source destination timeout:功能类似,列表为空时阻塞客户端,同一源列表的操作无法并行消费。
  6. Redis Cluster 中的槽位相关阻塞操作:涉及槽位的操作会锁定对应分片,影响该分片 Key 的并行处理。
    • 槽位迁移(Resharding):迁移槽位期间,目标槽位会被临时锁定,针对该槽位内 Key 的读写操作会阻塞,直到迁移完成。
    • 全集群扫描(如未指定槽位的 KEYS/SCAN):遍历所有分片时,各分片需依次执行扫描,过程中可能导致分片短暂阻塞,影响并行操作。
  7. 大 Value 的序列化 / 反序列化(间接阻塞):大对象处理耗时,延长命令执行时间,阻塞主线程。
    • 若 Value 是大 JSON、大二进制数据(如几 MB 以上),执行GET(反序列化)、SET(序列化)时,数据处理耗时较长,主线程被占用,其他请求需等待,间接影响并行性。

3. 为什么这些操作会 “阻塞并行”?

Redis 核心命令处理是单线程,所有命令按顺序在一个线程中执行。正常情况下,单个命令(如 GET/SET)执行时间极短(微秒级),看起来像 “并行”;但上述操作执行时间过长(毫秒甚至秒级),会导致后续命令排队等待,表现为 “锁表” 效果。

4. 避免阻塞的原则

  1. 禁止在生产环境使用 KEYS,改用 SCAN(渐进式遍历,分批返回,不阻塞)。
  2. 全量操作(如 FLUSHDB)优先用异步版本。
  3. 避免一次性获取大量数据(如 LRANGE 限制返回数量,用 HSCAN/SSCAN 分批获取哈希 / 集合数据)。
  4. 控制 Lua 脚本和事务的复杂度,避免长时间执行。
  5. 监控 Redis 慢查询(通过 slowlog get),及时发现并优化耗时命令。
 posted on 2025-09-05 14:19  xibuhaohao  阅读(8)  评论(0)    收藏  举报