redis的五种数据类型原始命令详解


前言

redis是key value格式的数据库,redis的value提供了五种数据类型,string list hash set sorted_set,这里要特别注意,redis所谓的数据类型针对的是value,而key只有String这种类型。我们很有必要学习通过redis客户端操作这些数据类型,因为后续我们使用高级的API方式调用,也可以从redis基本的五种数据类型操作命令中找到影子。其实不管redis的数据类型是哪种,在redis中**,只存储成字节**,redis服务把字节数组返回给客户端,客户端根据自己的编码转成对应的编码格式。这里就要求redis的客户端需要提前商量好编码格式,不然会出现乱码的现象。而出现乱码的原因就是存储在redis的客户端和读取redis的客户端编码格式不一致。
我们先说下怎么学习这些操作命令,不管学什么技术,不懂就输入help
在这里插入图片描述

一、String类型

要学习string 类型的命令,我们可以输入
help @string
在这里插入图片描述
这个时候回出现所有string数据类型的相关操作命令。好下面就让我们来学习下string相关的命令。string数据类型的数据类型又可以细分为字符串,数值型,bitmap型。
在进行验证前先把之前redis库中的数据情空,有两种方式清库
FLUSHALL 这个命令是把redis中16库都清空
FLUSHDB 这个命令是把redis客户端登录当前的这个数据库清空
这两个命令在生产环境中慎用,不然数据就丢失了

数据类型推断方法

# 通过help @generic可以找到type命令含义,判断对应key的value的数据类型
# 举例,如果k0 是通过set k0 xx ,那么通过help @string可以推断出string类型
127.0.0.1:6379> type k0
string
127.0.0.1:6379> OBJECT help
1) OBJECT <subcommand> arg arg ... arg. Subcommands are:
2) ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.
3) FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.
4) IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.
5) REFCOUNT <key> -- Return the number of references of the value associated with the specified key.
我们可以通过 OBJECT ENCODING  key知道对应key的数据类型字符串还是数值型

1.1 字符串类型

增加

# 增加单个key
127.0.0.1:6379> set k0 a
OK

# 往k0的value后面追加  此时k0 为aaa
127.0.0.1:6379> APPEND k0 aa
(integer) 3

# 批量新增多个key
127.0.0.1:6379> mset k1 b k2 c k3 d
OK

# 如果该key不存在,则新增一个key
127.0.0.1:6379> SETNX k4 e
(integer) 1

#批量创建key,如果有一个存在,则新增不成功
127.0.0.1:6379> MSETNX k5 f k6 g
(integer) 1


# 设置key 超时时间 时间单位为妙
127.0.0.1:6379> SETEX k5  100 f
OK

#设置key 超时时间,时间单位毫秒
127.0.0.1:6379> PSETEX k5 100000 ff
OK

查询

# 查询单个key
127.0.0.1:6379> get k0
"a"

# 批量查询key
127.0.0.1:6379> MGET k0 k1 k2
1) "a"
2) "b"
3) "c"

# 推断数据类型
127.0.0.1:6379> type k0
string

# 字符串类型
127.0.0.1:6379> OBJECT ENCODING k1
"embstr"

# 返回key 对应value字节的长度
127.0.0.1:6379> STRLEN k0
(integer) 3
# 因为我的客户端是utf8编码所以在redis中就存储3个字节本来表示
127.0.0.1:6379> set k0 中
OK

2.2 数值类型

和string类型差不多一样的命令,只是数值类型是存储进redis的数据为 整数字时,redis会默认设置他的value类型为string 中数值类型

127.0.0.1:6379> set k0 111
OK

127.0.0.1:6379> TYPE k0
string

127.0.0.1:6379> OBJECT encoding k0
"int"

数值类型的数据可以进行递增或者递减操作

# 增加1
127.0.0.1:6379> INCR k0
(integer) 112

#指定增长幅度
127.0.0.1:6379> INCRBY k0 3
(integer) 115

# 增长浮点数据,此时的数据就变成字符串了,就不能进行数值运算了
127.0.0.1:6379> INCRBYFLOAT k0 0.5
"115.5"

#将数据还原
127.0.0.1:6379> set k0 111
OK
# 减少1
127.0.0.1:6379> DECR k0
(integer) 110

# 指定减少幅度减少
127.0.0.1:6379> DECRBY k0 5
(integer) 105

像incr decr 这些原子性的操作可以应用在秒杀,点赞数量等这种需要事务控制数据增加或者减少的地方。

2.3 bitmap

位图,这是一个在redis中非常有用的好东西。在讲他之前先来聊下这两个场景,大家会怎么去设计。
第一,统计每个用户一年当中登录访问了我们的系统多少次
第二,一段时间内,统计有多少用户登录
首先什么是位图?
举个例子,一个字节有8个比特位
0000 0000 我们通过redis的命令可以改变比特位从0变1或者1变0.这就是位图

新增

# 0100 0000 对应ASCII中的@符号
127.0.0.1:6379> SETBIT k0 1 1
(integer) 0

127.0.0.1:6379> get k0
"@"

# 0100 0001 对应ASCII中的A符号
127.0.0.1:6379> SETBIT k0 7 1
(integer) 0

127.0.0.1:6379> get k0
"A"

统计有少比特位是1

127.0.0.1:6379> BITCOUNT k0
(integer) 2

查看比特位在区间内,哪个位置先出现

# 0100 0001
#这里有个正反向索引的概念,从左向右去算位置是0,1,2...递增
#从右向左算位置则为-1,-2,-3....
# 所以下面命令的意思是在【0,-1】从头到尾统计比特位第一次出现1的位置
127.0.0.1:6379> BITPOS k0 1 0 -1
(integer) 1
127.0.0.1:6379> get k0
"A"
# 下面命令的意思是在【2,-1】从第二个索引到尾统计比特位第一次出现1的位置
127.0.0.1:6379> BITPOS k0 1 2 -1
(integer) -1
127.0.0.1:6379>

比特位与或运算

127.0.0.1:6379> SETBIT k0 1 1
(integer) 0

127.0.0.1:6379> SETBIT k1 7 1
(integer) 0

# or或运算
127.0.0.1:6379> BITOP or dk k0 k1
(integer) 1

127.0.0.1:6379> get dk
"A"

#与运算
127.0.0.1:6379> BITOP and dk2 k0 k1
(integer) 1

127.0.0.1:6379> get dk2
"\x00"

基础知识说完,现在回到最上面说的两个问题。如果用redis的bitmap将可以结局
首选第一个问题,统计每个用户一年当中哪几天登录访问了我们的系统
我们可以这样设计,redis中的key是userId至于value我们可以给365个bit。我们只需要45个字节就可以处理。比如用户10.1号登陆,我们就可以在redis中对应10.1号的bit位上设置为1。后面只需要bitcount就可以得出这个用户一年登陆我们系统多少次。

第二个问题,一段时间内,统计有多少用户登录,这里我们需要使用redis给我们提供bitmap的比特位或运算。
我们可以这样设计,redis的key为日期如20200920这种,然后根据给我们系统每个用户一个编号从0开始计数,这样每天,当有用户登录的时候,我们在redis对应日期key的value中找到对应用户所表示的位,给他设置为1,这样就表示了这个用今天登录了。
当我们要统计一段时间内总共有多少用户登录访问量的时候,只需要把这几天的key拿出来进行BITOP or或运算,然后将得到的值再进行bitcount就可以得出一段时间内登录的总用户数。

二、list

redis的list数据类型,可以模拟出很多种数据结构如:栈,队列,数组,单播(FIFO)。将在下面学习list操作命令的时候结合起来

首先list是有顺序的,这个顺序是指存入list先后的顺序,我们可以脑补下list在内存中相当于是一个双向链表结构。redis的key中有两个指针,一个head指向前一个节点,一个tail指向后一个节点。

通过help @list可以看出list的命令大体可以分为两大类,L开头表示left 左边的意思,R开头表示right右边的意思。
在这里插入图片描述

#从左边一次一个压进去 比如下面的在内存中的表示如下   
#    g f e d c b a
127.0.0.1:6379> LPUSH k0 a b c d e f g
(integer) 7

# 根据下面取出的结果,我们得到一个结论
# 同向命令  相当于  栈
127.0.0.1:6379> LRANGE k0 0 -1
1) "g"
2) "f"
3) "e"
4) "d"
5) "c"
6) "b"
7) "a"


#从右边一次一个压进去 比如下面的在内存中的表示如下   
#    a b c d e f
127.0.0.1:6379> RPUSH k1 a b c d e f g
(integer) 7
# 根据下面取出的结果,我们得到一个结论
# 反向命令  相当于  队列(FIFO)
127.0.0.1:6379> LRANGE k1 0 -1
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"

# 根据下标获取数据,这里相当于 “数组“  数据结构
127.0.0.1:6379> LINDEX k0 0
"g"

# 只有当key存在 下面命令才执行成功  值才会添加进对应key的list中
127.0.0.1:6379> LPUSHX k0 aaaa
(integer) 8

# 删除指定key中数据,从左边开始删除指定个数。
127.0.0.1:6379> LREM k0 1 aaaa
(integer) 1

# lpop移除并拿到第一个值
127.0.0.1:6379> LPUSH k0 a b c d e f g
(integer) 7
127.0.0.1:6379> LPOP k0
"g"

# 查看list长度
127.0.0.1:6379> LLEN k0
(integer) 6

list中的命令还包括阻塞式的命令

# 当我们k0中还有数据的时候,就能得道k0弹出的值是f
127.0.0.1:6379> BLPOP k0 0
1) "k0"
2) "f"

# 当我们要获取一个不存在的key中值的时候,如果不存在并且我们设置超时时间是0(表示永远不超时),redis中超时单位是秒
127.0.0.1:6379> BLPOP k1 0

# 我们另外开个redis客户端给k1设置值后,他才能弹出值
# 为了展示单播(FIFO)的数据,我们多开几个客户端阻塞等待k1,当我们设置k1的值后
#第一个阻塞等待k1的客户端将获取到值打印在屏幕(其实我一直觉得单播和队列挺像的)

[root@localhost ~]# redis-cli
127.0.0.1:6379> LPUSH k1 aaa
(integer) 1

27.0.0.1:6379>  BLPOP k1 0
1) "k1"
2) "aaa"
(190.59s)

三、hash

hash这种数据结构,相当于java中的hashmap,就是一个kv键值对的集合。主要用途是保存一些对象属性的值。
比如我们设计需要存储用户信息则可惜这么设计redis key为userId value为hash,里面存储name=zs ,age=18等。
在这里插入图片描述

其实hash的使用方式和string类型的使用方式基本一样,只是hash类型的命令前面多了h。

# 新增
127.0.0.1:6379> HSET k0 name zs
(integer) 1

# 获取
127.0.0.1:6379> HGET k0 name
"zs"

#批量设置key值
127.0.0.1:6379> HMSET k0 name zs age 18
OK

# 批量获取值
127.0.0.1:6379> HMGET k0 name age
1) "zs"
2) "18"

# 获取key所有的kv
127.0.0.1:6379> HGETALL k0
1) "name"
2) "zs"
3) "age"
4) "18"

# 获取对应key 的所有的key(有点绕,就是获取redis key对应的hash中所有的key值)
127.0.0.1:6379> HKEYS k0
1) "name"
2) "age"

# 获取key 所有values值(有点绕,就是获取redis key对应的hash中所有的value值)
127.0.0.1:6379> HVALS k0
1) "zs"
2) "18"

四、set

set类型和list类型很像,但是set与list相比,set是无序且唯一,要求去重的话只能用set 类型
在这里插入图片描述

# 增加 
127.0.0.1:6379> sadd k0 a b c d a
(integer) 4

# 查看到a去重,只留下一个,无序
127.0.0.1:6379> SMEMBERS k0
1) "c"
2) "b"
3) "a"
4) "d"

# 统计set集合数量
127.0.0.1:6379> SCARD k0
(integer) 4

# 判断给定的值是否在set 集合中已经出现过  返回1 表示存在  0 表示不存在
127.0.0.1:6379> SISMEMBER k0 a
(integer) 1

# 删除集合某个成员
127.0.0.1:6379> SREM k0 d
(integer) 1


127.0.0.1:6379> SADD k0 d  e f g
(integer) 4
127.0.0.1:6379> SMEMBERS k0
1) "f"
2) "e"
3) "a"
4) "b"
5) "c"
6) "d"
7) "g"
# 随机删除集合中三个成员,并返回删除的三个成员。
127.0.0.1:6379> SPOP k0 3
1) "b"
2) "f"
3) "d"
127.0.0.1:6379> SMEMBERS k0
1) "e"
2) "a"
3) "c"
4) "g"

set 类型还可以进行集合的运算,交集并集,差集
先FLUSHALL清除数据下

127.0.0.1:6379> SADD k0 1 2 3 4 5
(integer) 5
127.0.0.1:6379> SADD k1 4 5 6 7 8
(integer) 5

#交集,并把结果打印出来
127.0.0.1:6379> SINTER k0 k1
1) "4"
2) "5"

# 交集,并把数据存储在另一个key中,下面的差集和并集我就不演示对应带又store的命令,
# 因为带又store的命,是把数据存储成另一个key
127.0.0.1:6379> SINTERSTORE ik k0 k1
(integer) 2
127.0.0.1:6379> SMEMBERS ik
1) "4"
2) "5"

# 差集 = k0的数据减去k0和k1的交集
127.0.0.1:6379> SDIFF k0 k1
1) "1"
2) "2"
3) "3"
# 差集=k1 的数据减去k0和k1的交集
127.0.0.1:6379> SDIFF k1 k0
1) "6"
2) "7"
3) "8"

# 并集
127.0.0.1:6379> SUNION k0 k1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"

set 类型的常见用途
主要利用他的随机事件。可以用来做抽奖,下面我们的例子都是建立在抽奖人数远远大于奖品数量的前提下。(因为如果奖品远大于抽奖人,我们要实现单次抽奖不重复将实现不了,将会有奖品剩余)
抽奖分成两种,第一种是当你抽中后,就再也不能参加下面的抽奖。第二种是可以重复抽奖
第一种,我们可以使用set 中命令

127.0.0.1:6379> SADD k0 a b c d e f g
(integer) 7
127.0.0.1:6379> SPOP k0
"c"
127.0.0.1:6379> SMEMBERS k0
1) "f"
2) "a"
3) "b"
4) "e"
5) "d"
6) "g"

第二种,可重复抽奖。这里又分为两种,因为我们单次的抽奖可以分为可重复和不可重复
我们需要了解set当中一个命令
其中count 有三种情况 正数 0 负数
正数表示数据不可重复
0 返回数据为空
负数表示数据可以重复
在这里插入图片描述
所以对应我们的单次抽奖重复的情况

# 随机抽出3名,那么有可能重复,也有可能不重复
SRANDMEMBER k0 -3

在这里插入图片描述

单次抽奖不重复

127.0.0.1:6379> SRANDMEMBER k0 3

在这里插入图片描述

五、sorted_set

理论上sorted_set也是set的一种,但是 sorted_set实现了排序的功能,这里的排序是集合内的数据进行排序,和list是存入的顺序排序不一样。这里很多命令,其实和上面的很像。
因为sorted_set要进行排序,那就引申出一个问题,根据什么维度进行排序呢?所以redis出现score分值这个概念,redis根据用户给各个成员的分值情况进行排序,如果分值一样,则按照字典序排序。

#新增
127.0.0.1:6379> ZADD k0 1 a 2 b 3 c 4 d
(integer) 4

# 根据分值,查询出所有数据,从低到高排序
127.0.0.1:6379> ZRANGE k0 0 -1 withscores
1) "a"
2) "1"
3) "b"
4) "2"
5) "c"
6) "3"
7) "d"
8) "4"

# 根据分值,查询出所有数据,从高到低排序
127.0.0.1:6379> ZREVRANGE k0 0 -1 withscores
1) "d"
2) "4"
3) "c"
4) "3"
5) "b"
6) "2"
7) "a"
8) "1"

# 删除
127.0.0.1:6379> ZREM k0 a
(integer) 1

sorted_set也支持集合操作。并集和交集

交集

# 可以看出 主要麻烦的是sort_set有分值的概念,这里需要如何处理
# 从图片中灰色的描述看出,要么求和,最小,最大。默认是求和的
127.0.0.1:6379> help ZINTERSTORE

在这里插入图片描述


127.0.0.1:6379> ZADD k0 80 jack 90 tom 100 kang
(integer) 3
127.0.0.1:6379> ZADD k1 70 hong 80 jack 90 kang
(integer) 3

# 求交集,最大和最小我就不演示,把sum 换成max 或者min就行了
127.0.0.1:6379> ZINTERSTORE ik 2 k0 k1 aggregate sum
(integer) 2
127.0.0.1:6379> ZRANGE ik 0 -1 withscores
1) "jack"
2) "160"
3) "kang"
4) "190"

并集

127.0.0.1:6379> help ZUNIONSTORE

在这里插入图片描述

127.0.0.1:6379> ZUNIONSTORE uk 2 k0 k1 aggregate max
(integer) 4
127.0.0.1:6379> ZRANGE uk 0 -1 withscores
1) "hong"
2) "70"
3) "jack"
4) "80"
5) "tom"
6) "90"
7) "kang"
8) "100"

这里在说下,redis的sorted_set为什么排序这么快? 在面试中经常会被问到。
理由是redis使用的是skip list跳跃表的数据结构,所以速度就快。如果不用跳跃表,那么只能存一个数据就挨个和redis中已经存在的数据进行比较,这个复杂度就是O(n)了
好,那什么是跳跃表呢?
一图胜前言
在这里插入图片描述
这个过程不好用语言描述,有点类似平衡二叉树。当我们需要插入数据18,那么我们从第一层开始比较,发现18比11 大,所以我们跳到第二层和22 比较,发现比22小,然后我们调到第三层和11比较,发现比11大,所以18的位置就再11和22之间。
至于上面例子中18会不会造层,说不一定,是随机的。

总结

好,至此redis常见的五种数据类型及常用的命令介绍完毕,今后看redis相关的高级API,也应该能想到对应原生redis哪个命令。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值