Redis基础篇 - 数据类型整理及常用类型详解(含各种使用场景)

1 Redis中的数据类型

Redis中的数据类型可以分为两个大类:

  • 核心数据类型:Redis社区版就支持的数据类型。
  • 拓展数据类型:Redis Stack 和 Redis Enterprise 中包含的拓展类型。

1.1 核心数据类型

核心数据类型包含以下几种:

  • String字符串:最基本的 Redis 数据类型,表示字节序列。
  • Hash哈希:键值对类型的集合。
  • List列表:按插入顺序排序的字符串列表。
  • Set集合:是唯一字符串的无序集合
  • Sorted set有序集合:唯一字符串的集合,它们通过每个字符串的关联分数来维护顺序。
  • Stream流:作用类似于仅附加日志,流有助于按事件发生的顺序记录事件,然后将它们联合起来进行处理。
  • Bitmap位图:方便对字符串执行按位运算
  • Bitfield位域:Redis 位域有效地对字符串值中的多个计数器进行编码。位域提供原子获取、设置和增量操作,并支持不同的溢出策略
  • Geospatial地理空间索引:对于查找给定地理半径或边界框内的位置很有用。

1.2 拓展数据类型

拓展数据类型包含以下几种:

  • JSON:提供了一种与常见的 JSON 文本文件格式相匹配的数据结构,包括结构化的、分层的数组和键值对象。您可以将 JSON 文本导入 Redis 对象并访问、修改和查询各个数据元素。
  • Probabilistic data types概率数据类型:这些数据类型使您能够以近似但高效的方式收集和计算统计数据。有以下类型可供选择:
    • HyperLogLog:提供大型集合基数(即元素数量)的概率估计。
    • Bloom filter布隆过滤器:检查集合中是否存在元素。
    • Cuckoo filter布谷鸟过滤器:检查集合中是否存在元素。它们与布隆过滤器类似,但在功能和性能之间的权衡略有不同。
    • t-digest:根据数据值流估计百分位数。
    • Top-K:估计数据点在值流中的排名。
    • Count-min sketch:估计值流中数据点的频率。
  • Time series时间序列:存储和查询带时间戳的数据点。

1.3 通用特性

1.3.1 【聚合数据类型】自动创建与删除Keys

使用Redis List时,不需要手动创建空的列表再添加元素,而不需要在列表为空后手动删除列表。Redis会在我们往列表里添加元素时自动创建列表(如果列表不存在的的话),也会在列表为空时自动删除列表。

This is not specific to lists, it applies to all the Redis data types composed of multiple elements – Streams, Sets, Sorted Sets and Hashes.

这种特性并不只是争对Lists,也适用于所有由多个元素组成的Redis数据类型(如流、集合、有序集合、Hashes)。

基本上我们可以用三个规则来总结:

  1. 当我们向聚合数据类型添加元素时,如果目标键不存在,则会在添加元素之前创建一个空的聚合数据类型。
  2. 当我们从聚合数据类型中删除元素时,如果容器内变空,则键会自动销毁。 Stream 数据类型是此规则的唯一例外。
  3. 调用一个只读命令,比如LLEN(返回列表的长度),或者一个移除元素的写命令(对于一个空的键),总是会产生与键持有空的聚合类型相同的结果,命令期望找到该类型的空聚合。

2 常用数据类型详解

2.1 String

2.1.1 概述

字符串是最基本最简单的Redis数据类型,可以存储字节序列,包括文本、序列化的对象以及二进制数组。通常用于缓存,同时也支持一些附加功能,可以实现计数器和执行按位运算。

类似于go语言里的string。

2.1.2 命令

字符串类型全量操作命令

2.1.3 使用字符串实现计数器

字符串是 Redis 的基本值,使用它也可以执行一些有趣的操作。例如原子增量操作可以用于实现计数器。

相关命令:

  • INCR:将字符串值解析为整数,然后将其加一,最后将获得的值设置为新值。
  • DECR:将字符串值解析为整数,然后将其减一,最后将获得的值设置为新值。
  • DECRBY:将字符串值解析为整数,然后将其减N,最后将获得的值设置为新值。

在内部,它始终是相同的命令,但作用方式略有不同。

这几个操作都是原子的!!!

2.1.4 使用字符串实现简单分布式锁

使用Redis实现分布式锁是很常见的场景。下面是一个简单的基于Redis的分布式锁的解决方案:

  1. 获取锁:

    • 使用SET key value NX PX milliseconds命令去尝试设置一个键值对。其中,key是用于表示锁的唯一标识,value可以是一个唯一的标识符(如UUID),NX选项表示仅在键不存在时才设置键值对,PX选项表示设置键的过期时间(毫秒)。
    • 如果该命令返回成功,表示获取到了锁,可以执行需要保护的代码。
    • 如果该命令返回失败(表示锁已经被其他实例持有),可以选择重试或等待一段时间后再次尝试获取锁。
  2. 释放锁:

    • 使用DEL key命令删除锁的键。
    • 释放锁时,确保只有持有锁的实例能够释放它,以避免误删其他实例的锁。

请注意,这只是一个简单的示例,实际上在实现分布式锁时还需要考虑更多的细节,如锁超时时间、宕机处理、锁重入等。更复杂的实现可以使用Redlock或基于Redis的分布式锁库,它们提供了更强大和可靠的分布式锁功能。

2.1.5 使用字符串实现简单时间序列

APPEND 命令可用于创建固定大小样本列表的非常紧凑的表示,通常称为时间序列。每次有新样本到达时,我们都可以使用命令存储它:

APPEND timeseries "fixed-size sample"

访问时间序列中的各个元素并不难:

  • 可以使用 STRLEN 来获取样本数。
  • GETRANGE 允许随机访问元素。如果我们的时间序列具有关联的时间信息,我们可以轻松地实现二分搜索。
  • SETRANGE 可用于覆盖现有时间序列。

这种模式的局限性是我们被迫进入仅追加操作模式,无法轻松地将时间序列切割到给定大小,因为Redis目前缺乏能够修剪字符串对象的命令。然而以这种方式存储的时间序列的空间效率是显著的。

可以根据当前 Unix 时间切换到不同的键,通过这种方式,每个键可能只有相对少量的样本,避免处理非常大的键,并使这种模式更适合分布在许多 Redis 实例上。

2.1.6 使用字符串实现简单的速率限制器

使用Redis的计数器实现固定窗口速率限制器:

  1. 创建一个Redis键来表示计数器,例如rate_limiter:api_endpoint.

  2. 在每个请求到达时,使用INCR命令递增计数器的值,并获取递增后的值。

  3. 检查递增后的值是否超过了限制阈值。如果超过了阈值,则拒绝这个请求。

  4. 使用TTL(生存时间)设置键的过期时间,使其在一个固定的时间窗口后自动过期。例如,如果时间窗口是1秒,则可以设置TTL为1秒。

示例代码(使用Python和Redis的redis-py库):

import redis

def rate_limiter(api_endpoint, limit, window):
    redis_client = redis.Redis()
    key = f"rate_limiter:{api_endpoint}"
    count = redis_client.incr(key)
    if count > limit:
        return "Rate limit exceeded"
    redis_client.expire(key, window)
    return "Success"

2.1.7 提示及注意事项

  • 按位运算操作

    对字符串执行按位运算,请查看Bitmaps位图章节。

  • 字符串限制

    默认情况下,单个 Redis 字符串最大可为 512 MB。

  • 性能

    大多数字符串操作都是 O(1),这意味着它有很高的性能。然而也需要注意SUBSTR、GETRANGE 和 SETRANGE 命令,时间复杂度为 O(n),在处理大字符串时,这些随机访问字符串命令可能会导致性能问题。

  • 序列化结构数据存储

    如果您将结构化数据存储为序列化字符串,您还可以考虑使用 Redis 哈希或 JSON。

2.2 Hash

2.2.1 概述及使用场景

映射是键值对集合的数据类型。

类似于golang里的map[string]string

Redis 中的 Hash(哈希)数据结构是一个 key-value 的无序散列表。它适合存储和操作一些具有结构化数据的对象。以下是一些 Redis Hash 的常见应用场景:

  • 对象存储
    Redis Hash 可以用于存储和管理对象的属性。每个对象可以用一个 Hash 来表示,其中的字段可以表示对象的属性,字段的值则是属性的值。这样便于对对象进行读取、更新和查询。
  • 缓存存储
    Redis Hash 可以用于缓存数据。你可以将一些常用的数据结构化并存储为 Hash 对象,每个字段表示一个缓存项的键,对应的值则是缓存项的内容。这样可以提高读取和更新缓存的效率。
  • 用户属性存储
    对于需要存储用户属性的应用程序,可以使用 Redis Hash 来存储用户的属性信息。每个用户可以用一个 Hash 对象表示,字段表示属性名称,字段的值则是对应的属性值。这样可以方便地查询和更新用户属性。
  • 计数器
    Redis Hash 可以用于实现计数器功能。你可以使用 Hash 对象来存储某个实体的计数值,每个字段表示一个计数器的键,对应的值则是计数器的值。通过对计数器进行递增或递减操作,可以方便地实现计数功能。
  • 地理位置信息存储
    如果需要存储和查询地理位置信息,可以使用 Redis Hash。你可以将每个位置表示为一个 Hash 对象,其中的字段表示经度和纬度等属性,字段的值则是对应的数值。这样可以方便地存储和查询位置信息。

2.2.2 命令

Hash数据类型全量命令

2.2.3 提示和注意事项

  • 性能
    大多数 Redis 哈希命令的复杂度都是 O(1)。 一些命令(例如 HKEYS、HVALS 和 HGETALL)的复杂度为 O(n),其中 n 是字段值对的数量。

  • 限制
    每个哈希最多可以存储 4,294,967,295 (2^32 - 1) 个字段值对。实际上,您的哈希值仅受托管 Redis 部署的虚拟机上的总内存的限制。

2.3 List

2.3.1 概述

Redis Lists 是字符串值的链表结构,常用于:

  • 实现堆栈和队列
  • 为后台工作系统构建队列管理

Redis 列表采用链表实现,因为对于数据库系统来说,能够以非常快的方式将元素添加到非常长的列表中至关重要。另一个强大的优势是Redis 列表可以在恒定时间内以恒定长度获取。

当快速访问大量元素的中间部分很重要时,可以使用另一种数据结构——有序集。

2.3.2 命令

列表数据类型全量命令

2.3.2 使用列表实现简单消息队列

List 可以用作简单的消息队列,其中生产者将消息推送到列表的一端,而消费者从另一端消费消息。这样可以实现基于发布/订阅模型的简单消息传递。

  1. 生产者使用RPUSH命令将消息推送到列表的末尾
  2. 消费者使用LPOPBLPOP命令从消息队列的头部获取消息

2.3.3 使用列表实现最新消息/动态

可以使用 List 存储最新的消息、动态或帖子。每当有新的消息到达时,可以将其插入到列表的开头,同时限制列表的长度,以保持只有最新的消息在其中。

  1. 发布消息时,将消息使用LPUSH命令推送到列表,同时使用LTRIM命令控制列表的长度。
  2. 获取最新消息/动态时,使用LRANGE命令获取最新的消息/动态。

2.3.4 使用列表实现订阅者列表

在发布/订阅模型中,可以使用List存储订阅者的信息。每当有新的订阅者加入或离开时,可以通过插入或删除操作来维护订阅者列表。

  1. 使用 Redis 列表来存储订阅者的信息,每个订阅者作为列表的一个元素。
  2. 当有新订阅者加入时,使用 RPUSH 命令将其添加到列表的末尾。
  3. 当订阅者取消订阅时,可以使用LREM命令将其从列表中移除。
  4. 使用 LRANGE 命令可以获取整个列表或指定索引范围内的订阅者。

2.3.5 使用列表实现分页查询

List 支持从两端进行快速的插入和删除操作,这使得它非常适合用于实现分页查询功能。可以将查询的结果存储在一个 List 中,并根据需要从列表的两端获取结果。

  1. 使用 Redis 列表来存储要分页查询的数据,每个数据作为列表的一个元素。
  2. 使用 RPUSH 命令将新的数据添加到列表中。
  3. 使用 LRANGE 命令根据指定的索引范围获取指定页码的数据。(根据页码和每页数量,计算要获取数据的起始索引和结束索引。)

2.3.6 提示及注意事项

  • 列表限制

    Redis Lists最大元素个数为2^32 - 1 (4,294,967,295) 。

  • 性能

    访问其头部或尾部的列表操作的复杂度为 O(1),这意味着它们非常高效。然而,操作列表中元素的命令通常是 O(n)。这些示例包括 LINDEX、LINSERT 和 LSET。运行这些命令时请务必小心,尤其是在大型列表上操作时。

  • 备选方案

    当您需要存储和处理一系列不确定的事件时,请考虑将 Redis 流作为列表的替代方案。

2.4 Set

2.4.1 概述及使用场景

Redis Set集合是唯一字符串的无序集合。

类似于java里的HashSets

Redis 中的 Set(集合)数据结构是一个无序、唯一值的集合。它提供了一些操作来处理集合,包括添加元素、删除元素、检查成员存在性、集合间的交集、并集和差集等。以下是一些 Redis Set 的常见应用场景:

  • 标签系统
    可以使用 Set 存储和管理实体的标签。每个实体都有一个唯一的标识符,而这些标识符可以添加到相应实体的标签集合中。你可以使用 Set 提供的操作来查找共享标签、查找包含特定标签的实体等。
  • 用户关注列表
    对于社交媒体或关注系统,可以使用 Set 存储用户的关注列表。每个用户的关注列表是一个集合,其中包含他们所关注的其他用户的标识符。你可以使用 Set 提供的操作来查找共同的关注者、查找某个用户的关注者等。
  • 唯一值的存储
    如果你需要存储唯一的值,并且对于每个值的快速插入、查找和删除操作很重要,那么使用 Redis Set 是一个不错的选择。Set 保证值的唯一性,并提供了高效的集合操作。
  • 集合运算
    Redis Set 还提供了集合间的交集、并集和差集等操作。这些操作可以用于计算共同的元素、合并集合中的元素等。比如,在电子商务中,可以使用交集操作来查找同时满足多个筛选条件的商品。
  • 标记已处理的数据
    当需要对一些数据进行处理,并且需要跟踪已处理和未处理的数据时,可以使用 Set 来存储已处理的数据。每次处理完数据后,将其标识符添加到 Set 中,这样就可以轻松地检查数据是否已经处理过。

2.4.2 命令

集合数据类型全量命令

2.4.3 提示和注意事项

  • 限制:

    Redis 集的最大大小为 2^32 - 1 (4,294,967,295) 个成员。

  • 性能:

    大多数集合操作(包括添加、删除以及检查某项是否是集合成员)的复杂度都是 O(1)。这意味着他们的效率很高。但是,对于具有数十万或更多成员的大型集,在运行 SMEMBERS 命令时应小心谨慎。该命令的复杂度为 O(n),并在单个响应中返回整个集合。作为替代方案,请考虑 SSCAN,它允许您迭代地检索集合的所有成员。

  • 替代方案

    对大型数据集(或流数据)设置成员资格检查可能会使用大量内存。如果您担心内存使用情况并且不需要完美的精度,请考虑使用 Bloom 过滤器或 Cuckoo 过滤器作为集合的替代方案。
    Redis 集经常用作一种索引。如果您需要对数据进行索引和查询,请考虑 JSON 数据类型以及搜索和查询功能。

2.5 Sorted-set

2.5.1 概述及使用场景

有序集合是一个按照给定分数(score)排序的不重复字符串的集合。如果有多个字符串的分数相同,就按照字典序排序。

有序集合的典型应用场景:

  • 排行榜
    您可以使用排序集轻松维护大型在线游戏中最高分数的有序列表。
  • 速率限制器
    您可以使用排序集构建滑动窗口速率限制器,以防止过多的 API 请求。

2.5.2 命令

有序集合数据类型全量命令

2.5.3 提示和注意事项

  • 性能
    大多数排序集操作的复杂度为 O(log(n)),其中 n 是成员数。
    运行具有较大返回值(例如,数万或更多)的 ZRANGE 命令时请务必小心。该命令的时间复杂度为 O(log(n) + m),其中 m 是返回结果的数量。
  • 备选方案
    Redis 排序集有时用于索引其他 Redis 数据结构。如果您需要对数据进行索引和查询,请考虑 JSON 数据类型以及搜索和查询功能。
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值