Redis 五种数据结构以及三种高级数据结构解析
前言
在 Redis 最重要最基础就属 它丰富的数据结构了,Redis 之所以能脱颖而出很大原因是他数据结构丰富,可以支持多种场景。并且 Redis 的数据结构实现以及应用场景在面试中是相当常见的,接下来就和大家聊聊 Redis 的数据结构。
Redis 数据结构有:string、list、hash、set、sorted set 这五个是大家都知道的,但 Redis 还有更高级得数据结构,比如:HyperLogLog、Geo、BloomFilter 这几个数据结构,接下来聊聊 Redis 得这些数据结构吧。
String
<font size=3 color=green>基本概念</font>:String 是 Redis 最简单最常用的数据结构,也是 Memcached 唯一的数据结构。在平时的开发中,String 可以说是使用最频繁的了。
<font size=3 color=green>底层实现</font>:
如果一个字符串对象保存的是整数值, 并且这个整数值可以用 long 类型来表示, 那么字符串对象会将整数值保存在字符串对象结构的 ptr 属性里面(将 void* 转换成 long ), 并将字符串对象的编码设置为 int 。
如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度大于 39 字节, 那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值, 并将对象的编码设置为 raw。
如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度小于等于 39 字节, 那么字符串对象将使用 embstr 编码的方式来保存这个字符串值。
由于篇幅有限,关于 Redis 得底层实现详细介绍可以参考:Redis底层实现详细解析
<font size=3 color=green>使用</font>:
接下来会重点讲一下 set key value [EX seconds] [PX milliseconds] [NX|XX] 这个一系列命令,这块还是挺重要的,也很容易混淆。
reids 每次对 以前的值覆盖时,会 清空 TLL 值。(TTL 是过期时间)
EX second:设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
XX :只在键已经存在时,才对键进行设置操作。
在开发过程中,用 redis 来实现锁是很常用的操作。结合 NX 以及 EX 来实现。
锁可以通过设置过期时间以及手动 del 删除来释放锁。
string 的命令比较常用就多介绍了点,下面的命令我就挑重点介绍了。
<font size=3 color=green>应用场景</font>:
缓存功能:string 最常用的就是缓存功能,会将一些更新不频繁但是查询频繁的数据缓存起来,以此来减轻 DB 的压力。
计数器:可以用来计数,通过 incr 操作,如统计网站的访问量、文章访问量等。
List
<font size=3 color=green>基本概念</font>: list 是有序可重复列表,和 Java 的 List 蛮像的,查询速度快,可以通过索引查询;插入删除速度慢。
<font size=3 color=green>底层实现</font>:
列表对象的编码可以是 ziplist 或者 linkedlist 。
列表对象保存的所有字符串元素的长度都小于 64 字节并且保存的元素数量小于 512 个,使用 ziplist 编码;否则使用 linkedlist;
由于篇幅有限,关于 Redis 得底层实现详细介绍可以参考:Redis底层实现详细解析
<font size=3 color=green>使用</font>:
<font size=3 color=green>使用场景</font>:
消息队列:Redis 的 list 是有序的列表结构,可以实现阻塞队列,使用左进右出的方式。Lpush 用来生产 从左侧插入数据,Brpop 用来消费,用来从右侧 阻塞的消费数据。
数据的分页展示: lrange 命令需要两个索引来获取数据,这个就可以用来实现分页,可以在代码中计算两个索引值,然后来 redis 中取数据。
可以用来实现粉丝列表以及最新消息排行等功能。
Hash
<font size=3 color=green>简介</font>:Redis 散列可以存储多个键值对之间的映射。和字符串一样,散列存储的值既可以是字符串又可以是数值,并且用户同样可以对散列存储的数字值执行自增或自减操作。这个和 Java 的 HashMap 很像,每个 HashMap 有自己的名字,同时可以存储多个 k/v 对。
<font size=3 color=green>底层实现</font>:
哈希对象的编码可以是 ziplist 或者 hashtable 。
哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节并且保存的键值对数量小于 512 个,使用 ziplist 编码;否则使用 hashtable;
由于篇幅有限,关于 Redis 得底层实现详细介绍可以参考:Redis底层实现详细解析
<font size=3 color=green>使用</font>:
<font size=3 color=green>应用场景</font>:
Hash 更适合存储结构化的数据,比如 Java 中的对象;其实 Java 中的对象也可以用 string 进行存储,只需要将 对象 序列化成 json 串就可以,但是如果这个对象的某个属性更新比较频繁的话,那么每次就需要重新将整个对象序列化存储,这样消耗开销比较大。可如果用 hash 来存储 对象的每个属性,那么每次只需要更新要更新的属性就可以。
购物车场景:可以以用户的 id 为 key,商品的 id 为存储的 field,商品数量为键值对的 value,这样就构成了购物车的三个要素。
Set
<font size=3 color=green>基本概念</font>:Redis 的 set 和 list 都可以存储多个字符串,他们之间的不同之处在于,list 是有序可重复,而 set 是无序不可重复。
<font size=3 color=green>底层实现</font>:
集合对象的编码可以是 intset 或者 hashtable 。
集合对象保存的所有元素都是整数值并且保存的元素数量不超过 512 个,使用 intset 编码;否则使用 hashtable;
由于篇幅有限,关于 Redis 得底层实现详细介绍可以参考:Redis底层实现详细解析
<font size=3 color=green>使用</font>:
<font size=3 color=green>应用场景</font>:
标签:可以将博客网站每个人的标签用 set 集合存储,然后还按每个标签 将用户进行归并。
存储好友/粉丝:set 具有去重功能;还可以利用 set 并集功能得到共同好友之类的功能。
Sorted Set
<font size=3 color=green>基本概念</font>:有序集合和散列一样,都用于存储键值对:其中有序集合的每个键称为成员(member),都是独一无二的,而有序集合的每个值称为分值(score),都必须是浮点数。可以根据分数进行排序,有序集合是 Redis 里面唯一既可以根据成员访问元素(这一点和散列一样),又可以根据分值以及分值的排列顺序来访问元素的结构。和 Redis 的其他结构一样,用户可以对有序集合执行添加、移除和获取等操作。
<font size=3 color=green>底层实现</font>:
有序集合的编码可以是 ziplist 或者 skiplist
有序集合保存的元素数量小于 128 个并且保存的所有元素成员的长度都小于 64 字节。使用 ziplist 编码;否则使用 skiplist;
由于篇幅有限,关于 Redis 得底层实现详细介绍可以参考:Redis底层实现详细解析
<font size=3 color=green>使用</font>:
<font size=3 color=green>应用场景</font>:
排行榜:有序集合最常用的场景。如新闻网站对热点新闻排序,比如根据点击量、点赞量等。
带权重的消息队列:重要的消息 score 大一些,普通消息 score 小一些,可以实现优先级高的任务先执行。
HyperLogLog
<font size=3 color=green>基本概念</font>:
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
<font size=3 color=green>使用</font>:这里就拿一个统计网站 2021 年 5 月 23 日,有多少用户登录举例
<font size=3 color=green>应用场景</font>:
可以用来统计网站的登陆人数以及其他指标
GEO
<font size=3 color=green>基本概念</font>:
在 Redis 3.2 版本中新增了一种叫 geo 的数据结构,它主要用来存储地理位置信息,并对存储的信息进行操作。
<font size=3 color=green>使用</font>:
geoadd 用于存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
geopos 用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
geodist 用于返回两个给定位置之间的距离。单位参数:m :米,默认单位。km :千米。mi :英里。ft :英尺。
<font size=3 color=green>应用场景</font>:
用于存储地理信息以及对地理信息作操作的场景。
科普一个地理小知识:经度范围:-180 - 180。从 0°经线算起,向东、向西各分作 180°,以东的 180°属于东经,习惯上用“E”作代号,以西的 180°属于西经,习惯上用“W”作代号。0°位置是:英国格林威治(Greenwich)天文台子午仪中心的经线为本初子午线。
纬度范围:-90 - 90。位于赤道以北的点的纬度叫北纬,记为 N;位于赤道以南的点的纬度称南纬,记为 S。为了研究问题方便,人们把纬度分为低、 中、高纬度。0°~30°为低纬度, 30°~ 60°为中纬度, 60~90°为高纬度。
BloomFilter
<font size=3 color=green>基本概念</font>:
一种数据结构,是由一串很长的二进制向量组成,可以将其看成一个二进制数组。既然是二进制,那么里面存放的不是 0,就是 1,但是初始默认值都是 0。他的主要作用是:判断一个元素是否在某个集合中。比如说,我想判断 20 亿的号码中是否存在某个号码,如果直接插 DB,那么数据量太大时间会很慢;如果将 20 亿数据放到 缓存 中,缓存也装不下。这个时候用 布隆过滤器 最合适了,布隆过滤器的原理是:
添加元素
当要向布隆过滤器中添加一个元素 key 时,我们通过多个 hash 函数,算出一个值,然后将这个值所在的方格置为 1。
判断元素是否存在:判断元素是否存在,是先将元素经过多个 hash 函数计算,计算到多个下标值,然后判断这些下标对应的元素值是否都为 1,如果存在不是 1 的,那么元素肯定不在集合中;如果都是 1,那么元素大概率在集合中,并不能百分之百肯定元素存在集合中,因为多个不同的数据通过 hash 函数算出来的结果是会有重复的,所以会存在某个位置是别的数据通过 hash 函数置为的 1。
总的来说:布隆过滤器可以判断某个数据一定不存在,但是无法判断一定存在。
布隆过滤器的优缺点:
优点:优点很明显,二进制组成的数组,占用内存极少,并且插入和查询速度都足够快。
缺点:随着数据的增加,误判率会增加;还有无法判断数据一定存在;另外还有一个重要缺点,无法删除数据。
<font size=3 color=green>使用</font>:
redis 4.0 后可以使用 布隆过滤器的插件 RedisBloom,命令如下:
<br>
Redisson 使用布隆过滤器 :
<br>2. Guava 使用布隆过滤器:
Guava 是谷歌提供的 Java 工具包,功能非常强大<br>
<font size=3 color=green>应用场景</font>:
解决缓存穿透问题:一般得查询场景都是先去查询缓存,如果缓存没有,那么就去 DB 查询,如果查到了,先存在 缓存 中,然后返回给调用方。如果查不到就返回空。这种情况如果有人频繁的请求缓存中没有得数据,比如 id = -1 得数据,那么会对 DB 造成极大得压力,这种情况就可以使用 redis 得布隆过滤器了,可以先将可能得 id 都存在布隆过滤器中,当查询来的时候,先去布隆过滤器查,如果查不到直接返回,不请求缓存以及 DB,如果存在 布隆过滤器 中,那么才去缓存中取数据。
黑名单校验:可以将黑名单中得 ip 放入到布隆过滤器中,这样不用每次来都去 db 中查询了。
总结
Redis 丰富的数据结构是支撑 Redis 重要基石之一。他使 Redis 可以适应很多复杂的场景。这块的内容在面试中可以说是必考的内容,所以要在这方面要多下些功夫。
版权声明: 本文为 InfoQ 作者【善良的布隆】的原创文章。
原文链接:【http://xie.infoq.cn/article/4edaa0413c1ff7a9e37a90150】。文章转载请联系作者。
评论