剑指 Offer:一文带你吃透麻辣鲜香的redis数据类型!

2022-09-24 21:23:19 浏览数 (1)

引言

一日三餐不可少,好友宴请炊烟袅,干煸烘烤一起炒,String、list、hash、set不难搞!

话说小面的朋友张三近日寻得一新东家,近期就要上任了,特于周末宴请自己的同事到小窝吃饭,张三为显诚意,特地在家自制晚餐招待大家,同事大大是老前辈了,率先提出让大家就今天的饭菜制作过程和菜品来谈一谈redis的value类型

大大:想必大家都用过redis,但总共有哪些存储结构的数据类型呢?

可可:我知道,redis的value类型有字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)

string

不不:张三这会正在搓面团,我就这个面来谈一谈String类型,面粉可以衍生出多种常吃食物比如包子、馒头、面条、面包、抄手等等。我们平常都会说一坨面或者一团面,那其实就像是我们大多数时候往redis里放的数据结构k,v->k是String类型,value是Object类型,管他三七二十一,将想要缓存的对象用这种方式放到redis,需要时直接取出来使用,当我们装好redis服务端并用客户端连接上后,在redis客户端对String常用的操作有

代码语言:javascript复制
set k1 aabb
get k1

还可以help set查看跟哪些参数

代码语言:javascript复制
set k1 hello
set k1 ooxx nx      #这个nx表示当k1这个key不存在时才去设置,应用场景比如分布式锁
get k1

这个nx比如一堆连接去创建某个Key,但是只有一个会返回成功,其他都会失败

代码语言:javascript复制
set k2 hello xx #xx表示只能更新,就是说这个Key是预先存在的

mset 这个命令后面可以跟多个key-valuemget 对应多个key取出


更多的可以直接 help@string来查看更多命令 比如APPEND GETRANGESTRLEN k1 取长度

就说面粉做的那个小笼包,外面基本上都是蒸笼一笼一笼的重着放,我们可以把最上面的一笼标记为1也可以把最下面的一笼标记为1,在redis对String类型处理中就类似的情况是正反向索引

我们可以通过0到-1来遍历整个字符串

type命令 可以查看key的value类型,属于哪个分组就是哪个类型OBJECT 命令可以查看key的encoding,查看更多关于object可以 输入object help查看帮助

object简单使用

在key的结构里除了存储key的名字还存储了vaule的type以及encoding,客户端如java代码对某些key操作时,比如类型不匹配可以直接报错

继续拿这个面粉说事,我每天早上去办公室楼下买早餐,里面卖的各色馅的包子,花卷,馒头都是有价格的,柜台流水关于这个包子等都是一个一个相加的,也就是说可以进行计算,redis也设计了这个数值类型的String,可以直接在redis侧进行数值计算

INCR

继续看个小实验,我们将k2用字符和数值的方式都来搞一下

可以看见k2的type会根据操作进行变换 继续跟着实验

代码语言:javascript复制
set k3 a
strlen k3 # 结果为1
APPEND k3 中
strlen k3 #长度为4,为什么?因为二进制安全,当前用的xshell连接的字符集是utf-8,一个中占3个字节,a一个字节
二进制安全

redis在存储时使用的是字节流,是二进制安全的,所以比如java代码在设置和取值时就要约定好编码解码的字符集。我们分别在utf-8和GBK的字符下set一个中字,结果在utf-8下长度为3,GBK下长度为2

我们再看一下value的存储字节

加上参数raw后它就会按照utf-8进行结果输出

bitmap

你们看张三做的那个红糖锅盔和红糖糍粑就像是二进制的0101010100011这样的,不禁就想到了redis的String当中还有一种bitmap的操作,我们先来看下有关它的操作

setbit

setbit是对位进行操作置1,取值是二进制对应的ASCII码,01000000对应@,01000001对应Abitpos,找的是二进制字符流指定的位置,结果是二进制的位的位置

命令bitcount* 统计key的字节里包含1的个数

命令bitop对两个Key进行按位与、或、非、异或

bitop应用场景比如:

  • 统计某个网站的用户一年365天某个随即时间段的登录天数
  • 比如京东是我们在开发,现在有个需求双十一要给用户送礼物,京东总共有2亿用户要准备多少礼品呢?这就涉及到到底有多少活跃用户

做个小结并把刚刚bitop应用简单说明一下

List

哔哔:刚刚不不讲解了String类型的value,我来讲讲这个list吧。话说张三刚刚做凉虾的时候我看那个米浆糊糊是挨着密漏一点点的往下挤到盆里,这个过程是连续的并且是先挨着出口的先出,后挨着出口的后出,就像是一个链先进先出就像队列一样 描述队列,反向命令lpush rpop

张三穿的烧烤肉块串在吃的时候是先吃的最后穿上去的,就像是栈一样后进先出 描述栈的命令有lpush lpop rpush rpop,后进先出,同向命令

接下来我们看看有关一些list的操作命令LRANGE遍历list list也是有正负向索引的

LINDEX 根据索引取值

LSET根据索引更新

LINSERT插入操作

LREM移除元素,中间的count有正数负数和零,正数就是从左数

LLEN统计长度,BLPOP,BRPOP阻塞的,一直等着有元素了就pop出来,模拟一下就是我开redis3个客户端,第一个和第二个使用blpop ooxx 0,这时候2个都阻塞着了,第三个客户端使用rpush ooxx hello,第一个拿到元素,第二个还是阻塞,第三个客户端重复压入数据,第二个拿到元素。更多关于list操作我们可以通过help @list查看关于list类型的命令帮助

hash

点点:你们都说的挺好的,我看张三做的糖醋排骨非常香,总共有15块,颜色是红泛黑,每块大约30g重量共计450g,形状都是长方体非常的标准,有着浓浓的番茄酱香,说着都有点流口水了,我刚刚描述的糖醋排骨可以用redis中的hash来存储,我们把key设为糖醋排骨,它有数量、重量、颜色、香味、形状这几个特征,并且可以根据特征类型来赋予不同类型的值,接下来就简单谈一谈hash的操作

help @hash命令基本上可以理解为在string命令前加了Hhset hmset hget hmget hkeys hvals

HINCRBYFLOAT hash也支持对数值的操作

set

赞赞:张三刚刚做的芋儿烧鸡中一个一个的芋儿是不重复的,每个都是独立的,一锅芋儿鸡中的芋儿是混乱的没有任何顺序可言,这个就像是redis中的set类型,接下来我来谈一谈set相关的操作

SADD 添加,SMEMBERS遍历

set也可以做多个key的交并差集,SINTER 做多个key的交集,SINTERSTORE 会将结果存在一个目标key里

SUNION并集且结果去重

SDIFF差集,是有方向性的

随机事件SRANDMEMBER,也区分正负数,0不返回,一批人抽3个人中奖,正数不重复

一批人抽3个奖,负数可重复(1个人可多次中奖)

一批人(比如7个热)来抽20个奖

还有一个场景,比如公司年会,奖品数一定小于参会人数且还分1等奖2等奖等等SPOP

set小结

sorted_set

张三:接着刚刚赞赞说的这个芋儿,其实我们也可以给它排个序,我们可以根据每个芋儿的重量或者大小来进行排序,这个应用就可以用redis的sorted_set来实现,我就来谈谈它的相关操作吧 理解sorted_set要理解元素是根据排序依据来排序的,这个排序依据就是一些维度指标就比如我刚刚说的重量和大小,再比如我要把水果中的苹果、香蕉、鸭梨来排序,可以根据名称、含糖量、大小、价格、吃货热度来排序,在redis中会给出一个权重分手动的指定你想要的的排序规则

命令开头基本上都是Z开头的ZADD添加元素,ZRANGE遍历

ZRANGEBYSCORE给权重分

比如想取价格由低到高的前2名,该怎么做呢ZRANGE k1 0 1比如想取价格由高到低的前2名呢ZrevRANGE k1 0 1 反向命令(比如取出网易云播放量前10名的歌曲,这个数据就是倒序,因为sorted-set在物理存放的时候是由小到大存放的)ZRANGE k1 -2 -1这个取法还是改变不了顺序

ZSCORE查分值,ZRANK查排名

ZINCRBY也支持数值计算

也支持交并差集,ZUNIONSTORE,不带权重、分值,默认是将分值相加

带上权重

带上分值

是怎么实现排序的呢

使用跳跃表,大致结果就是在链表的基础上增加层(基于二分),上面的每一层都像是链表,随机造层是牺牲存储空间来换取查询效率

更多操作可以help @sorted_set查看

sorted_set小结

点点:我们刚刚说了它的命令,我用java或者其他语言怎么用呢?

大大:这些redis客户端的命令都会了,还需要去学api吗?不需要的,api也不过是根据这些命令做的一个封装而已,到时候想用那种命令直接用对应的api结合命令试一下就出来了,实在不行在查资料

张三:就是就是,饭也做好了,今天的考题就到此结束,开饭!

总结

通过一场简单的对话相信大家对redis的value类型有了一定的认知和理解,后续遇到问题可以直接使用help命令查看相关说明,api只是程序员具体CRUD的路径,第三方像spring等框架封装的api底层都是调用redis自身支持的命令实现的,得其根本才能变化万通,api不会都没关系,使用的时候查一下就好了,今天的分享就到这了,我们下期再见!关注我,实时获取推送

点点关注,不迷路噢~

0 人点赞