Redis 五种数据类型详解

redis 数据类型详解

本文主要介绍Redis 五种数据结构字符串、哈希、列表、集合、有序集合,本文主要从下面几个方面进行介绍

  • 使用方式
  • 使用场景
  • 内部编码

redis 字符串

命令格式

SET key value [EX seconds] [PX milliseconds] [NX|XX]
key:要设置的键名。
value:与键关联的新值。
[EX seconds]:设置键的过期时间(以秒为单位)。
[PX milliseconds]:设置键的过期时间(以毫秒为单位)。
[NX]:仅在键不存在时设置键。
[XX]:仅在键已经存在时设置键。

批量命令

使用 MSETMGET 批量多个键值对操作,批量操作可以减少网络传输时间,一定程度能提升性能,注意由于redis是单线程执行命令,如果一次批量执行命令过多,可能会阻塞其他命令。

使用场景

  • 分布式锁

    通过 NX 选项可以实现分布式锁, 当然如果考虑锁续期,以及分布式下单点故障,可以使用redission 替代。

  • 缓存

    比如缓存用户信息,登录态、

使用demo

> SET user:1000 "Alice"
OK
> GET user:1000
"Alice"


# 批量命令

> MSET user:1001 "Bob" user:1002 "Charlie" user:1003 "Diana"
OK

# 同时获取多个用户的姓名
> MGET user:1001 user:1002 user:1003
1) "Bob"
2) "Charlie"
3) "Diana"

内部编码

  • int:

    当字符串是整数,并且可以由 long 类型表示时,Redis 直接将这个整数存储为一个整数值,这种编码非常节省内存,因为不需要额外的 SDS 头部信息。

  • embstr:

    当字符串较短时,Redis 会使用 embstr 编码。,它将 SDS 的头部和数据放在一块连续的内存区域中,这样做可以减少内存碎片,提高缓存局部性。

  • raw:

    对于较长的字符串,Redis 会使用 raw 编码。

    在这种编码下,SDS 的头部和数据是分开分配的。这种方式更灵活,允许对大容量数据进行高效的管理。

综上 Redis字符串类型底层结构不仅仅是SDS,根据情况对空间与时间的折中选择合适的编码格式。

哈希

redis 哈希可以用来存储多个键值对,例如存储用户信息、商品信息等结构化数据

使用demo

# 使用HSET命令存储用户信息
HSET user:1000 username "codetonignt"  
HSET user:1000 age 18  
HSET user:1000 email "codetonignt@example.com"

# 使用HGET命令获取用户信息
HGET user:1000 username  # 返回 "codetonignt"  
HGET user:1000 age       # 返回 "18"  
HGET user:1000 email     # 返回 "codetonignt@example.com"

# 使用HGETALL命令获取用户的所有信息
HGETALL user:1000
# 返回 "username" "codetonignt" "age" "18" "email" "codetonignt@example.com"



# HMSET 设置多个字段值
HMSET user:1000 sex man nationality china

# HMGET 获取多个字段值
HMGET user:1000 sex nationality
# 返回  "man"  "china"

性能

hash 可以最多存储(2^32 - 1)个键值对,大多数hash操作时间复杂度是O(1)。HKEYS, HVALS, HGETALL时间复杂度是O(N);

内部编码

  • ziplist:

    当哈希中的字段数量较少,并且每个字段和值的大小都较小时,Redis 会使用 ziplist 编码,它更加节约内存。

  • hashtable:

    当哈希中的字段数量较多或单个字段/值的大小较大时,Redis 会切换到 hashtable 编码。哈希表提供 O(1) 平均时间复杂度的操作性能,但存储空间比ziplist消耗更多。

列表

Redis列表是简单的字符串列表,按照插入顺序排序。支持在两端进行添加或移除元素,列表的最大长度是 2^32 - 1 个元素。

Redis 列表的主要特性包括:

  • 可以在两端插入和删除
  • 列表中元素可以重复
  • 元素是有顺序的
  • BLPOP 和 BRPOP 操作是阻塞的

常见命令

LPUSH key value [value ...]:将一个或多个值插入到列表 key 的头部。
RPUSH key value [value ...]:将一个或多个值插入到列表 key 的尾部。
LPOP key:移除并返回列表 key 的头元素。
RPOP key:移除并返回列表 key 的尾元素。
LRANGE key start stop:获取列表 key 中指定范围内的元素,索引从 0 开始。
LLEN key:返回列表 key 的长度。
LINDEX key index:返回列表 key 中,下标为 index 的元素。
LSET key index value:设置列表 key 中下标为 index 的元素的值为 value。
LTRIM key start stop:修剪列表 key,使之只保留指定范围内的元素。
BLPOP key [key ...] timeout:移除并返回第一个非空列表的第一个元素,或者阻塞直到有元素可取或超时。
BRPOP key [key ...] timeout:与 BLPOP 类似,但是是从列表的尾部弹出元素。

内部编码

  • ziplist

    当列表中元素较少,且每个元素都是小正数或者是比较短字符串时,使用ziplist存储,它将多个元素紧凑地存储在一起,减少了内存碎片

  • 双向链表

    否则使用 双向链表实现的:Redis 使用的是双向链表,维护一个长度计数器,因此LLEN key时间复杂度O(1)

相关配置

如果列表中的元素数量超过了 list-max-ziplist-entries 参数设置的值(默认为512)。

或者如果列表中的任何一个元素的大小超过了 list-max-ziplist-value 参数设置的值(默认为64字节)

使用场景

  • 最新消息排行榜
  • 消息队列实现 (利用列表阻塞操作)

性能

list 最多可以有 2^32 - 1 个元素。

LPUSH、LPOP、RPUSH、RPOP 头部或尾部操作时间复杂度是O(1),

LINDEX、 LINSERT、 LSET 时间复杂度是O(N);

同样的如果链表中元素数量过多,同样存在大key问题 ,

(比如使用 LRANGE key start stop 等命令取出所有元素)

集合

Redis 集合是一种无序且不重复的数据结构,类似于Java中的HashSet,它支持交集、并集、差集等功能,用于存储一组字符串元素。

限制:

集合最多可以有 2^32 - 1 (4,294,967,295)个元素

使用示例

# 为用户 user1001 添加标签
> SADD user1001:tags "photography" "travel" "food"
(integer) 3

# 为用户 user1001 添加更多标签
> SADD user1001:tags "music"
(integer) 1

# 删除用户 user1001 的某个标签
> SREM user1001:tags "food"
(integer) 1


# 获取用户 user1001 的所有标签
> SMEMBERS user1001:tags
1) "photography"
2) "travel"
3) "music"

# 检查用户 user1001 是否有特定标签
> SISMEMBER user1001:tags "photography"
(integer) 1
> SISMEMBER user1001:tags "sports"
(integer) 0

性能

大多数集合操作包括SADD、SREM和SISMEMBER 时间复杂度都是O(1),SMEMBERS 返回所有元素如果集合中元素过多时应谨慎使用,考虑使用SSCAN替代,对于大数据场景,判断元素是否在集合中可能占大量内存,可以考虑使用bloom过滤器替代。

内部编码

  • intset:

    当集合中的所有元素都是整数,并且集合的大小较小(默认少于512个元素)时,Redis 会使用 intset 编码。intset 是一个有序的整数数组,支持快速查找和插入操作。它在内存中占用的空间较少,并且对小规模的数据集非常高效。

  • hashtable:

    当集合中的元素较多或包含非整数元素时,Redis 会切换到 hashtable 编码。哈希表提供 O(1) 平均时间复杂度的操作性能。

使用场景

可以用集合存储用户标签、好友关系等。

有序集合

Redis有序集合可以存储不重复的字符串成员,并为每个成员关联一个分数(score),以便按分数对成员进行排序。

因此它可以用来实现排行榜、优先级队列等业务场景

使用举例

> ZADD leaderboard 100 "user1"
(integer) 1
> ZADD leaderboard 200 "user2" 150 "user3"
(integer) 2


#  ZRANGE key start stop [WITHSCORES] 按照分数从小到大获取指定范围内的成员
> ZRANGE leaderboard 0 2 WITHSCORES
1) "user1"
2) "100"
3) "user3"
4) "150"
5) "user2"
6) "200"




# ZREVRANGE key start stop [WITHSCORES] 按照分数从大到小获取指定范围内的成员

> ZREVRANGE leaderboard 0 2 WITHSCORES
1) "user2"
2) "200"
3) "user3"
4) "150"
5) "user1"
6) "100"

性能

大部分操作时间复杂度是 O(log(n))

内部编码

  • ziplist:

    当有序集合中的元素数量较少,并且每个元素的大小也较小时,Redis 会使用 ziplist 编码。ziplist 非常适合小规模的数据集,因为它在内存中占用的空间较小,并且访问速度较快。

  • skiplist:

    当有序集合中的元素数量较多或单个元素的大小较大时,Redis 会切换到 skiplist 编码。跳表支持高效的范围查询和插入/删除操作,适用于大规模的数据集。

总结

  • 时间与空间兼顾

Redis 底层使用不仅仅考虑效率(时间复杂度),同时也兼顾空间复杂度,因此同一种数据类型,Redis底层编码因存储内容,大小等因素不同,会采用不同的编码进行存储。

  • 合理使用批量命令

Redis 基本是内存操作,因此性能很高,通常多个操作耗时主要在网络传输上,因此redis支持一些批量命令,如mget ,mset 可以有效减少网络传输时间,由于redis 是当线程模型,批量命令也不能滥用,一次批量键值对也不能过多,否则会阻塞其他命令。

  • 大Key问题

大key指占用大量内存的键,如很长的字符串,列表元素过多。访问或操作大Key,会导致 Redis 服务器阻塞一段时间,同时会消耗大量的 CPU 和内存资源,因此需要尽量避免。

相关推荐
Zilliz Planet18 分钟前
GenAI 生态系统现状:不止大语言模型和向量数据库
数据库·人工智能·语言模型·自然语言处理
Xvens1 小时前
thinkphp6 redis 哈希存储方式以及操作函数(笔记)
redis·php·哈希算法
瓜牛_gn1 小时前
redis详细教程(4.GEO,bitfield,Stream)
数据库·redis·缓存
昨天今天明天好多天1 小时前
【Linux】Redis 部署
linux·redis·bootstrap
练习两年半的工程师1 小时前
建立一个简单的todo应用程序(前端React;后端FastAPI;数据库MongoDB)
前端·数据库·react.js·fastapi
新知图书2 小时前
MySQL 9从入门到性能优化-创建触发器
数据库·mysql·性能优化
HEX9CF2 小时前
【SQLite】改善默认输出格式不直观难以阅读问题:通过修改输出设置提升数据可读性
数据库·sqlite
HEX9CF3 小时前
【Linux】SQLite 数据库安装教程(Ubuntu 22.04)
linux·数据库·sqlite
恬淡虚无真气从之3 小时前
django中entity.save(using=)的使用
数据库·python·django
零希3 小时前
正则表达式
java·数据库·mysql