Redis-常见数据类型及应用场景

目录

String

内部实现

常用指令

应用场景

List

内部实现

常用命令

应用场景

Hash

内部实现

常用命令

应用场景

Set

内部实现

常用命令

应用场景

ZSet

内部实现

常用命令

应用场景


Redis常见的基本数据类型包括String、List、Hash、Set、ZSet;除此之外,例如BitMap、Stream也是十分常见的数据类型。

String

String是最基本的key-value结构,其中key是唯一标识,value是具体的值。

内部实现

String类型的底层数据结构主要是int和SDS。字符串对象的内部编码方式有三种:int、raw和embstr:

· 如果一个字符串对象保存的是整数值 ,并且这个值可以用long类型来表示,则字符串对象会将整个数值保存在字符串对象的ptr属性中(将void*转换成long),并将字符串对象的编码设置为int;

**·**如果字符串对象保存的是一个字符串,且这个字符串的长度小于等于32字节,则字符串对象会使用一个简单动态字符串(SDS)来保存这个字符串,并将对象的编码方式设置为embstr,embstr是一种专门用于保存短字符串的一种优化编码方式;

**·**如果字符串对象保存的是一个字符串,且这个字符串的长度大于32字节,则字符串对象使用SDS来保存这个字符串,并将编码格式设置为raw。

这个长度在不同redis版本是不同的。在redis 2.+是32字节,redis3.0 - 4.0是39字节,redis5.0+是44字节。

embstr和raw编码格式的区别主要在于:embstr只需要分配一次内存,其会通过一次内存分配函数来分配一块连续的空间保存redisObject和SDS;而rwa编码方式会分配两次内存,来分别存储redisObject和SDS动态字符串。

很显然,embstr的优点是只需分配一次内存,也只需要释放一次内存,而且其将字符串对象所有的数据都保存在一块连续的内存,可以减少内存碎片,提高CPU缓存命中率。但其缺点embstr编码的字符串对象是只读的,不支持修改,如果修改会转换为raw编码。

raw编码可以动态修改,支持追加、截断等操作,无需重新分配RedisObject,兼容性比较好。

常用指令

复制代码
# 设置 key-value
set key test

# 根据 key 获得对应的value
get key

# 判断 key 是否存在
exists key

# 返回 key 所存储的字符串值的长度
strlen key

# 删除 key 对应的值
del key
复制代码
# 批量设置 key
mset key1 value1 key2 value2

# 分别获取 key1 和 key2
get key1
get key2

#批量获取 key1 和 key2
mget key1 key2

应用场景

1、做缓存:通常情况下,使用redis的String类型来作为缓存,例如在手机号登陆时,使用redis来缓存验证码,再通过EX参数给其设置验证码过期时间,例如60s过期。又例如,在项目中,使用String类型来缓存活动信息、中奖信息等;

2、实现分布式锁:key为锁,value为客户端的唯一标识。set命令有个NX参数可以实现 key不存在才插入,可以利用其来实现分布式锁,并结合EX参数来设置过期时间,防止一个服务持有分布式锁时服务挂了的情况。在释放锁时,由于首先需要判断当前客户端是否是持有锁的客户端,如果是,再删除,因此这个操作不是原子性的,可以通过Lua脚本来保证原子性。

复制代码
// Lua脚本,先判断 
if redis.call("get", key) == value then
    return redis.call("del", key)
else
    return 0;
end

3、共享Session信息:在开发项目时,通常会使用Session来保存用户的会话状态,这些session信息是存储在服务端的,只适合于单系统应用,不适合微服务/分布式系统。

就比如服务器1保存的用户A的session信息,但用户A第二次访问时被分配到了服务器2,此时服务器2没有用户A的session信息,就需要重新登录,主要是因为分布式系统每次会将请求随机分配到不同的服务器。

因此,可以使用redis来存储这些session信息,进行统一的存储与管理,无论请求到哪台服务器,服务器都会去同一个redis服务器去获取相关的session信息,就可以解决分布式系统下session的存储问题。

List

List列表时简单的字符串列表,按照插入顺序排序,可以从头部或者尾部向List列表添加元素

内部实现

List类型的底层数据结构是由双向链表或压缩列表来实现的:

**·**当列表的元素个数小于512个(默认值,可以通过参数 list-max-ziplist-entries 进行配置),列表每个元素的值都小于64字节(默认值,可以通过参数 list-max-ziplist-value 来进行配置)时,redis使用压缩列表作为List类型的底层数据结构;

**·**如果列表的元素不满足上面的条件,就会使用双向链表来作为List的底层数据结构

但是在redis3.2版本之后,List数据类型底层数据结构就只由 quicklist 实现了。

常用命令

复制代码
# 将一个或多个value插入到key列表的最左边,最后的值在最前面
lpush key value [value ...]

# 将一个或多个value插入到key列表的表尾
rpush key value [value ...]

# 移除并返回 key 列表的头元素
lpop key

# 移除并返回 key 列表的尾元素
rpop key

# 返回列表key中指定区间内的元素,区间以偏移量start和stop指定,从0开始
lrange key start stop

应用场景

可以基于List来实现消息队列,一个消息队列需要满足以下条件:

1、消息的存取

List是按照先进先出的顺序来进行数据的存取的,生产者通过LPUSH命令来向List中存放消息,消费者通过RPOP命令来从List中读取消息,满足先进先出以及消息的顺序性。

但基于LPUSH+RPOP命令实现的消息队列有一个潜在的风险点。在生产者往List写入数据时,List并不会通知消费者有新的消息写入,如果消费者想要及时处理消息,就需要在程序中不停的调用RPOP命令,但这种情况如果生产者一直没有写入消息,那消费者就会一直循环去读消息。

Redis提供了BRPOP命令,这个命令也可以称为阻塞式读取,消费者在没有读取到List中数据时,自动阻塞,直到有新的数据写入了List,才会重新开始读取新数据

2、处理重复的消息

要保证重复的消息不会被重复消费,首先需要保证每一条消息都有一个全局唯一ID (可以通过UUID来实现);第二点就是消费者需要记录已经处理过的消息的ID,每当消费者收到一条消息时,消费者首先判断这条消息ID是否与已经消费过的消息ID是否相等,如果不相等就处理,如果相等就返不再处理。

3、保证消息的可靠性

当消费者从List中读取一条消息后,List就不会再保留这条消息了。此时,如果消费者还没有完成这条消息的消费,出现了服务挂了的情况,那么这条消息就会丢失,服务器重启后无法重新消费这条消息。

List提供了BRPOPLPUSH命令,这个命令的作用是让消费者程序从一个List中读取消息,同时,redis会将这条消息再插入到另一个List留存。消费者基于BRPOPLPUSH命令来读取消息的话,如果出现服务挂了的情况,重启后可以从备份List中重新读取这条消息并进行处理。

总结一下,就是LPUSH和RPOP可以满足消息的存取、BRPOP可以实现阻塞式读取、通过给每条消息加上全局唯一的id以及消费者记录已经处理过的消息ID来处理重复的消息、通过BRPOPLPUSH命令来保证消息的可靠性。

Hash

Hash是一个键值对集合,其中value的形式如:vlaue=[{field1, value1}, ..., {fieldN, valueN}],Hash非常适合于存储对象。

内部实现

Hash类型的底层数据结构是由压缩列表或哈希表实现的:

**·**如果哈希类型元素个数小于512个(默认值,可以由hash-max-ziplist-entries配置),所有值小于64字节(默认值,可以由hash-max-ziplist-value配置)时,redis会使用压缩列表作为Hash类型的底层数据结构;

**·**如果不满足上述条件,redis就会使用哈希表作为Hash类型的底层数据结构。

在redis7.0中,压缩列表数据结构已经废弃,由listpack数据结构来实现。

常用命令

复制代码
# 存储一个哈希表key的键值
hset key field value

# 获取一个哈希表key对应的field键值
hget key field

# 在一个哈希表key中存储多个键值对
hmset key field value [field vlaue...]

# 批量获取哈希表key中多个field键值
hmget key field [field ...]

# 删除哈希表key中的field键值
hdel key field [field ...]

# 返回哈希表key中的field键值
hlen key

# 获取哈希表key中所有的键值
hgetall key

# 为哈希表key中field键的值加上增量n
hincrby key field n

应用场景

**·**使用hash数据类型可以缓存类似于用户信息,订单信息等。对象里面有很多属性,使用string来存的话每次都要做json序列化,会有性能问题,使用hash进行对象存储就可以直接拿到某个属性的值。例如用户信息存储、地理位置存储等都是属于对象存储的。

· 可以使用Hash来做购物车,用户id作为key,商品id作为field,商品数量作为value,具体操作以下为例:

复制代码
# 给购物车中添加商品
hset cart:{用户id} {商品id} 1

# 增加商品数量
hincrby cart:{用户id} {商品id} 1

# 统计商品总数
hlen cart:{用户id}

# 删除商品
hdel cart:{用户id} {商品id}

# 获取购物车所有商品
hgetall cart:{用户id}

Set

Set类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。一个集合最多可以存储2^32 - 1个元素,该类型除了支持集合内的增删改查还支持多个集合取交集、并集、差集。

内部实现

Set类型的底层数据结构是由哈希表或整数集合实现的:

**·**如果集合中的元素都是整数且元素个数小于512(默认值,set-maxintset-entries 配置)个,Redis会使用整数集合作为Set类型的底层数据结构;

**·**如果集合中的元素不满足上述条件,则Redis使用哈希表作为Set类型的底层数据结构。

常用命令

集合内的增删改查

复制代码
# 给集合key中存入元素,元素存在则忽略,若key不存在则新建
sadd key value value value1

# 从集合key中删除元素
srem key value

# 获取集合key中所有元素
smembers key

# 获取集合key中的元素个数
scard key

# 判断value元素是否存在于集合key中
sismember key value

# 从集合key中随机选出count个元素,元素不从key中删除
srandmemeber key count

# 从集合key中随机选出count个元素,元素从key中删除
SPOP key count

集合外的交集、并集、差集

复制代码
# 交集运算
sinter key key1

# 将交集结果存入新的集合
sinterstore new_key key key1

# 并集运算
sunion key key1

# 差集运算
sdiff key key1

应用场景

· 点赞:利用Set结构的特性,可以实现每个用户仅允许点赞一次。例如对于文章的点赞,我们可以将文章id作为key,然后将用户id作为value,根据Set的特性,同一个key中不允许有相同的value。

我们将article:1作为key,uid:1、uid:2、uid:3作为value,当用户1、2、3分别对文章进行点赞时:

复制代码
# 用户1点赞
sadd article:1 uid:1

# 用户2点赞
sadd article:1 uid:2

# 用户3点赞
sadd article:1 uid:3

如果用户1取消点赞,即:

复制代码
# 用户1取消点赞
srem article:1 uid:1

获取文章1的所有点赞用户、以及点赞数

复制代码
# 获取文章1所有的点赞用户
smembers article:1

# 获取文章1的点赞数
scard article:1

判断用户1是否对该文章点赞

复制代码
# 判断用户1是否点赞
sismember article:1 uid:1

· 共同关注:利用Set来求交集可以实现共同关注的功能,例如用户1关注的公众号id为1、2、3,用户2关注2、3、4

实现用户关注

复制代码
# 用户1关注公众号1、2、3
sadd uid:1 1 2 3

# 用户2关注公众号2、3、4
sadd uid:2 2 3 4

求用户1和用户2的共同关注

复制代码
# 求用户1和用户2的共同关注
sinter uid:1 uid:2

给用户2推荐用户1关注的公众号

复制代码
# 谁在前面返回谁的非共同value
sdiff uid:1 uid:2

· 也可以使用Set类型来实现抽奖

ZSet

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

内部实现

ZSet的底层由压缩列表或跳表实现:

**·**如果有序集合的元素个数小于128个,并且每个元素的值小于64字节时,redis使用压缩列表来作为ZSet的底层存储结构;

**·**如果有序集合的元素不满足上面的条件,redis会使用跳表作为ZSet类型的底层数据结构。

在redis7.0中,压缩列表数据结构已经废弃了,由listpack数据结构实现

常用命令

复制代码
# 往有序集合key中加入带分值的元素
zadd key score member

# 删除有序集合key中元素member的分值
zrem key member

# 返回有序集合key中元素member的分值
zscore key member

# 返回有序集合中key的个数
zcard key

# 给有序集合key中元素member的分值加上increment
zincrby key increment member

# 正序获取有序集合key从start到stop下标的元素
zrange key start stop withscores

# 倒序获取有序集合key从start到stop下标的元素
zrevrange key start stop withscores

应用场景

· 基于ZSet实现排行榜,例如学生成绩排名榜,微信步数榜、商品销量榜等等

相关推荐
麦兜*16 小时前
【springboot】图文详解Spring Boot自动配置原理:为什么@SpringBootApplication是核心?
android·java·spring boot·spring·spring cloud·tomcat
rabbit_pro17 小时前
Java使用Mybatis-Plus封装动态数据源工具类
java·python·mybatis
期待のcode17 小时前
Java虚拟机类加载机制
java·开发语言
瀚高PG实验室17 小时前
逻辑导入导出(pg_dump/pg_restore)用法2-导入到不同的schema或tablespace
数据库·瀚高数据库
whyfail17 小时前
前端数据存储新选择:IndexedDB与Dexie.js技术指南
前端·javascript·数据库
短剑重铸之日17 小时前
《SpringBoot4.0初识》第四篇:原生镜像
java·原生镜像·springboot4.0
煎蛋学姐17 小时前
SSM校园快递系统q9061(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·开题报告·java 开发·校园信息化·ssm 框架
程序员欣宸17 小时前
LangChain4j实战之十二:结构化输出之三,json模式
java·人工智能·ai·json·langchain4j
元亓亓亓17 小时前
考研408--操作系统--day11--文件管理&逻辑物理结构&目录&存储空间管理
数据库·考研·文件管理·408