Redis 数据类型详解:5种基本类型与应用场景

Redis 数据类型详解:5 种基本类型与应用场景

Redis (REmote DIctionary Server) 是一个开源的、基于内存的、高性能的键值对(key-value)数据库。它以其卓越的性能、丰富的数据类型以及对事务、持久化、Lua 脚本、发布/订阅等高级特性的支持,在众多 NoSQL 数据库中脱颖而出,被广泛应用于缓存、消息队列、排行榜、计数器、分布式锁等各种场景。

Redis 的强大功能很大程度上得益于其多样化的数据类型。与传统的关系型数据库不同,Redis 不仅仅提供简单的字符串存储,还内置了多种数据结构,使得开发者可以直接在 Redis 中进行更复杂的数据操作,减少了客户端与服务器之间的网络交互,提高了应用程序的性能。

本文将深入探讨 Redis 的 5 种基本数据类型:String(字符串)、List(列表)、Set(集合)、Hash(哈希)和 Sorted Set(有序集合),详细介绍它们的特性、常用命令、底层实现以及典型应用场景,帮助读者全面理解并灵活运用 Redis 的数据类型。

1. String(字符串)

String 类型是 Redis 中最基本、最常用的数据类型。它可以存储字符串、整数或浮点数。Redis 中的字符串是二进制安全的(binary-safe),这意味着它可以存储任何类型的数据,包括图片、序列化的对象等,最大长度可达 512MB。

特性:

  • 二进制安全: 可以存储任何二进制数据。
  • 高效操作: 大部分字符串操作的时间复杂度都是 O(1)。
  • 可变长度: 字符串长度可以动态调整。
  • 整数/浮点数支持: 可以直接对字符串类型的整数或浮点数进行自增、自减操作。

常用命令:

  • SET key value:设置 key 对应的 value。
  • GET key:获取 key 对应的 value。
  • SETNX key value:仅当 key 不存在时,设置 key 对应的 value(常用于实现分布式锁)。
  • SETEX key seconds value:设置 key 对应的 value,并设置过期时间(秒)。
  • PSETEX key milliseconds value:设置 key 对应的 value,并设置过期时间(毫秒)。
  • GETSET key value:设置 key 的新 value,并返回旧的 value。
  • APPEND key value:将 value 追加到 key 原来的值的末尾。
  • STRLEN key:获取 key 对应的 value 的长度。
  • INCR key:将 key 对应的 value 自增 1(如果 value 是整数)。
  • INCRBY key increment:将 key 对应的 value 增加指定的整数 increment。
  • DECR key:将 key 对应的 value 自减 1(如果 value 是整数)。
  • DECRBY key decrement:将 key 对应的 value 减少指定的整数 decrement。
  • MSET key1 value1 key2 value2 ...:批量设置多个 key-value。
  • MGET key1 key2 ...:批量获取多个 key 对应的 value。

底层实现:

Redis 的字符串类型底层使用简单动态字符串(Simple Dynamic String,SDS)实现。SDS 与 C 语言中的传统字符串相比,具有以下优势:

  • 常数时间复杂度获取字符串长度: SDS 结构体中直接存储了字符串的长度,因此获取长度的时间复杂度为 O(1),而 C 语言字符串需要遍历整个字符串才能获取长度,时间复杂度为 O(N)。
  • 防止缓冲区溢出: SDS 在修改字符串时会检查剩余空间是否足够,如果不足则会先扩展空间再进行修改,避免了缓冲区溢出问题。
  • 减少内存重分配次数: SDS 采用了空间预分配和惰性空间释放策略,减少了字符串修改时内存重分配的次数,提高了性能。
  • 二进制安全: SDS 使用 len 属性记录字符串的长度,而不是依赖于空字符 \0 来判断字符串的结束,因此可以存储包含空字符的二进制数据。

应用场景:

  • 缓存: 将热点数据(如用户信息、文章内容、商品信息等)存储在 Redis 中,减少对数据库的访问,提高应用程序的响应速度。
  • 计数器: 利用 INCRDECR 命令实现计数器功能,如统计文章阅读量、点赞数、用户在线人数等。
  • 分布式锁: 利用 SETNX 命令实现分布式锁,保证在分布式环境下多个进程或线程对共享资源的互斥访问。
  • Session 共享: 将用户的 Session 信息存储在 Redis 中,实现 Session 共享,解决多台服务器之间的 Session 同步问题。
  • 全局唯一 ID 生成器: 利用 INCR 命令生成全局唯一的 ID。

2. List(列表)

List 类型是一个有序的字符串列表,按照插入顺序排序。可以在列表的头部(左侧)或尾部(右侧)添加或删除元素。

特性:

  • 有序性: 元素按照插入顺序排列。
  • 双向操作: 可以在列表的两端进行插入和删除操作。
  • 可重复元素: 列表中可以包含重复的元素。

常用命令:

  • LPUSH key value1 value2 ...:将一个或多个值插入到列表的头部。
  • RPUSH key value1 value2 ...:将一个或多个值插入到列表的尾部。
  • LPOP key:移除并返回列表的头部元素。
  • RPOP key:移除并返回列表的尾部元素。
  • LLEN key:获取列表的长度。
  • LRANGE key start stop:获取列表指定范围内的元素(索引从 0 开始,支持负数索引,-1 表示最后一个元素)。
  • LINDEX key index:获取列表指定索引位置的元素。
  • LSET key index value:设置列表指定索引位置的元素值。
  • LINSERT key BEFORE|AFTER pivot value:在列表的某个元素之前或之后插入一个新元素。
  • LREM key count value:从列表中删除指定数量的元素。
  • LTRIM key start stop:对列表进行修剪,只保留指定范围内的元素。
  • BLPOP key1 key2 ... timeout:阻塞式地从多个列表中弹出头部元素,如果列表为空则阻塞,直到有元素可弹出或超时。
  • BRPOP key1 key2 ... timeout:阻塞式地从多个列表中弹出尾部元素。

底层实现:

Redis 3.2 版本之前,List 类型的底层实现主要使用两种数据结构:

  • ziplist(压缩列表): 当列表中的元素较少且每个元素的长度较小时,Redis 会使用 ziplist 来存储列表。ziplist 是一种紧凑的、节省内存的数据结构,它将所有元素连续存储在一起,通过一系列特殊的编码方式来表示不同类型和长度的数据。
  • linkedlist(双向链表): 当列表中的元素较多或某个元素的长度较大时,Redis 会将 ziplist 转换为 linkedlist。linkedlist 是一种传统的链表结构,每个节点包含一个指向前一个节点和后一个节点的指针,以及存储元素值的指针。

Redis 3.2 版本之后,List 类型的底层实现改为了 quicklist。quicklist 是 ziplist 和 linkedlist 的结合体,它将 linkedlist 按段切分,每一段使用 ziplist 来存储数据,多个 ziplist 之间使用双向指针连接起来。quicklist 既保留了 ziplist 节省内存的优点,又避免了 ziplist 在插入和删除元素时可能导致的连锁更新问题(连锁更新是指当 ziplist 中的某个元素发生变化时,可能需要对后续的所有元素进行重新编码,导致性能下降)。

应用场景:

  • 消息队列: 利用 LPUSHRPOP(或 BRPOP)命令实现简单的消息队列,生产者将消息推入列表,消费者从列表中取出消息进行处理。
  • 任务队列: 与消息队列类似,可以将需要异步处理的任务放入列表中,由多个 worker 进程从列表中取出任务执行。
  • 最新动态: 存储用户的最新动态,如朋友圈、微博等,按照时间顺序展示。
  • 日志收集: 将应用程序产生的日志信息追加到列表中,方便后续的分析和处理。

3. Set(集合)

Set 类型是一个无序的、不重复的字符串集合。可以对集合进行添加、删除、查找元素等操作,还可以进行交集、并集、差集等集合运算。

特性:

  • 无序性: 集合中的元素没有固定的顺序。
  • 唯一性: 集合中不允许出现重复的元素。
  • 集合运算: 支持交集、并集、差集等集合运算。

常用命令:

  • SADD key member1 member2 ...:向集合中添加一个或多个成员。
  • SREM key member1 member2 ...:从集合中移除一个或多个成员。
  • SMEMBERS key:获取集合中的所有成员。
  • SISMEMBER key member:判断一个成员是否属于集合。
  • SCARD key:获取集合的成员数量。
  • SRANDMEMBER key [count]:随机返回集合中的一个或多个成员。
  • SPOP key [count]:随机移除并返回集合中的一个或多个成员。
  • SMOVE source destination member:将一个成员从源集合移动到目标集合。
  • SINTER key1 key2 ...:计算多个集合的交集。
  • SUNION key1 key2 ...:计算多个集合的并集。
  • SDIFF key1 key2 ...:计算多个集合的差集。
  • SINTERSTORE destination key1 key2 ...: 求交集并将结果集保存到 destination.
  • SUNIONSTORE destination key1 key2 ...: 求并集并将结果集保存到 destination.
  • SDIFFSTORE destination key1 key2 ...: 求差集并将结果集保存到 destination.

底层实现:

Set 类型的底层实现使用两种数据结构:

  • intset(整数集合): 当集合中的所有元素都是整数,并且元素的数量较少时,Redis 会使用 intset 来存储集合。intset 是一种紧凑的、节省内存的数据结构,它将所有整数按照从小到大的顺序存储在一个连续的内存块中。
  • hashtable(哈希表): 当集合中的元素不是整数,或者元素的数量较多时,Redis 会将 intset 转换为 hashtable。hashtable 是一种键值对的集合,它的键是集合中的元素,值始终为 NULL。hashtable 提供了快速的查找、添加和删除操作,时间复杂度为 O(1)。

应用场景:

  • 标签系统: 将用户的标签存储在集合中,可以方便地查找拥有相同标签的用户,或者根据标签进行用户推荐。
  • 好友关系: 将用户的好友关系存储在集合中,可以方便地查找共同好友、好友推荐等。
  • 黑名单/白名单: 将需要阻止或允许访问的 IP 地址或用户 ID 存储在集合中,实现黑名单或白名单功能。
  • 抽奖系统: 将参与抽奖的用户 ID 存储在集合中,利用 SRANDMEMBERSPOP 命令随机抽取中奖用户。
  • 唯一访客统计: 将访问网站的独立 IP 地址存储在集合中,可以统计网站的独立访客数量。

4. Hash(哈希)

Hash 类型是一个键值对集合,其中每个键都映射到一个包含多个字段和值的哈希表。Hash 类型非常适合存储对象类型的数据。

特性:

  • 键值对集合: 每个哈希表包含多个字段和值。
  • 字段唯一: 哈希表中的字段是唯一的。
  • 适合存储对象: 可以将对象的多个属性存储在同一个哈希表中。

常用命令:

  • HSET key field value:设置哈希表中指定字段的值。
  • HGET key field:获取哈希表中指定字段的值。
  • HMSET key field1 value1 field2 value2 ...:批量设置哈希表中多个字段的值。
  • HMGET key field1 field2 ...:批量获取哈希表中多个字段的值。
  • HGETALL key:获取哈希表中所有字段和值。
  • HKEYS key:获取哈希表中所有字段。
  • HVALS key:获取哈希表中所有值。
  • HLEN key:获取哈希表中字段的数量。
  • HEXISTS key field:判断哈希表中是否存在指定字段。
  • HDEL key field1 field2 ...:删除哈希表中一个或多个字段。
  • HINCRBY key field increment:将哈希表中指定字段的值增加指定的整数 increment。
  • HINCRBYFLOAT key field increment:将哈希表中指定字段的值增加指定的浮点数 increment。

底层实现:

Hash 类型的底层实现使用两种数据结构:

  • ziplist(压缩列表): 当哈希表中的字段数量较少且每个字段和值的长度较小时,Redis 会使用 ziplist 来存储哈希表。ziplist 将字段和值按照相邻的方式存储在一起,通过一系列特殊的编码方式来表示不同类型和长度的数据。
  • hashtable(哈希表): 当哈希表中的字段数量较多或某个字段或值的长度较大时,Redis 会将 ziplist 转换为 hashtable。hashtable 是一种键值对的集合,它的键是哈希表中的字段,值是字段对应的值。hashtable 提供了快速的查找、添加和删除操作,时间复杂度为 O(1)。

应用场景:

  • 存储对象: 将对象的多个属性存储在同一个哈希表中,如用户信息(姓名、年龄、性别、邮箱等)、商品信息(名称、价格、描述、库存等)。
  • 购物车: 将用户的购物车信息存储在哈希表中,字段为商品 ID,值为商品数量。
  • 配置管理: 将应用程序的配置信息存储在哈希表中,方便读取和修改。

5. Sorted Set(有序集合)

Sorted Set 类型是一个有序的、不重复的字符串集合。每个成员都关联一个分数(score),Redis 根据分数对成员进行排序。Sorted Set 既具有 Set 的唯一性,又具有 List 的有序性。

特性:

  • 有序性: 集合中的成员按照分数从小到大排序。
  • 唯一性: 集合中不允许出现重复的成员。
  • 分数: 每个成员都关联一个分数,用于排序。
  • 分数可重复: 不同的成员可以拥有相同的分数。

常用命令:

  • ZADD key score1 member1 score2 member2 ...:向有序集合中添加一个或多个成员,并指定分数。
  • ZREM key member1 member2 ...:从有序集合中移除一个或多个成员。
  • ZRANGE key start stop [WITHSCORES]:获取有序集合指定范围内的成员(按照分数从小到大排序,支持负数索引,-1 表示最后一个成员)。
  • ZREVRANGE key start stop [WITHSCORES]:获取有序集合指定范围内的成员(按照分数从大到小排序)。
  • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]:获取有序集合指定分数范围内的成员。
  • ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]:获取有序集合指定分数范围内的成员(按照分数从大到小排序)。
  • ZSCORE key member:获取有序集合中指定成员的分数。
  • ZRANK key member:获取有序集合中指定成员的排名(按照分数从小到大排序,排名从 0 开始)。
  • ZREVRANK key member:获取有序集合中指定成员的排名(按照分数从大到小排序)。
  • ZCARD key:获取有序集合的成员数量。
  • ZCOUNT key min max:计算有序集合指定分数范围内的成员数量。
  • ZINCRBY key increment member:将有序集合中指定成员的分数增加指定的增量 increment。
  • ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] : 求交集,并将结果集保存到 destination.
  • ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] : 求并集,并将结果集保存到 destination.

底层实现:

Sorted Set 类型的底层实现使用两种数据结构:

  • ziplist(压缩列表): 当有序集合中的成员数量较少且每个成员和分数的长度较小时,Redis 会使用 ziplist 来存储有序集合。ziplist 将成员和分数按照相邻的方式存储在一起,按照分数从小到大排序。
  • skiplist(跳跃表)和 hashtable(哈希表): 当有序集合中的成员数量较多或某个成员或分数的长度较大时,Redis 会将 ziplist 转换为 skiplist 和 hashtable。
    • skiplist: skiplist 是一种多层级的有序链表结构,它通过在每个节点中维护多个指向不同层级节点的指针,实现了快速的查找、插入和删除操作,时间复杂度为 O(logN)。
    • hashtable: hashtable 用于存储成员到分数的映射关系,提供了快速的查找成员分数的操
THE END