Redis Scan 原理解析与注意点

一、 概述
由于 Redis 是单线程在处理用户的命令,而 Keys 命令会一次性遍历所有 Key,于是在 命令执行过程中,无法执行其他命令。这就导致如果 Redis 中的 key 比较多,那么 Keys 命令执行时间就会比较长,从而阻塞 Redis。
所以很多教程都推荐使用 Scan 命令来代替 Keys,因为 Scan 可以限制每次遍历的 key 数量。

Keys 的缺点:
1)没有limit,我们只能一次性获取所有符合条件的key,如果结果有上百万条,那么等待你的就是“无穷无尽”的字符串输出。
2)keys命令是遍历算法,时间复杂度是O(N)。如我们刚才所说,这个命令非常容易导致Redis服务卡顿。因此,我们要尽量避免在生产环境使用该命令。
相比于keys命令,Scan命令有两个比较明显的优势:
1)Scan命令的时间复杂度虽然也是O(N),但它是分次进行的,不会阻塞线程。
2)Scan命令提供了 count 参数,可以控制每次遍历的集合数。

可以理解为 Scan 是渐进式的 Keys。

Scan 命令语法如下:

SCAN cursor [MATCH pattern] [COUNT count]
  • cursor - 游标。
  • pattern - 匹配的模式。
  • count - 指定每次遍历多少个集合。
  1. 可以简单理解为每次遍历多少个元素
  2. 根据测试,推荐 Count大小为 1W。

Scan 返回值为数组,会返回一个游标+一系列的 Key
大致用法如下:
SCAN命令是基于游标的,每次调用后,都会返回一个游标,用于下一次迭代。当游标返回0时,表示迭代结束。

第一次 Scan 时指定游标为 0,表示开启新的一轮迭代,然后 Scan 命令返回一个新的游标,作为第二次 Scan 时的游标值继续迭代,一直到 Scan 返回游标为0,表示本轮迭代结束。

通过这个就可以看出,Scan 完成一次迭代,需要和 Redis 进行多次交互。

Scan原理
Redis使用了Hash表作为底层实现,原因不外乎高效且实现简单。类似于HashMap那样数组+链表的结构。其中第一维的数组大小为2n(n>=0)。每次扩容数组长度扩大一倍。
Scan命令就是对这个一维数组进行遍历。每次返回的游标值也都是这个数组的索引。Count 参数表示遍历多少个数组的元素,将这些元素下挂接的符合条件的结果都返回。因为每个元素下挂接的链表大小不同,所以每次返回的结果数量也就不同。


Scan 命令注意事项

  • 返回的结果可能会有重复,需要客户端去重复,这点非常重要;
  • 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
  • 单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零;

二、 Scan 踩坑
使用时遇到一个 特殊场景,跨区域远程连接 Redis 并进行模糊查询,扫描所有指定前缀的 Key。
最开始也没多想,直接就是开始 Scan,然后 Count 参数指定的是 1000。

Redis 中大概几百万 Key,最后发现这个接口需要几十上百秒才返回。
什么原因呢?
Scan 命令中的 Count 指定一次扫描多少 Key,这里指定为 1000,几百万Key就需要几千次迭代,即和 Redis 交互几千次,然后因为是远程连接,网络延迟比较大,所以耗时特别长。
最后将 Count 参数调大后,减少了交互次数,就好多了。Count 参数越大,Redis 阻塞时间也会越长,需要取舍。极限一点,Count 参数和总 Key 数一致时,Scan 命令就和 Keys 效果一样了。
Count 大小和 Scan 总耗时的关系如下图:

可以发现 Count 越大,总耗时就越短,不过越后面提升就越不明显了。所以推荐的 Count 大小为 1W 左右。
如果不考虑 Redis 的阻塞,其实 Keys 比 Scan 会快很多,毕竟一次性处理,省去了多余的交互。

 

<think>好的,我现在需要回答用户关于在Redis中如何将key展示成树形结构的问题。用户之前已经询问过为什么不能创建多级目录的key,现在他想知道如何展示成树形结构。首先,我需要回忆之前给出的回答,确保一致性,同时补充新的方法。 首先,用户可能希望像文件系统那样有层次结构,但Redis本身不支持。因此,我需要解释Redis的扁平结构,并介绍如何模拟树形结构。接下来,我需要考虑不同的方法,比如使用分隔符、Hash类型、RedisJSON模块,或者外部工具。 我需要确保每个方法的步骤清晰,并且给出具体的命令示例,比如使用`:`作为分隔符,然后通过脚本或工具解析这些key,生成树形结构。此外,提到第三方工具如Redis Tree命令或RedisInsight的可视化功能可能会对用户有帮助。 还要注意潜在的问题,比如使用KEYS命令的风险,推荐使用SCAN代替,以及处理大量数据时的性能问题。可能需要提到Lua脚本的例子,或者使用编程语言(如Python)来遍历和构建树形结构。 最后,总结各个方法的优缺,帮助用户根据需求选择合适的方式。确保语言简洁,结构清晰,避免技术术语过于复杂,让用户容易理解。</think>在 Redis 中实现键(key)的树形结构展示,需要结合**命名约定**和**工具解析**。由于 Redis 原生不支持层级目录,但可通过以下方法模拟树形结构: --- ### 一、核心原理:通过分隔符模拟层级 常用 `:`、`/` 或 `.` 作为分隔符定义键名,例如: ```bash SET user:1000:name "Alice" SET user:1000:orders:2023 "Order123" SET group:admin:permissions "write" ``` 此时键名隐含层级关系,但 Redis 仍视其为普通字符串键。 --- ### 二、树形结构展示方法 #### 方法 1:使用 `KEYS` 或 `SCAN` 命令 通过模式匹配获取层级关系(生产环境建议用 `SCAN` 代替 `KEYS`): ```bash SCAN 0 MATCH user:1000:* # 获取 user:1000 下的所有子键 ``` 输出结果: ``` 1) "user:1000:name" 2) "user:1000:orders:2023" ``` 需自行解析分隔符并构建树形结构。 --- #### 方法 2:编写脚本解析键名 以 Python 为例,将键解析为嵌套字典: ```python import redis r = redis.Redis() keys = r.keys("user:*") tree = {} for key in keys: parts = key.decode().split(':') node = tree for part in parts: if part not in node: node[part] = {} node = node[part] print(tree) # 输出树形结构字典 ``` 输出示例: ```json { "user": { "1000": { "name": {}, "orders": { "2023": {} } } } } ``` --- #### 方法 3:使用第三方工具 - **RedisInsight**(官方可视化工具) 自动将 `:` 分隔的键解析为树形结构: ![RedisInsight树形展示](https://redis.com/wp-content/uploads/2021/11/RedisInsight-Key-View.png) - **redis-tree-cli**(第三方工具) 安装后直接展示树形: ```bash npm install -g redis-tree-cli redis-tree user:* # 展示以 user 开头的树形结构 ``` --- #### 方法 4:利用 Hash 类型嵌套 通过 Hash 结构手动维护层级: ```bash # 第一层 HSET user:1000 name "Alice" # 第二层 HSET user:1000:orders 2023 "Order123" ``` 查询时逐层获取: ```bash HGETALL user:1000 # 获取用户信息 HGETALL user:1000:orders # 获取订单信息 ``` --- #### 方法 5:使用 RedisJSON 模块 若需要原生树形支持,可安装 RedisJSON(需 Redis 6+): ```bash JSON.SET user:1000 $ '{"name":"Alice","orders":{"2023":"Order123"}}' JSON.GET user:1000 $.orders # 直接获取子树 ``` 输出: ```json {"orders":{"2023":"Order123"}} ``` --- ### 三、注意事项 1. **性能问题** - 避免频繁使用 `KEYS *`(阻塞其他操作),优先用 `SCAN` - 超大数据量时,树形解析可能消耗内存 2. **命名规范** - 统一分隔符(如 `:` 或 `/`) - 避免特殊字符(如 `空格`、`\n`) 3. **适用场景** - 简单层级:用分隔符 + 脚本解析 - 复杂嵌套:优先选择 RedisJSON --- ### 四、最终效果示例 假设有以下键: ``` user:1000:profile:name user:1000:orders:2023:item1 group:admin:permissions ``` 树形展示为: ``` ├── user │ └── 1000 │ ├── profile │ │ └── name │ └── orders │ └── 2023 │ └── item1 └── group └── admin └── permissions ``` --- 通过以上方法,可在 Redis 中实现键的树形结构展示,但需注意这本质上仍是客户端逻辑,Redis 本身并不维护层级关系。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值