玩转Redis-如何高效访问Redis中的海量数据「建议收藏」

玩转Redis-如何高效访问Redis中的海量数据「建议收藏」1、前言   Redis以高性能著称,但性能再好,在面对海量数据时,若不正确的使用,也终将会有性能瓶颈,甚至造成服务宕机。 在实际项目中你是否会有以下疑问? 如何访问Redis中的海量数据,却不影响…

1、前言

  Redis以高性能著称,但性能再好,在面对海量数据时,若不正确的使用,也终将会有性能瓶颈,甚至造成服务宕机。

在实际项目中你是否会有以下疑问?

  • 如何访问Redis中的海量数据,却不影响其他请求访问Redis?
  • Redis中有百万/千万数据,如何高效访问?
  • Redis中数据量太大,如何既保证快速访问,又不至于使服务宕机?

以上问题亦是Redis面试的高频问题。

  《玩转Redis》系列文章主要讲述Redis的基础及中高级应用,文章基于Redis5.0.4+,欢迎前往CSDN、订阅号、开源中国、掘金等平台搜索【zxiaofan】查看系列文章。


2、思考

Q1:为什么Redis中的数据量很大时,某些数据操作会导致Redis卡顿,甚至宕机?

A1:Redis是单线程服务,所有指令都是顺序执行,当某一指令耗时很长时,就会阻塞后续的指令执行。当被积压的指令越来越多时,Redis服务占用CPU将不断升高,最终导致Redis实例崩溃甚至服务器宕机。

Q2:利用万能的keys命令查询任何想查的数据?

A2:自己电脑几万条数据玩玩就好了,线上使用keys命令,Excuse me?你想卷铺盖走人了吧。
++“某公司php工程师执行redis keys * 导致数据库宕机! 技术部发生2起本年度PO级特大事故,造成公司资金损失400万。”++ 这条新闻记忆犹新,警钟长鸣!

Q3:Redis中海量数据的正确操作方式

A3:利用SCAN系列命令(SCAN、SSCAN、HSCAN、ZSCAN)完成数据迭代。

  Redis的【SCAN系列命令】你了解多少呢?

3、SCAN系列命令详解

  SCAN系列命令,并不单纯指代SCAN命令,还包含SSCAN、HSCAN、ZSCAN,每种命令操作对象是有区别的,但用法及功能基本相同。

3.1、SCAN系列命令对比分析

  • cursor:迭代游标;
  • MATCH:数据匹配模式;
  • COUNT:迭代返回数量;
命令 功能 参数 返回值
SCAN 基于游标迭代DB cursor [MATCH pattern] [COUNT count] 返回数组,第一个值是下一次迭代的游标(无符号64bit),第2个值是元素列表(key列表)
SSCAN 基于游标迭代Sets key cursor [MATCH pattern] [COUNT count] 返回数组,第一个值是下一次迭代的游标(无符号64bit),第2个值是元素列表
HSCAN 基于游标迭代Hashes key cursor [MATCH pattern] [COUNT count] 返回数组,第2个值是field-value列表
ZSCAN 基于游标迭代ZSets key cursor [MATCH pattern] [COUNT count] 返回数组,第2个值是member-score列表

3.2、SCAN系列命令注意事项

  • SCAN的参数没有key,因为其迭代对象是DB内数据;
  • 返回值都是数组,第一个值都是下一次迭代游标;
  • 时间复杂度:每次请求都是O(1),完成所有迭代需要O(N),N是元素数量;
  • 可用版本:version >= 2.8.0;

3.3、SCAN系列命令详解

3.3.1、 增量迭代,可用于生产环境

  • 并不像KEYS、SMEMBERS一样是全量迭代,对大集合执行时可能阻塞服务很长时间;

3.3.2、不保证准确结果

  • SMEMBERS可以返回整个set的元素,而SCAN这类增量迭代命令可能出现迭代过程中元素被改变,所以并不能保证准确的返回结果;

3.3.3、基于游标迭代

  • SCAN基于游标迭代,每次请求将返回下一次需要使用的游标;
  • 游标cursor可以比DB元素总量大,可以为负数;
  • 错误游标:使用间断(不是迭代返回的)、负数、超出范围或其他非法游标,迭代不会报错,可能产生未定义行为(无法保证准确性);

3.3.4、迭代结束标记

  • SCAN返回的游标不一定递增,某次迭代返回的元素数量可能为0;
  • 返回元素列表为空,不代表迭代结束;
  • 一个完整的迭代:SCAN游标从0开始,返回游标为0结束;
  • 迭代状态由返回的游标控制。可以并发执行迭代;可随时终止迭代;

3.3.5、迭代完整性

  • 遍历开始到遍历结束一直存在的数据,一定能被迭代返回;
  • 同一个元素可能返回多次,数据去重应由应用程序完成;
  • 在迭代过程中增删的元素,可能返回,可能不返回;
  • 当数据类型是sets(由integer组成)、hashes、sorted sets且集合较小时,迭代将返回整个集合的数据,与count无关;
  • 迭代结束保证:元素添加速率小于迭代速率。

3.3.6、why有时迭代直接返回整个集合

  • 底层数据结构是hash时,如果数据量较小,Redis有内存优化策略,会使用紧凑的压缩编码。此时SCAN操作并不是返回有意义的游标,而是迭代整个集合;
  • 数据量较小?参见官方memory-optimization(内存优化)说明。

3.3.7、参数count说明

  • count默认值是10;
  • 数据集较大时,如果没有使用match,返回元素为count或比count略大;
  • 每次迭代的count参数值可以不同,只要使用上次迭代返回的游标即可;

3.3.8、参数match说明

  • 和keys的pattern类似;
  • MATCH操作是在检索出数据到返回元素前的期间执行,所以如果被匹配的元素较少,那么可能多次迭代返回的元素列表均为空;

4、SCAN系列命令示例

4.1、SCAN示例

  详见《5.2、部分问题解答》

4.2、SSCAN示例

// SSCAN示例 @zxiaofan
127.0.0.1:6378> SADD sscantest sscantest:1 1 sscantest:2 2 sscantest:3 3 sscantest:4 4 sscantest:1a 1a sscantest:2a 2a sscantest:1ab 1ab sscantest:a1 a1 sscantest:aa1 aa1 
(integer) 0
// MATCH ?:无匹配数据
127.0.0.1:6378> SSCAN sscantest 0 MATCH ? COUNT 1
1) "24"
2) (empty list or set)
127.0.0.1:6378> SSCAN sscantest 24 MATCH ? COUNT 1
1) "20"
2) (empty list or set)
127.0.0.1:6378> SSCAN sscantest 0 MATCH * COUNT 1
1) "24"
2) 1) "sscantest:3"
   2) "sscantest:2a"
127.0.0.1:6378> SSCAN sscantest 24 MATCH * COUNT 1
1) "20"
2) 1) "a1"

代码100分

4.3、HSCAN示例

代码100分// HSCAN示例 @zxiaofan
127.0.0.1:6378> HMSET hscantest hscantest:1 1 hscantest:2 2 hscantest:3 3 hscantest:4 4 hscantest:1a 1a hscantest:2a 2a hscantest:1ab 1ab hscantest:a1 a1 hscantest:aa1 aa1 
OK
127.0.0.1:6378> HSCAN hscantest 0 MATCH hscantest*a COUNT 20
1) "0"
2) 1) "hscantest:1a"
   2) "1a"
   3) "hscantest:2a"
   4) "2a"
127.0.0.1:6378> HSCAN hscantest 0 MATCH hscantest*a COUNT 2
1) "0"
2) 1) "hscantest:1a"
   2) "1a"
   3) "hscantest:2a"
   4) "2a"
127.0.0.1:6378> 

  从HSCAN示例可以看出,即使count参数为2,也返回了所有匹配的结果。这就是先前提到的,数据量较小时,直接返回所有数据。

4.4、ZSCAN示例

// ZSCAN示例 @zxiaofan
// 【移除】并弹出count个分数最大的元素,count默认为1
127.0.0.1:6378> ZPOPMAX zscantest 20
 1) "sscantest:1ab"
 2) "6"
 3) "sscantest:2a"
 4) "5"
 5) "sscantest:1a"
 6) "4"
 7) "sscantest:3"
 8) "3"
 9) "zscantest:1"
10) "2"
11) "sscantest:2"
12) "2"
13) "test1"
14) "1"
15) "sscantest:1"
16) "1"
127.0.0.1:6378> ZPOPMAX zscantest 20
(empty list or set)
127.0.0.1:6378> ZADD zscantest 1 zscantest:1 2 zscantest:2 3 zscantest:3 4 zscantest:1a 5 zscantest:2a 6 zscantest:1ab 7 zscantest:a1 8 zscantest:aa1
(integer) 8
// NX:不存在才添加;CH:返回被改变(含新增)的元素个数
127.0.0.1:6378> ZADD zscantest NX CH 1 test1 2 zscantest:1
(integer) 1
127.0.0.1:6378> ZSCAN zscantest 0 MATCH *a COUNT 5
1) "0"
2) 1) "zscantest:1a"
   2) "4"
   3) "zscantest:2a"
   4) "5"
127.0.0.1:6378> 

5、总结

5.1、看看面试时你能答上几个问题

  • SCAN迭代可以并发吗?
  • SCAN返回数据为空就是迭代结束了吗?
  • 如果首次迭代cursor参数不是0,能实现完整迭代吗?
  • 可以严格控制每次迭代返回的数据量吗?
  • 迭代返回的数据一定完整吗?
  • 为什么迭代返回的元素列表可能为空?

5.2、部分问题解答

5.2.1、SCAN返回数据为空就是迭代结束了吗

代码100分// SCAN返回数据为空就是迭代结束了吗? @zxiaofan
127.0.0.1:6378> keys k?
1) "k1"
2) "k2"
127.0.0.1:6378> SCAN 0 MATCH k?
1) "88"
2) (empty list or set)
127.0.0.1:6378> SCAN 88 MATCH k?
1) "34"
2) 1) "k1"
127.0.0.1:6378> SCAN 34 MATCH k?
1) "122"
2) (empty list or set)
127.0.0.1:6378> SCAN 122 MATCH k?
1) "14"
2) (empty list or set)
127.0.0.1:6378> SCAN 14 MATCH k?
1) "33"
2) (empty list or set)
127.0.0.1:6378> SCAN 33 MATCH k?
1) "53"
2) (empty list or set)
127.0.0.1:6378> SCAN 53 MATCH k?
1) "93"
2) (empty list or set)
127.0.0.1:6378> SCAN 93 MATCH k?
1) "107"
2) 1) "k2"
127.0.0.1:6378> SCAN 107 MATCH k?
1) "79"
2) (empty list or set)
127.0.0.1:6378> SCAN 79 MATCH k?
1) "0"
2) (empty list or set)
127.0.0.1:6378> 

  看上述示例,匹配“k?”的数据实际有2条“k1”、“k2”,在整个迭代过程中,多次返回数据为空,但是迭代未曾结束(因为“k1”、“k2”没有全部迭代返回)。
  所以,只有当游标返回为0时,才能说明迭代结束了。

5.2.2、如果首次迭代cursor参数不是0,能实现完整迭代吗?

// 如果首次迭代cursor参数不是0,能实现完整迭代吗? @zxiaofan
127.0.0.1:6378> keys k?
1) "k1"
2) "k2"
127.0.0.1:6378> SCAN 66 MATCH k?
1) "122"
2) (empty list or set)
127.0.0.1:6378> SCAN 122 MATCH k?
1) "14"
2) (empty list or set)
127.0.0.1:6378> SCAN 14 MATCH k?
1) "33"
2) (empty list or set)
127.0.0.1:6378> SCAN 33 MATCH k?
1) "53"
2) (empty list or set)
127.0.0.1:6378> SCAN 53 MATCH k?
1) "93"
2) (empty list or set)
127.0.0.1:6378> SCAN 93 MATCH k?
1) "107"
2) 1) "k2"
127.0.0.1:6378> SCAN 107 MATCH k?
1) "79"
2) (empty list or set)
127.0.0.1:6378> SCAN 79 MATCH k?
1) "0"
2) (empty list or set)
127.0.0.1:6378> 

  看上述示例,匹配“k?”的数据实际有2条“k1”、“k2”,当第一次SCAN使用cursor为66,我们可以发现经过多次迭代,游标返回为0时,“k1”一直未曾被迭代返回。
  所以,如果首次迭代cursor参数不是0,不能实现完整迭代。

  完整迭代必须是游标从0开始,游标到0结束。

6、后记

  本文针对Redis的SCAN系列命令做了详细的对比分析以及实际使用示例,并整理了面试中的高频问题。建议阅读本文的同学实际动手练习下,效果更好。欢迎关注@zxiaofan《玩转Redis》系列文章共同成长。
  第5节提到的面试问题,现在能答上几个了呢?


祝君好运!
Life is all about choices!
将来的你一定会感激现在拼命的自己!
CSDN】【GitHub】【OSCHINA】【掘金】【微信公众号


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/10933.html

(0)
上一篇 2022-12-19
下一篇 2022-12-19

相关推荐

  • SQL的约束解析

    SQL的约束解析SQL的约束解析 必须在奋斗中求生存,求发展。 约束 constraints 非空约束 not null #创建表的时候有如下字段 NAME VARCHAR(30) NOT NULL #这里添加了非空

    2023-04-20
    151
  • KINGBASE 支持全局临时表「建议收藏」

    KINGBASE 支持全局临时表「建议收藏」Postgresql 支持会话级别的临时表,表的存续期只在创建临时表的会话存活期间,会话退出后,临时表自动删除,表结构及数据也无法跨会话共享。KINGBASE除了支持PG原生的临时表机制外,还支持类似

    2023-04-17
    170
  • 使用Python列表管理和操作项目

    使用Python列表管理和操作项目在Python中,列表是一个经常被使用的数据结构,用于存储一组有序的数据。列表可以包含不同类型的元素,例如数字、字符串和其他列表。Python提供了一些内置函数和方法,可以方便地对列表进行管理和操作。下面将从多个方面对使用Python列表管理和操作项目进行详细阐述。

    2024-03-03
    81
  • Mysql数据迁移,从A表赋值B表,同一个表从C字段赋值到D字段,通过SQL实现

    Mysql数据迁移,从A表赋值B表,同一个表从C字段赋值到D字段,通过SQL实现以下都是本人亲自测试,绝对靠谱。 INSERT INTO mdm_area_copy1(id,parent_id,area_name,code,level) SELECT id,parent_id,a

    2023-03-30
    143
  • Python生成二维码

    Python生成二维码二维码在我们的生活中越来越常见,如何使用Python生成二维码呢?在这篇文章中,我们将介绍如何使用Python生成二维码。

    2024-05-28
    63
  • Python 中如何反转字符串?

    Python 字符串是 Unicode 字符的集合。Python 有很多字符串操作的函数,但是 Python 字符串库不支持内置的“reverse()”函数。但是有各种方法来反转弦。我们正在定义以下方法来反转 Python 字符串。

    2023-08-24
    130
  • 深入理解Python中Return的使用

    深入理解Python中Return的使用在Python中,return是一个非常常用的关键字。它用于函数中,用来指定函数的返回值。当函数执行到return语句时,会将返回值返回给函数的调用者。如果函数没有定义return语句,那么函数将默认返回None。

    2024-07-08
    51
  • 云时代下,医药行业管理居然这么简单[亲测有效]

    云时代下,医药行业管理居然这么简单[亲测有效]摘要:为了持续打造核心竞争力,英克康健联合华为云,基于云数据库RDS for PostgreSQL全新打造了一个高性能、大容量、高可用的SaaS医药管理系统,助力万千药企业务迈上新台阶。 本文分享自华

    2023-06-16
    146

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注