1. keys

Redis 提供了一个简单暴力的指令 keys 用来列出所有满足特定正则字符串规则的 key

127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set wohu1104go1 1
OK
127.0.0.1:6379> set wohu1104go2 2
OK
127.0.0.1:6379> set wohu1104go3 3
OK
127.0.0.1:6379> set wohu1104go4 5
OK
127.0.0.1:6379> set wohu1104go5 5
OK
127.0.0.1:6379> set wohu1104python 1
OK
127.0.0.1:6379> set wohu1104python2 2
OK
127.0.0.1:6379> set wohu1104python3 3
OK
127.0.0.1:6379> set wohu1104python4 4
OK
127.0.0.1:6379> keys *
1) "wohu1104python4"
2) "wohu1104go3"
3) "wohu1104python3"
4) "wohu1104go1"
5) "wohu1104go4"
6) "wohu1104python2"
7) "wohu1104go2"
8) "wohu1104python"
9) "wohu1104go5"
127.0.0.1:6379> keys wohu1104go*
1) "wohu1104go3"
2) "wohu1104go1"
3) "wohu1104go4"
4) "wohu1104go2"
5) "wohu1104go5"
127.0.0.1:6379> keys wohu1104python*
1) "wohu1104python4"
2) "wohu1104python3"
3) "wohu1104python2"
4) "wohu1104python"
127.0.0.1:6379>

这个指令使用非常简单,提供一个简单的正则字符串即可,但是有很明显的两个缺点

  1. 没有 offsetlimit 参数,一次性吐出所有满足条件的 key,万一实例中有几百万个 key 满足条件,输出结果很难分析。
  2. keys 算法是遍历算法,复杂度是 O(n),如果实例中有千万级以上的 key,这个指令就会导致 Redis 服务卡顿,所有读写 Redis 的其它的指令都会被延后甚至会超时报错,因为 Redis 是单线程程序,顺序执行所有指令,其它指令必须等到当前的 keys 指令执行完了才可以继续。

2. scan

2.1 命令

scan命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 scan命令的游标参数, 以此来延续之前的迭代过程。

scan返回一个包含两个元素的数组, 第一个元素是用于进行下一次迭代的新游标, 而第二个元素则是一个数组, 这个数组中包含了所有被迭代的元素。如果新游标返回 0 表示迭代已结束。

scan 指令是一系列指令,除了可以遍历所有的 key 之外,还可以对指定的容器集合进行遍历。比如

  • zscan 遍历 zset 集合元素
  • hscan 遍历 hash 字典的元素
  • sscan 遍历 set 集合的元素。

它们的原理同 scan 都会类似的,因为 hash 底层就是字典,set 也是一个特殊的 hash(所有的 value 指向同一个元素),zset 内部也使用了字典来存储所有的元素内容。

scan 命令基本语法如下:

SCAN cursor [MATCH pattern] [COUNT count]
  • cursor - 游标。
  • pattern - 匹配的模式。
  • count - 指定从数据集里返回多少元素,默认值为 10 。

第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。

2.2 特点

Redis 为了解决这个 keys 性能问题,它在 2.8 版本中加入了指令—— scanscan 相比 keys 具备有以下特点:

  • 复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
  • 提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是一个 hint,返回的结果可多可少;
  • keys 一样,它也提供模式匹配功能;
  • 服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
  • 返回的结果可能会有重复,需要客户端去重,这点非常重要;
  • 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
  • 单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零;

2.3 使用

使用下面代码往 Redis 中添加 1000 个有相同前缀的 key

import redisclient = redis.Redis("127.0.0.1", 6379)
for i in range(1000):client.set("wohu{}".format(i), i)

假设我们要寻找 wohu520 这个 key,那么使用下面的命令,

127.0.0.1:6379> scan 0 match wohu52* count 100
1) "488"
2) 1) "wohu529"
127.0.0.1:6379> scan 488 match wohu52* count 100
1) "372"
2) 1) "wohu52"
127.0.0.1:6379> scan 372 match wohu52* count 100
1) "242"
2) 1) "wohu523"2) "wohu521"3) "wohu522"
127.0.0.1:6379> scan 242 match wohu52* count 100
1) "342"
2) (empty array)
127.0.0.1:6379> scan 342 match wohu52* count 100
1) "449"
2) (empty array)
127.0.0.1:6379> scan 449 match wohu52* count 100
1) "261"
2) 1) "wohu527"
127.0.0.1:6379> scan 261 match wohu52* count 100
1) "93"
2) 1) "wohu525"
127.0.0.1:6379> scan 93 match wohu52* count 100
1) "139"
2) 1) "wohu524"2) "wohu528"
127.0.0.1:6379> scan 139 match wohu52* count 100
1) "279"
2) (empty array)
127.0.0.1:6379> scan 279 match wohu52* count 100
1) "0"
2) 1) "wohu526"2) "wohu520"
127.0.0.1:6379>

从上面的过程可以看到虽然提供的 limit 是 100,但是返回的结果只有几个甚至没有。因为这个 limit 不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数量(约等于)。如果将 limit 设置为 10,你会发现返回结果是空的,但是游标值不为零,意味着遍历还没结束。

2.4 存储结构

Redis 中所有的 key 都存储在一个很大的字典中,是一维数组 + 二维链表结构,第一维数组的大小总是 2^n(n>=0),扩容一次数组大小空间加倍,也就是 n++

scan 指令返回的游标就是第一维数组的位置索引,我们将这个位置索引称为槽 (slot)。如果不考虑字典的扩容缩容,直接按数组下标挨个遍历就行了。limit 参数就表示需要遍历的槽位数,之所以返回的结果可能多可能少,是因为不是所有的槽位上都会挂接链表,有些槽位可能是空的,还有些槽位上挂接的链表上的元素可能会有多个。每一次遍历都会将 limit 数量的槽位上挂接的所有链表元素进行模式匹配过滤后,一次性返回给客户端。

3. 大 key 定位

有时候会因为业务人员使用不当,在 Redis 实例中会形成很大的对象,比如一个很大的 hash,一个很大的 zset 这都是经常出现的。这样的对象对 Redis 的集群数据迁移带来了很大的问题,在内存分配上,如果一个 key 太大,那么当它需要扩容时,会一次性申请更大的一块内存,这也会导致卡顿。如果这个大 key 被删除,内存会一次性回收,卡顿现象会再一次产生。

如果观察到 Redis 的内存大起大落,这极有可能是因为大 key 导致的,这时候你就需要定位出具体是那个 key,进一步定位出具体的业务来源,然后再改进相关业务代码设计。

为了避免对线上 Redis 带来卡顿,这就要用到 scan 指令,对于扫描出来的每一个 key,使用 type 指令获得 key 的类型,然后使用相应数据结构的 size 或者 len 方法来得到它的大小,对于每一种类型,保留大小的前 N 名作为扫描结果展示出来。

上面这样的过程需要编写脚本,比较繁琐,不过 Redis 官方已经在 redis-cli 指令中提供了这样的扫描功能,我们可以直接拿来即用。

redis-cli -h 127.0.0.1 -p 6379 –-bigkeys

如果你担心这个指令会大幅抬升 Redisops 导致线上报警,还可以增加一个休眠参数。

redis-cli -h 127.0.0.1 -p 6379 –-bigkeys -i 0.1

上面这个指令每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升,但是扫描的时间会变长。

参考:
https://juejin.cn/book/6844733724618129422/section/6844733724710404110​

Redis 笔记(13)— scan 和 keys 寻找特定前缀key 字段(命令格式、使用示例、定位大key)相关推荐

  1. Redis 笔记(02)— keys 键相关命令(查询数据库key数量、判断key是否存在、指定key过期时间、查看key类型、查看key剩余秒数、选择数据库、删除key、删除数据库)

    1. keys 键相关命令 命令 说明 dbsize 返回当前数据路的key数量 exists key 测试指定key是否存在 expire key seconds 为key指定过期时间 type k ...

  2. Redis 笔记(15)— 管道 pipeline(客户端将批量命令打包发送用来节省网络开销)

    Redis 是一种基于客户端-服务端模型以及请求/响应协议的 TCP 服务.这意味着通常情况下一个请求会遵循以下步骤: 客户端向服务端发送一个查询请求,并监听 Socket 返回,通常是以阻塞模式,等 ...

  3. 解决Redis大key问题,看这一篇文章就够了

    作者 | 个推数据库工程师  嘉木 个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪.滴滴在内的数十万APP.由于我们推送业务对并发量 ...

  4. redis中有key但是删不掉_Redis大 key的发现与删除方法全解析

    个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪.滴滴在内的数十万APP.由于我们推送业务对并发量.速度要求很高,为此,我们选择了高性 ...

  5. redis中有key但是删不掉_Redis大key的发现与删除方法全解析

    个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪.滴滴在内的数十万APP.由于我们推送业务对并发量.速度要求很高,为此,我们选择了高性 ...

  6. 如何提取Redis中的大KEY

    工作中,经常有些Redis实例使用不恰当,或者对业务预估不准确,或者key没有及时进行处理等等原因,导致某些KEY相当大.  那么大Key会带来哪些问题呢? 如果是集群模式下,无法做到负载均衡,导致请 ...

  7. 华为云企业级Redis评测第二期:大Key操作的影响

    点击蓝字 关注我们 在前一篇文章<华为云企业级Redis评测第一期:稳定性与扩容表现> 中,我们使用多线程压测工具memtier_benchmark对华为GaussDB(for Redis ...

  8. 深度评测丨GaussDB(for Redis)大Key操作的影响

    本文分享自华为云社区<墨天轮评测:GaussDB(for Redis)大Key操作的影响>,作者: 高斯 Redis 官方博客. 在前一篇文章<墨天轮评测:GaussDB(for R ...

  9. Redis——大Key问题

    上一篇文章讲了Redis的热点key问题,本篇介绍Redis大Key问题的概念,大Key问题产生的原因.危害以及解决大Key问题的思路: 1. 什么是大key?多大算大key? 其实关于"大 ...

最新文章

  1. PHP PDO 连接SQLSErver,PHP 使用 PDO 方式连接 sqlserver ,拼接 的 sql 命令总是报错
  2. vue 组件id重复问题
  3. 图解MySql命令行创建存储过程
  4. apt update无法连接上 127.0.0.1:10808
  5. AG9 Service order创建好之后,要自动通过middleware传其他系统去 disable
  6. Activity加载View调用顺序
  7. SQL 结合CASE WHEN 实现二维统计
  8. 对象androidandroid 开发中 如何取得ListView 的 每条Item 的对象
  9. aws linux 安装图形,linux – 用于安装AWS CLI工具的Bash脚本
  10. 嵌入式C的高级用法必须了解
  11. 【转】Maven 手动添加 JAR 包到本地仓库
  12. eNSP------三层交换机配置(拓扑图+命令)
  13. 剑指Offer_编程题(用两个栈实现队列/旋转数组的最小数字(O(n) + 二分O(lgn))/斐波那契数列/跳台阶)
  14. 如何获取音乐的地址链接
  15. 互联网发展的成功经验,以及面临的挑战
  16. 普渡大学计算机科学师生比,公立常春藤高校普渡大学,附申请要求+录取难度!...
  17. Navicat Premium远程连接ORACLE
  18. 计算机硬盘按数据传输,同时让多个硬盘轻松同步数据传输
  19. leaflet加载接入腾讯矢量、腾讯影像地图(leaflet篇.4)
  20. 美允许4G技术与WiFi共享5G频段;全球移动用户将达50亿│IoT黑板报

热门文章

  1. c语言long int表示范围_C语言编程第9讲——这些C语言整数类型的知识点你掌握了吗...
  2. linux环境下快速配置hadoop集群免密登录
  3. 107. Binary Tree Level Order Traversal II
  4. Cache 与Memory架构及数据交互
  5. LLVM数据流分析的理论
  6. Structured Streaming编程 Programming Guide
  7. 基于TensorRT的BERT实时自然语言理解(下)
  8. 三维点云去噪无监督学习:ICCV2019论文分析
  9. 2021年大数据Flink(四十):​​​​​​​Flink模拟双十一实时大屏统计
  10. 【网站汇总】论文相关