再过60分钟你就能了解Redis常用数据结构及命令啦

228 阅读16分钟

一、概述

Redis 有 6 种基础数据结构,它们分别是:string(字符串)list(列表)hash(字典)set(集合)zset(有序集合)stream(流) 。Redis是怎么知道我们是在使用哪个数据结构?其解决方法是,每个命令都相对应于一种特定的数据结构。例如,当使用set命令就是将值存储到一个字符串数据结构里。而当你使用hset命令,你就是将值存储到hash里。考虑到Redis的关键字集很小,这样的机制具有相当的可管理性。

二、字符串 string

在Redis里,字符串是最基本最常用的数据结构。设置和获取分别用SETGET即可,值可以是任何种类的字符串(包括二进制数据),例如你可以在一个键下保存一张 .jpeg 图片。 当 key 存在时,SET 命令会覆盖掉你上一次设置的值。 另外还可以使用 EXISTSDEL 关键字来查询是否存在和删除键值对。

1. GETRANGE

获取存储在指定 key 中字符串的子字符串

在这里插入图片描述

2. GETSET

将给定 key 的值设为 value ,并返回 key 的旧值

3. GETBIT

都是位操作 GETBIT key offset value 对 key 所储存的字符串值,获取指定偏移量上的位(bit),SETBIT就是清除或者设置了 这玩意有啥用呢?

设想一个场景:

腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,你能怎么做?千万别说给每个用户建立一个key,然后挨个记,你可以算一下需要的内存会很恐怖,而且这种类似的需求很多,腾讯光这个得多花多少钱?

原理是:

redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标index用来表示我们上面例子里面的用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统,就能实现上述场景。用到的命令是:setbit、getbit、bitcount

先来说说setbit、getbit、bitcount这三个指令的用法:

在学习这几个命令之前,我们得先了解下redis中字符串的存储方式,redis中的字符串都是以二进制的方式进行存储的,比如说我执行如下命令:

image.png

我们知道 'a' 的ASCII码是 97。转换为二进制是:01100001。我们BIT相关命令都是对这个二进制数据进行操作

GETBIT GETBIT命令可以返回key对应的value在offset(偏移)处的bit值,以上文提到的kk为例,a对应的二进制数据是01100001,所以当offset为0时,对应的bit值为0;offset为1时,对应的bit值为1;offset为2时,对应的bit值为1;offset为3时,对应的bit值为0,依此类推….,如下:

image.png

通过上述结果,可以看到offset从0到7,就是01100001,也就是说offset是从左往右计数的,也就是从高位往低位。当超过位数时,结果是0

BITCOUNT BITCOUNT可以用来统计这个二进制数据中1的个数,如下:

image.png

官网上有一个非常有意思的用户上线次数统计案例:

比如说,每当用户在某一天上线的时候,我们就使用 SETBIT key offset value ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的value设置为 1 。

举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令 SETBIT peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 SETBIT peter 101 1 ,以此类推。

当要计算 peter 总共以来的上线次数时,就使用 BITCOUNT key [start] [end] 命令:执行 BITCOUNT peter ,得出的结果就是 peter 上线的总天数。

4. SETBIT

我们通过SETBIT 命令将键kk的值 'a' 变成 'b' 应该怎么变呢? 也就是将 01100001 变成 01100010 (b的ASCII码是98),这个很简单啦,也就是将'a'中的offset 6从0变成1,将offset 7 从1变成0。如下图:

image.png

大家可能也发现了,每次SETBIT完毕之后,有一个(integer) 0或者(integer)1的返回值,这个是在你进行SETBIT 之前,该offset位的比特值。

另外使用 BITPOS 指令可以用来获取二进制位串中第一个1或者0的位置,如下:

image.png

5. MGET、MSET

读取、设置一个或多个key的值

image.png

6. SETEX、PSETEX

SETEX key seconds value 为指定的 key 设置值及其过期时间。如果 key 已经存在, SETEX 命令将会替换旧的值。 PSETEX也一样,但是以毫秒为单位

7. SETNX、MSETNX

只有key不存在的时候才会设置key的值,存在的话不覆盖 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在

8. SETRANGE

SETRANGE key offset value 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始

9. STRLEN

返回 key 所储存的字符串值的长度

10. INCR、INCRBY、INCRBYFLOAT

把key中存储的数字的值加一 INCRBY key increment将 key 所储存的值加上给定的增量值(increment) INCRBYFLOAT key increment将 key 所储存的值加上给定的浮点增量值(increment)

11.DECR、DECRBY

上面是加,这里就是减了

12. APPEND

APPEND key value 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾,如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样

Redis的字符串数据结构比我当初所想的要有用许多。又能存储对象又能计数。还能用来做缓存用。

三、列表 list

对于一个给定的关键字,列表数据结构让你可以存储和处理一组值。你可以添加一个值到列表里、获取列表的第一个值或最后一个值以及用给定的索引来处理值。列表数据结构维护了值的顺序,提供了基于索引的操作。 它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n)

  • 列表常用命令

1. LPUSH、LPUSHX、RPUSH、RPUSHX

LPUSH 将一个或多个值插入到列表头部,如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。

RPUSH 将一个或多个值插入到列表尾部

LPUSHX 将一个值插入到已存在的列表头部,列表不存在时操作无效。

RPUSHX 将一个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效。

2. LPOP 、RPOP

LPOP、RPOP key移出并获取列表的第一个、最后一个元素

3. BLPOP、BRPOP

BLPOP key1 [key2 ] timeout 移出并获取列表的第一个、最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

4. RPOPLPUSH、BRPOPLPUSH

RPOPLPUSH SOURCE_KEY_NAME DESTINATION_KEY_NAME 移除列表的最后一个元素,并将该元素添加到另一个列表并返回。

BRPOPLPUSH LIST1 ANOTHER_LIST TIMEOUT 从列表中取出最后一个元素,并插入到另外一个列表的头部; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

5. LINDEX

LINDEX key index 通过索引获取列表中的元素

6. LINSERT

LINSERT key BEFORE|AFTER pivot value 在列表的pivot元素前或者后插入元素

7. LLEN

LLEN key 获取列表长度

8. LRANGE

LRANGE key start stop 获取列表指定范围内的元素

9.LREM

LREM key count value Redis Lrem 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。 COUNT 的值可以是以下几种: count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。 count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。 count = 0 : 移除表中所有与 VALUE 相等的值。

10. LSET

LSET key index value 通过索引设置列表元素的值

11. LTRIM

LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除

对于存储和索引关键字的功能,并不是只有列表数据结构这种方式。值可以是任意的东西, 我们可以使用列表数据结构去存储日志,也可以用来跟踪用户浏览网站时的路径或者使用列表数据结构去跟踪用户的排队活动。

12. 利用列表实现队列

队列是先进先出的数据结构,常用于消息排队和异步逻辑处理,它会确保元素的访问顺序:

image.png

13. 利用列表实现栈

栈是先进后出的数据结构,跟队列正好相反:

image.png

四、哈希表 hash

  • 哈希结构常用的命令有

1.HSET、HGET

HSET KEY_NAME FIELD VALUE 为哈希表中的字段赋值,如果字段有了就覆盖

HGET就是读取

image.png

2. HDEL

删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略

image.png

3. HEXISTS

查看哈希表 key 中,指定的字段是否存在

4. HGETALL

HGETALL key 获取在哈希表中指定 key 的所有字段和值

5. HINCRBY、HINCRBYFLOAT

HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment

HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment

6. HKEYS、HVALS

HKEYS key 获取所有哈希表中的字段

HVALS key 获取所有值

7. HLEN

HLEN key 获取哈希表中字段的数量

8.HMGET、HMSET

获取、设置多个字段的值

9. HSETNX

HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值

10. HSCAN

当我们需要遍历Redis所有key或者指定模式的key时,首先想到的是KEYS命令,但是如果redis数据非常大,并且key也非常多的情况下,查询的时候很可能会很慢,造成整个redis阻塞,那么有什么办法解决呢?就是scan和hscan

SCAN cursor [MATCH pattern] [COUNT count]
HSCAN key cursor [MATCH pattern] [COUNT count]

SCAN命令是一个基于游标的迭代器, 这意味着命令每次被调用都需要使用上一次这个调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程, 当SCAN命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束,HSCAN同SCAN命令相同。 在这里插入图片描述 more: www.redis.cn/commands/sc…

哈希表数据结构比普通的字符串数据结构具有更多的可操作性。我们可以使用一个哈希表数据结构去获得更精确的描述,是存储一个用户,而不是一个序列化对象。从而得到的好处是能够提取、更新和删除具体的数据片段,而不必去获取或写入整个值。

对于散列数据结构,可以从一个经过明确定义的对象的角度来考虑,例如一个用户,关键之处在于要理解他们是如何工作的。从性能上的原因来看,这是正确的,更具粒度化的控制可能会相当有用。

五、集合 set

集合数据结构常常被用来存储只能唯一存在的值,并提供了许多的基于集合的操作,例如并集。集合数据结构没有对值进行排序,但是其提供了高效的基于值的操作。使用集合数据结构的典型用例是朋友名单的实现: 在这里插入图片描述 不管一个用户有多少个朋友,我们都能高效地(O(1)时间复杂度)识别出用户X是不是用户Y的朋友:

sismember friends:leto jessica
sismember friends:leto vladimir

甚至可以在一个新的关键字里存储结果:

sinterstore friends:leto_duncan friends:leto friends:duncan
  • 有时候需要对值的属性进行标记和跟踪处理,但不能通过简单的复制操作完成,集合数据结构是解决此类问题的最好方法之一。

当然,对于那些需要运用集合操作的地方(例如交集和并集),集合数据结构就是最好的选择。

  • 集合常用命令

1. SADD

SADD key member1 [member2] 向集合添加一个或多个成员

2. SCARD

SCARD key 获取集合的成员数

image.png

3. SDIFF

SDIFF key1 [key2] 返回给定所有集合的差集(key1-key2)

image.png

4. SDIFFSTORE

SDIFFSTORE destination key1 [key2] 将给定集合之间的差集存储在指定的集合中。如果指定的集合 key 已存在,则会被覆盖。

5. SINTER

SINTER key1 [key2] 返回给定所有集合的交集

6. SINTERSTORE

SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中

7. SISMEMBER

SISMEMBER key member 判断 member 元素是否是集合 key 的成员

8. SMEMBERS

SMEMBERS key 返回集合中的所有成员

9. SMOVE

SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合

10. SPOP

移除并返回集合中的一个随机元素

11. SRANDMEMBER

SRANDMEMBER key [count] 返回集合中一个或多个随机数

12. SREM

SREM key member1 [member2] 移除集合中一个或多个,不存在的成员元素会被忽略。

13. SUNION

返回所有给定集合的并集

image.png

14. SUNIONSTORE

SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中

15. SSCAN

SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素,迭代具体作用可以参考:www.redis.cn/commands/sc… 在这里插入图片描述

六、 有序集合 zset

有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。 有序集合的成员是唯一的,但分数(score)却可以重复。

  • 最常见的应用案例是用来实现排行榜系统。

对于一些基于整数排序,且能以标记来进行有效操作的东西,使用有序集合数据结构来处理应该都是不错的选择。

  • 有序集合常用命令

1. ZADD

向有序集合添加一个或多个成员,或者更新已存在成员的分数

2. ZCARD

获取有序集合的成员数

3. ZCOUNT

ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 在这里插入图片描述

4. ZINCRBY

ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment

5. ZINTERSTORE

ZINTERSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys代表后面有几个key 在这里插入图片描述

6. ZLEXCOUNT

ZLEXCOUNT key min max在有序集合中计算指定字典区间内成员数量 在这里插入图片描述

7. ZRANGE

ZRANGE key start stop [WITHSCORES] 返回有序集中,指定区间内的成员。

其中成员的位置按分数值递增(从小到大)来排序。 具有相同分数值的成员按字典序(lexicographical order )来排列。 下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。带WITHSCORES参数就显示分数值,不带就只显示元素值 在这里插入图片描述

8. ZRANGEBYLEX

ZRANGEBYLEX key min max [LIMIT offset count] 通过字典区间返回有序集合的成员 在这里插入图片描述

9. ZRANGEBYSCORE

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员

10. ZRANK

ZRANK key member 返回有序集中指定成员的排名。其中有序集成员按分数值递增(从小到大)顺序排列。

11. ZREM

ZREM key member [member ...] 移除有序集合中的一个或多个成员

12. ZREMRANGEBYLEX

ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员

13. ZREMRANGEBYRANK

ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员

14. ZREMRANGEBYSCORE

ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员

15. ZREVRANGE

ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到底

16. ZREVRANGEBYSCORE

ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序

17. ZREVRANK

ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

18. ZSCORE

ZSCORE key member 返回有序集中,成员的分数值

19. ZUNIONSTORE

ZUNIONSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的并集,并存储在新的 key 中

20. ZSCAN

ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值) 迭代具体作用可以参考:www.redis.cn/commands/sc…

七、流 stream

Redis5.0带来了Stream类型。从字面上看是流类型,但其实从功能上看,应该是Redis对消息队列的完善实现。用过Redis做消息队列的都了解,基于Reids的消息队列实现有很多种,例如:

  • PUB/SUB,订阅/发布模式
  • 基于List的 LPUSH+BRPOP 的实现
  • 基于Sorted-Set的实现

Redis Stream的结构如上图所示,它有一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的ID和对应的内容。消息是持久化的,Redis重启后,内容还在。 具体用法就不细说了