Redis系列:Redis的数据结构

2022-12-01 21:53:44 浏览数 (2)

Redis 的基本数据类型包括:二进制安全字符串 String、Hashes(哈希)、Lists 列表、Sets 集合 和 Sorted sets 有序集合; Redis 的特殊数据类型还包括:geospatial 地理位置、Bitmap 位存储、HyperLogLogs 基数统计;此外,Redis 在 5.0 版本中还引入了 stream 这个全新的数据类型。 本篇内容包括:Redis 基本数据类型(二进制安全字符串 String、Hashes(哈希)、Lists 列表、Sets 集合 和 Sorted sets 有序集合) 以及 Redis 特殊数据类型(geospatial 地理位置、Bitmap 位存储、HyperLogLogs 基数统计)等内容!


文章目录
  • 一、Redis 基本数据类型
    • 1、二进制安全字符串 String
    • 2、哈希 Hashes
    • 3、列表 Lists
    • 4、集合 Sets
    • 5、有序集合 Zset(Sorted sets)

  • 二、Redis 特殊数据类型
    • 1、位存储 Bitmap
    • 2、基数统计 HyperLogLogs
    • 3、地理位置 geospatial
    • 4、Stream

一、Redis 基本数据类型

Redis 的基本数据类型包括:二进制安全字符串 String、Hashes(哈希)、Lists 列表、Sets 集合 和 Sorted sets 有序集合;

1、二进制安全字符串 String

二进制安全字符串 String:String 是 Redis 的最基本的数据类型,一个 key 对应一个 value,String 类型是二进制安全的,意思是 Redis 的 String 可以包含任何数据,比如图片或者序列化的对象,一个 Redis 中字符串 value 最多可以是 512M。并且的存储是动态的(意味着可以随时修改它本身的值),每次分配内存时会高出实际字符串的length,这样采用预分配冗余空间方式来减少内存的频繁分配。

使用场景一般是存储简单的键值类型。比如用户信息,登录信息,配置信息等。还有一种用得比较多的是 string 的incr/decr操作,即自减/自增操作。调用它是原子性的,无论调用多少次,都一一计算成功,例如需要增减库存的操作。

使用场景:

  1. 缓存基础数据:例如缓存登录用户的基本的缓存数据,但是建议使用 Hash 缓存
  2. 计数器:浏览量、点击数等。利用 string 的 incr/decr 操作,即自减/自增操作,并且它是原子性的。
  3. 限制请求次数:也是利用 incr 方法,以访问者的 ip 和其他信息作为 key,访问一次增加一次计数,超过次数则返回 false;
  4. 分布式共享 session:因为 Redis 是分布式的独立服务,可以在多个应用之间共享,所以可以用作分布式 Session 等场景;
  5. 分布式锁:SET 命令有个 NX 参数可以实现「key不存在才插入」,可以用它来实现分布式锁。

Redis 中字符串类型常用命令:

代码语言:javascript复制
SET key value 							# 设置指定key的值
GET key 										# 获取指定key的值
SETEX key seconds value 		# 设置指定key的值,并将 key 的过期时间设为 seconds 秒
SETNX key value 						# 只有在 key 不存在时设置 key 的值
2、哈希 Hashes

Redis 的 Hash 相当于 Java 的 HashMap,内部结构实现与 HashMap 一致,即数组 链表结构。只是 reHash 方式不一样。分为大 key、小 key、小 key 的 value。

由于 Hash 结构会在单个 Hash 元素在不足一定数量时进行压缩存储,所以可以大量节约内存。这一点在 String 结构里是不存在的。

使用场景:

  • 缓存: 能直观,相比 string 更节省空间,的维护缓存信息,如用户信息,视频信息等;
  • 购物车:类似存储商品信息,大 key 为商家 id,小 key 是商品 id 即 goodsId,value 为该 goodsId 的详细信息。

hash特别适合用于存储对象,常用命令:

代码语言:javascript复制
HSET key field value 				# 将哈希表 key 中的字段 field 的值设为 value
HGET key field 							# 获取存储在哈希表中指定字段的值
HDEL key field 							# 删除存储在哈希表中的指定字段
HKEYS key 									# 获取哈希表中所有字段
HVALS key 									# 获取哈希表中所有值
HGETALL key 								# 获取在哈希表中指定 key 的所有字段和值
3、列表 Lists

Lists 列表,它是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部(左边)或者尾部(右边),它的底层实际上是个链表( Redis 用双端链表实现 List)。每个链表的节点由一个 listNode 结构来表示,每个节点都有指向前置节点和后置节点的指针,同时表头节点的前置和后置节点都指向 NULL。列表有两个特点:有序、可以重复。

使用场景:

  • 消息队列:消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性。Redis 的 List 和 Stream 两种数据类型,就可以满足消息队列的这三个需求;
  • 最新消息排队功能:与消息队列类似。

Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:

代码语言:javascript复制
LPUSH key value1 [value2] 	# 将一个或多个值插入到列表头部
LRANGE key start stop 			# 获取列表指定范围内的元素
RPOP key 										# 移除并获取列表最后一个元素
LLEN key 										# 获取列表长度
BRPOP key1 [key2 ] timeout 	# 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超 时或发现可弹出元素为止
4、集合 Sets

Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。相对于列表,集合也有两个特点:无序、不可重复

一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,可以交集,并集,差集等等,所以 Set 类型除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。

使用场景:

  • 点赞、踩、收藏:Set 类型可以保证一个用户只能点一个赞;
  • 共同关注、标签:Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等;
  • 抽奖活动:存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。

Redis set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:

代码语言:javascript复制
SADD key member1 [member2] 	# 向集合添加一个或多个成员
SMEMBERS key 								# 返回集合中的所有成员
SCARD key 									# 获取集合的成员数
SINTER key1 [key2] 					# 返回给定所有集合的交集
SUNION key1 [key2] 					# 返回所有给定集合的并集
SDIFF key1 [key2] 					# 返回给定所有集合的差集
SREM key member1 [member2] 	# 移除集合中一个或多个成员
5、有序集合 Zset(Sorted sets)

Zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。

有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。

使用场景:

  • 排行榜:有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名,网站也可以按照用户关注数,更新时间,字数等打分,做排行等;
  • 电话、姓名排序:使用有序集合的 ZRANGEBYLEXZREVRANGEBYLEX 可以帮助我们实现电话号码或姓名的排序。

Zset 常用命令:

代码语言:javascript复制
ZADD key score1 member1 [score2 member2] 	# 向有序集合添加一个或多个成员,或者更新已存在成员的 分数
ZRANGE key start stop [WITHSCORES] 				# 通过索引区间返回有序集合中指定区间内的成员
ZINCRBY key increment member 							# 有序集合中对指定成员的分数加上增量 increment
ZREM key member [member …] 								# 移除有序集合中的一个或多个成员

Ps:Redis中的通用命令(针对key进行操作的相关命令):

代码语言:javascript复制
KEYS pattern 															# 查找所有符合给定模式( pattern)的 key
EXISTS key							 									# 检查给定 key 是否存在
TYPE key 																	# 返回 key 所储存的值的类型
TTL key 																	# 返回给定 key 的剩余生存时间(TTL, time to live),以秒为单位
DEL key 																	# 该命令用于在 key 存在是删除 key

二、Redis 特殊数据类型

Redis 的特殊数据类型还包括:geospatial 地理位置、Bitmap 位存储、HyperLogLogs 基数统计;此外,Redis 在 5.0 版本中还引入了 stream 这个全新的数据类型。

1、位存储 Bitmap

Bitmap,即位图,是一串连续的二进制数组(0和1),可以通过偏移量(offset)定位元素。BitMap通过最小的单位bit来进行0|1的设置,表示某个元素的值或者状态,时间复杂度为O(1)

Bitmap 类型非常适合二值状态统计的场景,这里的二值状态就是指集合元素的取值就只有 0 和 1 两种,在记录海量数据时,Bitmap 能够有效地节省内存空间。

使用场景:

  • 签到统计:我们把每天的日期作为 Bitmap 的 key,userId 作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录;
  • 连续签到用户总数:一共有 7 个这样的 Bitmap,如果我们能对这 7 个 Bitmap 的对应的 bit 位做 『与』运算。同样的 UserID offset 都是一样的,当一个 userID 在 7 个 Bitmap 对应对应的 offset 位置的 bit = 1 就说明该用户 7 天连续打卡;
  • 签到登陆态::Bitmap 提供了 GETBIT、SETBIT 操作,通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作,需要注意的是 offset 从 0 开始。

Bitmap 的常用命令:

代码语言:javascript复制
setbit <key> <offset> <value>		# 设置Bitmaps中某个偏移量的值(0或1)
getbit													# 获取Bitmaps中某个偏移量的值
bitcount <key> [start end] 			# 统计字符串从start字节到end字节比特值为1的数量
2、基数统计 HyperLogLogs

Redis HyperLogLog 是 Redis 2.8.9 版本新增的数据类型,是一种用于「统计基数」的数据集合类型,基数统计就是指统计一个集合中不重复的元素个数。但要注意,HyperLogLog 是统计规则是基于概率完成的,不是非常准确,标准误算率是 0.81%。

所以,简单来说 HyperLogLog 提供不精确的去重计数。

HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的内存空间总是固定的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。

使用场景:

  • 百万级网页 UV 计数: 在统计 UV 时,你可以用 PFADD 命令(用于向 HyperLogLog 中添加新元素)把访问页面的每个用户都添加到 HyperLogLog 中。 接下来,就可以用 PFCOUNT 命令直接获得 page1 的 UV 值了,这个命令的作用就是返回 HyperLogLog 的统计结果。 不过,有一点需要你注意一下,HyperLogLog 的统计规则是基于概率完成的,所以它给出的统计结果是有一定误差的,标准误算率是 0.81%。
代码语言:javascript复制
PFADD key element [element ...] 							# 添加指定元素到 HyperLogLog 中。
PFCOUNT key [key ...] 												# 返回给定 HyperLogLog 的基数估算值。
PFMERGE destkey sourcekey [sourcekey ...] 		# 将多个 HyperLogLog 合并为一个 HyperLogLog
3、地理位置 geospatial

Redis GEO 是 Redis 3.2 版本新增的数据类型,主要用于存储地理位置信息,并对存储的信息进行操作。

在日常生活中,我们越来越依赖搜索“附近的餐馆”、在打车软件上叫车,这些都离不开基于位置信息服务(Location-Based Service,LBS)的应用。LBS 应用访问的数据是和人或物关联的一组经纬度信息,而且要能查询相邻的经纬度范围,GEO 就非常适合应用在 LBS 服务的场景中。

使用场景:

  • 滴滴叫车:假设车辆 ID 是 33,经纬度位置是(116.034579,39.030452),我们可以用一个 GEO 集合保存所有车辆的经纬度,集合 key 是 cars:locations。 当用户想要寻找自己附近的网约车时,LBS 应用就可以使用 GEORADIUS 命令。
代码语言:javascript复制
geoadd china:city 116.40 39.90 beijing       # 添加城市经纬度及名称
geopos china:city beijing                # 返回城市的经度和纬度
geodist china:city beijing shanghai km       # 返回两个元素间的直线距离(单位为km)
georadius china:city 110 30 1000 km        # 以给定的经纬度(110,30)为中心,找到某一半经(1000km)的元素
1)georadius china:city 110 30 1000 km withdist 					# 显示到中间距离的位置
2)georadius china:city 110 30 1000 km withcoord 				# 显示他人的定位信息
3)georadius china:city 110 30 1000 km withcoord count 1 # 筛选出指定个数
georadiusbymember china:city beijing 1000km   # 找出位于指定元素的周围的其他元
4、Stream

Redis Stream 是 Redis 5.0 版本新增加的数据类型,Redis 专门为消息队列设计的数据类型。

在 Redis 5.0 Stream 没出来之前,消息队列的实现方式都有着各自的缺陷,例如:

  • 发布订阅模式,不能持久化也就无法可靠的保存消息,并且对于离线重连的客户端不能读取历史消息的缺陷;
  • List 实现消息队列的方式不能重复消费,一个消息消费完就会被删除,而且生产者需要自行实现全局唯一 ID。

基于以上问题,Redis 5.0 便推出了 Stream 类型也是此版本最重要的功能,用于完美地实现消息队列,它支持消息的持久化、支持自动生成全局唯一 ID、支持 ack 确认消息的模式、支持消费组模式等,让消息队列更加的稳定和可靠。

使用场景:主要就是消息队列。

0 人点赞