Redis的基础数据结构总结

4 篇文章 0 订阅

前言

面试被问到Redis相关知识,总是被虐的很惨。但是人就是这样,越挫越勇,就这块儿被虐的惨那我就要啃下这个硬骨头。从这次开始就要总结Redis的相关知识。

Redis基础数据类型

string

string是一个可变的字节数组,内部结构类似于Java的ArrayList,采用预分配冗余空间的方式,来减少内存的频繁分配。当字符串小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次最多只扩容1M的空间。string最大空间为512M。

string其实是有三种编码格式的:

  • 整形(int 编码:保存long 型的64位有符号整数
  • 短字符串(embstr 编码:保存长度小于44字节的字符串
  • 长字符串(raw 编码:保存长度大于44字节的字符串

当string类型的数据为一个整数时,还可以做计数器使用。
例如:

127.0.0.1:6379> set indexNumber 10
OK
127.0.0.1:6379> get indexNumber
"10"
127.0.0.1:6379> incrby indexNumber 2
(integer) 12
127.0.0.1:6379> get indexNumber
"12"
127.0.0.1:6379> 
  1. 使用场景一string类型的可以用来当做缓存,频繁读取,但是不常修改的信息,如用户信息,视频信息等。
  2. 使用场景二可以用来记录每个用户的访问次数或对一个商品的浏览次数,用户用户id和商品页面id联合做key,然后访问次数做value。
  3. 使用场景三限定某个ip特定时间内的访问次数,key为ip地址,value为访问次数,key的过期时间可以设定一个固定值,例如60秒。如果key过期了则重新设置,否则就进行判断,当60秒内同一个ip的访问次数超过100次或者一个指定大小时就禁止访问。
  4. 使用场景四分布式session,当需要将用户登录状态共享在多个服务之间或者多个服务器之间时,本地session肯定是不能满足需求的,可以将session存放在redis中,redis可以独立于所有服务器,但是这些服务器连接的都是同一个redis。
List

Redis中的List相当于Java中的LinkedList。是双向链表而不是动态数组,插入删除快,按索引定位元素慢,需要从头一个一个去找,时间复杂度为O(n)。

当用做队列时使用:

先进先出

127.0.0.1:6379> rpush nameList lilei hanmeimei wang
(integer) 3
127.0.0.1:6379> rpop nameList
"wang"
127.0.0.1:6379> rpop nameList
"hanmeimei"
127.0.0.1:6379> rpop nameList
"lilei"
127.0.0.1:6379> llen nameList
(integer) 0
127.0.0.1:6379> 

当用做栈时使用:

先进后出

127.0.0.1:6379> rpush nameList lilei hanmeimei wang
(integer) 3
127.0.0.1:6379> llen nameList
(integer) 3
127.0.0.1:6379> lpop nameList
"lilei"
127.0.0.1:6379> lpop nameList
"hanmeimei"
127.0.0.1:6379> lpop nameList
"wang"
127.0.0.1:6379> llen nameList
(integer) 0
127.0.0.1:6379> 

List类型在,Redis底层其实并不是简单的类似LinkedList那样的一个简单的双向链表,而是被称为快速链表(quicklist)的一个数据结构。

当数据量较少时就会使用一块连续的内存存储,这个结构是压缩列表(ziplist)。所有元素紧挨着存储,这样避免了类似只存储int类型数据时,还需要保持前后两个指针的这种空间浪费。只有数据量比较大的时候才会改成quicklist,也就是将多个ziplist通过前后指针串起来。

既满足了快速插入删除,又不会出现太大的空间冗余。

使用场景
按范围输出list的长度。

127.0.0.1:6379> lpush nameList zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> lrange nameList 0 -1
1) "wangwu"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> lrange nameList 0 1
1) "wangwu"
2) "lisi"

我们可以在统计每日排行榜的时候使用,例如网易云音乐的往期的每天排行榜,这种之前的数据都是当天结束后计算好了的。
这样可以把每天计算好了的排行榜放到list里面,然后通过lrange操作将过往当天的数据取出来展示,lrange还可以进行分页展示。

Hash

Redis的Hash类型相当于Java中的HashMap,是一种hash表的结构。

单个数据存放通过hset命令,多个数据一起放置通过hmset。
删除单个元素用hdel命令,多个一起删除用hdel。

127.0.0.1:6379> hset testName hebei shijianzhuang
(integer) 1
127.0.0.1:6379> hset testName guangdong guangzhou
(integer) 1
127.0.0.1:6379> hmset testName fujian fuzhou hunan changsha hainan haikou
OK
127.0.0.1:6379> hget testName fujian
"fuzhou"
127.0.0.1:6379> hmget testName fujian guangdong hainan
1) "fuzhou"
2) "guangzhou"
3) "haikou"

值得一提的是,hash的扩容机制,虽然扩容的容量也是和HashMap类似,直接扩容到原理的2倍,并且将原先的数组中的数据,复制到新的数组当中,但是当一个hash中的数据很大的时候,rehash是一个非常耗时的过程,这对于单线程的Redis来说,可以会影响效率的。

所以Redis采用了一种渐进式的rehash策略,他会同时保留新旧两个hash集合,查询的时候两个都查,然后在后续的定时任务以及hash的读写指令中,循序渐进地将旧hash的内容一点点迁移到新的hash结构中。

当hash移除最后一个元素后,内存将被回收。

还有一点就是Redis中的hash是有缩容的,这点就比只有扩容的HashMap强。缩容的机制和扩容类似,也是缩成原先的一半大小。

redis的hash集合的使用场景
可以用做购物车,以用户id为key,那么购物车里所有的商品value,每个商品有id和和数量,那么键值对就是商品id为key,数量为value。

set

Redis中的set相当于Java中的HashSet,它内部存储的是无序且唯一的数据,value也都是同一个元素null。

127.0.0.1:6379> sadd database mysql	
(integer) 1
127.0.0.1:6379> sadd database mysql # 重复数据插入不进去
(integer) 0
127.0.0.1:6379> sadd database oracle sqlserver	# 一次插入多条记录
(integer) 2
127.0.0.1:6379> smembers database	# 查看数据,列出的数据无序
1) "sqlserver"
2) "oracle"
3) "mysql"
127.0.0.1:6379> sismember database mysql	# 查询摸个key是否存在
(integer) 1
127.0.0.1:6379> sismember database mongodb	
(integer) 0
127.0.0.1:6379> scard database	# 查询集合长度
(integer) 3
127.0.0.1:6379> spop database	# 弹出一个元素
"mysql"

使用场景
set集合可以做收藏夹,每个用户用一个set做收藏集合,每个集合中存放的是用户收藏过的商品的id。也可以用来存储中奖用户,已经中过将的用户就不会再中奖了。

sorted set

sorted set(zset) 是Redis中一个非常有特色的数据结构,它类似于Map<String,Double>和SortedSet的结合体,一方面它是一个set保证了元素的唯一性,另一方面它可以给每一个value赋予一个score,代表这个value的排序权重。

127.0.0.1:6379> zadd names 9.8 lilei
(integer) 1
127.0.0.1:6379> zadd names 8.7 zhangsan
(integer) 1
127.0.0.1:6379> zadd names 8.3 wangwu
(integer) 1
127.0.0.1:6379> zrange names 0 -1 # 按 score 排序列出,参数区间为排名范围
1) "wangwu"
2) "zhangsan"
3) "lilei"
127.0.0.1:6379> zrevrange names 0 -1 # 按 score逆序排列,参数区间为排名范围
1) "lilei"
2) "zhangsan"
3) "wangwu"
127.0.0.1:6379> zcard names # 查询数量
(integer) 3
127.0.0.1:6379> zscore names lilei # 获取指定value的score
"9.8000000000000007"	# redis的内部使用的double类型存储score,所以会有精度问题
127.0.0.1:6379> zrank names lilei # 查询指定value的排名
(integer) 2
127.0.0.1:6379> zrangebyscore names 0 9.0	# 根据score区间遍历zset
1) "wangwu"
2) "zhangsan"
127.0.0.1:6379> zrangebyscore names -inf 9.0 withscores	#根据score区间(-∞,9.0)遍历zset,同时返回分值。inf 代表 infinite,无穷大的意思。
1) "wangwu"
2) "8.3000000000000007"
3) "zhangsan"
4) "8.6999999999999993"
127.0.0.1:6379> zrem names zhangsan # 删除指定的value
(integer) 1
127.0.0.1:6379> zrange names 0 -1
1) "wangwu"
2) "lilei"

使用场景

  1. zset可以用来存储粉丝列表,每个用户一个zset集合,value是粉丝的用户id,score可以存储粉丝关注的时间,可以对粉丝列表按照关注时间排序。
  2. 也可以做实时的排行榜单,比如飙升榜、热歌榜、新歌榜等等,key可以用来存储榜单类型,value为歌曲id,score为点击量。随着点击量的增加zset会实时的将榜单进行排序。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值