Redis 实用小技巧——一文教你如何选择合适的 Key 类型

Mysql   2023-07-20 09:03   188   0  

简介

本文我们来聊一聊在 Redis 中应该如何选择合适的 Key 类型。

说到 Redis 的 Key 类型,相信很多朋友都会脱口而出:这不简单么,不就是 StringHashListSetZset 么?的确如此。但是对于一些刚刚接触 Redis 的同学来说,可能用过最多的就是 String 类型。记得我刚刚工作那会,最初接触 Redis 的时候,就是听说它是一种缓存技术,比直接操作 MySQL 更快,用的最多的操作也就是 String 类型的 SetGet 操作了,直到过了很长一段时间以后才慢慢接触到其他的数据类型(不知道有多少同学和我有过同样的经历)。

今天我们就来聊一聊 Redis 中每一种数据类型的特点,以及在实际开发中我们应该如何选择合适的数据类型。

文章会以一个「小学生入学」的场景展开,更加方便大家理解。

现在就让我们一起开始这段「小学生之旅」吧!

类型介绍

String:「小学生入学啦」

小学生入学后,学校会给每个班级的小朋友分配一个学号,这个学号必须是个唯一的,一般长这个样:2023010101(年份 + 年级 + 班级 + 唯一号)。初始化学号的命令如下:

> SET STUDENT:NUMBER:2023:01:01 2023010101OK

后续给每个学生分配学号时,只需要在原号码的基础上累加即可:

> INCR STUDENT:NUMBER:2023:01:01(integer) 2023010102

这样每位同学都可以通过「学号发号器」获得一个独一无二的学号了。

假设学校门口有一台「打卡机」,每位小朋友每天走进校门的第一件事就是进行「签到打卡」,具体的操作如下:

> SETEX STUDENT:SIGN:2023010101 86400 1OK

SETEX 相当于 SET + EXPIRE 命令的组合,即给 String 赋值,并设置过期时间。当小朋友在其他时间打卡的时候,这时候「打卡机」就会执行:

> EXISTS STUDENT:SIGN:2023010101(integer) 1

EXISTS 命令会检查 Key 是否存在,当返回整数 1 的时候,就会发出提示:「小朋友,你今天已经签到过了哦~」

这就是最简单的数据类型 String 的用法。正如开文讲到的一样,String 类型最常见且用的最广泛的命令就是 GETSET。当我们需要存储简单的数据,或者需要使用到整数自增这些操作时,再或者仅仅是为了「占位」操作时,我们就可以考虑直接使用 String 类型实现。

Hash:「来建个档案吧」

小学生入学后,第一件大事就是「建档」,即收集小学生的基本信息。小学生的基本信息包括:姓名,性别,年龄,身高,体重,父亲姓名、父亲电话、母亲姓名、母亲电话等。

如果你是刚刚接触 Redis 的话,你可能考虑直接把基本信息存储到一个 json 对象中,如下:

{"name":"皮拉夫","sex":"boy","age":6,"height":120,"weight":30,"father_name":"皮拉夫爸爸","father_phone":"13500010001","mother_name":"皮拉夫妈妈","mother_phone":"13500010002"}

然后使用 SET 命令进行存储:

> SET STUDENT:INFO:2023010101 '{"name":"皮拉夫","sex":"boy","age":6,"height":120,"weight":30,"father_name":"皮拉夫爸爸","father_phone":"13500010001","mother_name":"皮拉夫妈妈","mother_phone":"13500010002"}'OK

当我们需要读取信息时,先使用 GET 命令获取到 json 信息,然后再使用 json_decode 方法获取到对应的对象信息。乍一看没什么问题,但是当我们仅需要获取某一条信息(比如年龄)时,仍要返回整个信息,设置单独的信息也是如此。

针对这种数据结构,我们就可以考虑使用 Hash 类型进行存储了。

我们可以使用 HMSET 命令初始化所有的字段信息:

> HMSET STUDENT:INFO:2023010101 name "皮拉夫" sex "boy" age 6 height 120 weight 30 father_name "皮拉夫爸爸" father_phone "13500010001" mother_name "皮拉夫妈妈" mother_phone "13500010002"

然后可以使用 HMGET 命令获取所有的字段信息:

> HGETALL STUDENT:INFO:2023010101
 1) "name"
 2) "\xe7\x9a\xae\xe6\x8b\x89\xe5\xa4\xab"
 3) "sex"
 4) "boy"
 5) "age"
 6) "6"
 7) "height"
 8) "120"
 9) "weight"10) "30"11) "father_name"12) "\xe7\x9a\xae\xe6\x8b\x89\xe5\xa4\xab\xe7\x88\xb8\xe7\x88\xb8"13) "father_phone"14) "13500010001"15) "mother_name"16) "\xe7\x9a\xae\xe6\x8b\x89\xe5\xa4\xab\xe5\xa6\x88\xe5\xa6\x88"17) "mother_phone"18) "13500010002"

当我们需要获取某个字段(比如年龄)的时候,可以使用 HGET 获取:

> HGET STUDENT:INFO:2023010101 age"6"

同样,我们可以通过 HSET 单独设置某个字段的值:

> HSET STUDENT:INFO:2023010101 age 7(integer) 0

这样对比直接存储 json 字符串的方式,是不是方便的多呢?

List:「给家长发个短信吧」

为了表达对家长朋友们选择我们学校的感谢,学校决定以班级为单位给每位家长发一条感谢短信,内容如下:

有了短信内容,然后就是拿到家长的电话号码,依次发送就可以了。

这种情况下,就轮到 List 类型发挥作用了。

我们可以先把所有家长的电话存到一个 List 结构中:

> LPUSH STUDENT:SMS:THANKS:2023:01:01 13500010001 13500010002(integer) 2

然后我们需要在程序端通过进程任务来「消费」队列:

> RPOP STUDENT:SMS:THANKS:2023:01:01"13500010001"

取到电话以后,就是按照短信内容进行发送了。

这就是 List 一般的应用场景。

Set:「班级花名册不能少」

有了学号和档案以后,我们还需要一份「花名册」。因为作为老师需要知道班里有多少同学信息,我们这个「花名册」有两个特点:

1)内容仅维护学生的学号信息(学生详情在档案中可以查到)。
2)学号唯一,不重复。

这种情况下我们就可以使用 SET 来存储了。

SET 名叫「集合」,可以存储一组不重复的元素。正好可以满足我们的场景,具体的操作命令如下:

> SADD STUDENT:LIST:2023:01:01 2023010101 2023010102 2023010103(integer) 3

Zset:「来排个名吧」

很快,我们的小学生迎来了入学以来的第一次考试。我们决定对小朋友们的成绩做个排名(虽说现在不让排名了,我们就是喜欢特立独行)。所以我们需要做一份「成绩单」。

「成绩单」中需要存储小朋友的这些信息:

  • 学号信息

  • 学科成绩信息

考虑到一份成绩单中一个小朋友只能有一个成绩,所以对于我们的存储结构,「元素唯一性」也是必要因素之一。然后还需要记录到学生的成绩。

Hash 貌似能满足我们的诉求,我们可以设计这样的一个结构来存储学生的语文成绩信息:

> HMSET STUDENT:SCORE:CHINESE:2023:01:01 2023010101 100 2023010102 99OK

这样存储貌似也没什么问题。但是别忘了我们的诉求:除了存储,我们还需要「排名」。显然,Hash 并不能满足我们排名的诉求。

这时候就该请 Zset 登场了~

Zset 名作「有序集合」,除了保持了 Set 的「元素唯一性」之外,它还有一个强大的功能,就是可以给每一个元素附加一个「分值(score)」,有了这个「分值」,就大有文章可做了。

我们先来记录下学生们的成绩,还是采用上面的成绩信息:

> ZADD STUDENT:SCORE:CHINESE:2023:01:01 100 2023010101 99 2023010102(integer) 2

当我们需要根据成绩进行排名时,我们就可以使用以下命令操作:

> ZREVRANGEBYSCORE STUDENT:SCORE:CHINESE:2023:01:01 +inf -inf WITHSCORES limit 0 21) "2023010101"2) "100"3) "2023010102"4) "99"

这样我们就可以通过 ZREVRANGEBYSCORE 实现成绩的排名了,是不是很酷。当然我们还可以限制分值的范围和返回数据集的范围,也是非常灵活。

总结

这里我们就通过几个有趣的小场景介绍完了 Redis 不同的 Key 的使用场景。可以总结成以下几条结论:

  • 简单的字符串存储,优先考虑使用 String

  • 如果是普通的对象属性存储,优先考虑使用 Hash

  • 需要「先进先出」的任务场景,优先考虑使用 List

  • 需要存储一组元素,且元素具有唯一性,优先考虑使用 Set

  • 需要存储一组元素,且元素具有唯一性和「分值」,而且需要对元素进行排序比较,优先考虑使用 Zset

希望此文可以帮到那些刚刚接触到 Redis 的同学,可以在以后的工作中灵活运用,使程序更加高效。

你应该了解真相,真相会让你自由。

博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。
闲言碎语
偷一个人的主意是剽窃,偷很多人的主意是研究。
赞赏支持

如果觉得博客文章对您有帮助,异或土豪有钱任性,可以通过以下扫码向我捐助。也可以动动手指,帮我分享和传播。您的肯定,是我不懈努力的动力!感谢各位亲~