栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis五种数据结构及其使用场景

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis五种数据结构及其使用场景

Redis五种数据结构及其使用场景

先有个概念,redis 数据库其实就是一个大的 map,它容纳了所有的 key, key 都是 string 类型,而 value 则有 string, list, set, hashmap, zset……等类型。

1.String 字符串类型

String是redis中最基本的数据类型,一个key对应一个value。

String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象。

这里稍微讲一下什么是二进制安全,一个二进制安全功能(函数),其本质上将操作输入作为原始的、无任何特殊格式意义的数据流。对于每个字符都公平对待,不特殊处理某一个字符。

什么意思呢,举个例子:

C语言中的字符串是根据特殊字符“”来判断该字符串是否结束,对于字符串str="0123456789123456789”来说,在C语言里面str的长度就是10(strlen(str)=10),所以strlen()函数不是二进制安全的。而在Redis中,strlen str的结果是21,是二进制安全的(Redis底层所使用的字符串表示是Sds),它只关心二进制化的字符串,不关心字符串的具体格式,里面有啥字符,只会严格的按照二进制的数据存取,不会以某种特殊格式解析字符串。

常用的命令有: **set、get、decr、incr、mget、 del ** 等。

可以参考一下,对应的命令。

实战场景:

  1. 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。

    举个我曾经做看守所项目时候使用过的例子:做很多流程操作的时候比如家属会见、财务管理等等我们往往都会先判断当前人员是否为在押人员,而判断是否为在押人员是另外一个微服务接口,如果直接实时的去调用那个接口,短时的高并发很有可能把这个服务也拖挂,最终导致整个系统不可用,并且 RPC 本身也是比较耗时的,所以就考虑在这里进行优化。

    那当时我们是怎么做呢?很简单的一个思路,提前将所有的在押人员的user_id存到redis中,这样,当请求到来的时候我们直接通过缓存可以快速判断是否为在押人员。如果不是则直接在这里返回前端。

    通过预先处理减少了实时链路上的 RPC 调用,既减少了系统的外部依赖,也极大的提高了系统的吞吐量。

  2. 计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。一般会使用decr、incr命令用于计数器的实现。

    • 当遇到需求,在规定时间,用户的访问量不能超过规定次数的时候就可以用redis中的计数器来实现了
    • 又可以使用这个技术用来做限流(使用用户的ip作为key,用户访问一次,就加1,如果超过次数就返回false)
    • 可以处理业务上面的的一些访问次数之类的,例如:文章的点赞数,阅读量,允许有一点的延迟效果,先保存到redis中,然后在同步到数据库当中
  3. session:常见方案spring session + redis实现session共享(这块了解的不多)

2.Hash (哈希)

Hash是一个Mapmap,Value值本身又是一种键值对结构,如 value={{field1,value1},…fieldN,valueN}}

所有hash的命令都是h开头的

常用的命令有: hget 、hset 、hdel、hgetall等 。

实战场景:

1.缓存: 能直观,相比string更节省空间一些,可以维护缓存信息,如用户信息,视频信息等,但用hash实现的,string也可以实现。

为什么这么说呢?

其实hash类型的(key、field、value)的结构与对象的(对象id,属性,值)的结构相似,也可以用来存储对象。

所以hash也可以用string+json存储对象的一种方式,那么存储对象时,到底用string+json还是用hash呢?

string+jsonhash
效率很高
容量
灵活性
序列化简单复杂

所以说,当对象的某个属性需要频繁修改时,不适合用string+json,因为不够灵活,每次修改都需要重新将整个对象序列化并赋值,如果使用hash类型,则可以针对某个属性单独修改,没有序列化,也不需要修改整个对象。比如,商品的价格、销量、关注数、评论数等可能经常发生变化的属性,就适合存储在hash类型里面。

综上,一般对象用string+json存储,对象中某些频繁变化的属性抽出来用hash存储。

2.购物车:以用户id为key,商品id为field,商品数量为value,恰好构成购物车的3个要素。

3.链表

List 说白了就是链表(redis 使用双端链表实现的 List),是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据。

常用的命令有:lpush、rpush、lpop、rpop、lrange等。

  • lpush+lpop=Stack(栈)
  • lpush+rpop=Queue(队列)
  • lpush+ltrim=Capped Collection(有限集合)
  • lpush+brpop=Message Queue(消息队列)

实战场景:

  1. timeline:例如微博的时间轴,有人发布微博,用lpush加入时间轴,展示新的列表信息。

  2. 排行榜

    list类型的lrange命令可以分页查看队列中的数据。可将每隔一段时间计算一次的排行榜存储在list类型中,如京东每日的手机销量排行、学校每次月考学生的成绩排名。但是,并不是所有的排行榜都能用list类型实现,只有定时计算的排行榜才适合使用list类型存储,与定时计算的排行榜相对应的是实时计算的排行榜,list类型不能支持实时计算的排行榜

    但是,对于频繁更新的列表,list类型的分页可能导致列表元素重复或漏掉。

    举个例子,当前列表里由表头到表尾依次有(E,D,C,B,A)五个元素,每页获取3个元素,用户第一次获取到(E,D,C)三个元素,然后表头新增了一个元素F,列表变成了(F,E,D,C,B,A),此时用户取第二页拿到(C,B,A),元素C重复了。只有不需要分页(比如每次都只取列表的前5个元素)或者更新频率低(比如每天凌晨更新一次)的列表才适合用list类型实现。对于需要分页并且会频繁更新的列表,需用使用有序集合sorted set类型实现。另外,需要通过时间范围查找的最新列表,list类型也实现不了,也需要通过有序集合sorted set类型实现,如以成交时间范围作为条件来查询的订单列表。

4.Set 集合

集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中 1. 不允许有重复的元素,2.集合中的元素是无序的,不能通过索引下标获取元素,3.支持集合间的操作,可以取多个集合取交集、并集、差集。跟Java的HashSet类似。

常用的命令有:sset 、srem、scard、smembers、sismember、sadd、spop、sunion等。

实战场景:

1.标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。

2.点赞,或点踩,收藏等,可以放到set中实现

因为Redis为set类型提供了求交集,并集,差集的操作,可以非常方便地实现譬如共同关注、共同爱好、共同好友等功能。

  • sinter交集命令可以获得A和B两个用户的共同好友
  • sismember命令可以判断A是否是B的好友
  • scard命令可以获取好友数量
  • 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合
  • srandmember命令可以随机展示
  • 当然黑名单白名单也一样,set类型适合存储这些黑名单数据,sismember命令可用于判断用户、ip、设备是否处于黑名单之中。
5.zset 有序集合

有序集合和集合有着必然的联系,它和set一样是不可重复的,区别在于多了score值,用来代表排序的权重。也就是当你需要一个有序的,不可重复的集合列表时,就可以考虑使用这种数据类型。

(有序集合中的元素不可以重复,但是score 分数可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同)。

常用的命令有: zadd 、 zrange、 zscore、zrem、zcard等等。

实战场景:

1.排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/856901.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号