当前位置:   article > 正文

【Redis】强大的内存数据库

【Redis】强大的内存数据库

Redis是一个高性能、开源、内存键值存储数据库,同时也支持丰富的数据结构。它由 Salvatore Sanfilippo(又名 antirez)创建并在2009年开始公开发布。Redis以其优异的性能、灵活的数据模型和广泛的适用性而著称,已经成为现代应用程序开发中不可或缺的组件之一。以下是Redis的详细介绍:

基础特性

内存存储

  • Redis将所有数据存储在内存中,使得读写速度非常快,适合用于缓存和实时性要求高的场景。

数据持久化

Redis 数据持久化是指将内存中的数据保存到硬盘中,以防止服务重启或系统崩溃时数据丢失。Redis 提供了两种主要的数据持久化机制:

快照(Snapshotting) - RDB(Redis Database)

工作原理:
  • RDB 是 Redis 默认的持久化方式,它通过定期在指定的条件(如达到一定数量的键修改次数或经过一定时间间隔)下,将 Redis 数据集生成一个或多个快照文件(即.rdb文件)并将其存储在硬盘上。
  • 当 Redis 需要保存快照时,会 fork 出一个子进程,这个子进程负责将数据集写入硬盘,而父进程继续处理客户端请求。这样可以保证即使在大量数据写入时也不会阻塞主线程的正常操作。
  • 重启 Redis 时,可以从.rdb文件中载入数据恢复至内存,完成数据恢复。
优点:
  • 数据恢复速度快,RDB 文件紧凑且易于传输。
  • 占用较少的磁盘空间,因为 RDB 文件是经过压缩的二进制格式。
  • 可以选择不同的保存策略,以平衡数据安全性与性能消耗。
缺点:
  • 如果在两次快照之间发生故障,会导致最近一次快照以来的所有数据丢失。
  • 生成 RDB 文件是一个比较耗时的操作,尤其是在数据集较大的情况下,可能会导致短暂的延迟。

增量持久化 - AOF(Append Only File)

工作原理:
  • AOF 模式下,Redis 会将每一个写操作命令以文本形式追加到一个日志文件(即.aof文件)中。
  • Redis 重启时,会重新执行日志文件中的所有命令来重建数据集。
  • AOF 还支持重写机制,即在日志文件过大时,Redis 可以通过后台进程来压缩日志,移除无效命令和合并连续操作,从而减小文件大小。
优点:
  • 数据安全性更高,因为每个写操作都被记录下来,丢失的数据更少。
  • 可以配置不同的同步策略,例如每秒同步、每次写入同步等,灵活性较高。
缺点:
  • 随着写操作的增加,AOF 文件会越来越大,恢复数据所需时间相对于 RDB 更长。
  • 需要定期进行文件重写,虽然Redis自动处理,但增加了系统的复杂性。

混合持久化(Hybrid Persistence)

从 Redis 4.0 版本开始,还支持将 RDB 和 AOF 结合使用。在这种模式下,重启 Redis 时首先加载 RDB 文件快速恢复大部分数据,然后重放 AOF 文件中记录的增量命令,从而既提高了恢复速度,又降低了数据丢失的风险。

数据结构丰富

  1. 字符串(String):这是最基础的数据结构,用于存储简单的字符串或json、xml格式的字符串。其最大能存储512MB的数据,且字符串是动态的,可以修改。Redis的分布式锁、计数器、Session共享、分布式ID等功能都基于字符串实现。
  2. 哈希表(Hash):用于存储一些key-value键值对,更适合用来存储对象。
  3. 列表(List):Redis的列表通过命令的组合,既可以当作栈使用,又可以当作队列使用,可以用来缓存类似微信公众号、微博等消息流数据。
  4. 集合(Set):集合类似于列表,可以存储多个元素,但是集合内的元素不能重复。此外,集合可以进行交集、并集和差集操作,从而实现一些特殊功能,如共同关注的人、朋友圈点赞等。
  5. 有序集合(Sorted Set):有序集合与集合类似,但有序集合中的元素可以设置顺序,因此它可以用来实现排行榜功能。

这些数据结构使得Redis能够灵活应对各种数据存储和处理的需求,无论是简单的数据缓存,还是复杂的消息队列和分布式系统,Redis都能提供高效、可靠的支持。

单线程模型

Redis的线程模型主要是基于Reactor模式开发的网络事件处理器,这个处理器被称为文件事件处理器(file event handler)。它是单线程的,所以Redis也被称为单线程模型。虽然从严格意义上讲,Redis的其他模块也会用到多个线程,但在网络请求模块,Redis确实使用了单个线程来处理所有的网络请求。

文件事件处理器使用IO多路复用(multiplexing)程序来同时监听多个套接字(socket),并根据套接字当前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。

这种模型使得Redis能够高效地处理大量的网络请求,避免了线程切换和竞态产生的消耗,也避免了不必要的上下文切换和竞争条件,从而实现了高性能的网络通信。同时,由于Redis内部线程模型的简单性,它也更容易进行扩展和维护。

需要注意的是,虽然Redis的网络请求处理是单线程的,但在执行命令时,Redis仍然可以利用多核CPU资源,因为Redis会将命令的执行计划拆分给多个工作线程去执行。

此外,由于多个客户端发送的命令的执行顺序是不确定的,因此在使用Redis时,需要注意避免一些可能导致数据不一致的操作。

网络模型

Redis的网络模型主要基于IO多路复用和事件派发机制。这种模型使得Redis能够高效地处理大量的网络请求,实现高性能的网络通信。

在Redis的网络模型中,服务端会创建一个ServerSocket,并将其文件描述符(fd)注册到事件循环实例上。同时,会为这个事件绑定一个连接应答处理器,用于处理客户端的连接请求。当客户端连接上来时,会产生一个事件,IO多路复用程序监听到ServerSocket上有事件就绪,就会调用连接应答处理器,和客户端建立连接。

建立连接后,Redis会为客户端创建对应的socket,获取socket的读事件,并为其绑定命令请求处理器。这样,当客户端向主服务器发送命令请求时,Redis就能够通过读事件和命令请求处理器来接收并处理这些请求。

整个过程中,Redis使用单线程模型来处理网络请求,避免了线程切换和竞态产生的消耗。同时,由于Redis内部线程模型的简单性,它也更容易进行扩展和维护。

总的来说,Redis的网络模型是一个高效、可靠且易于扩展的模型,能够支持大量的并发连接和请求处理,使得Redis在各种应用场景中都能表现出色。

高级特性

  • 事务
  • Redis 事务允许用户以一种“批处理”的方式执行一系列命令,其特点包括一次性、顺序性和排他性执行命令队列。以下是 Redis 事务的关键特征和操作流程:
  1. 命令队列

    • 使用 MULTI 命令开启一个事务,此后客户端发送的所有命令都不会立即执行,而是被放入一个队列中待处理。
  2. 原子性

    • Redis 事务并没有像传统数据库那样具有严格的ACID属性,特别是不支持事务回滚。一旦事务中的所有命令开始执行(通过 EXEC 命令触发),它们会按照先进先出(FIFO)顺序执行完毕,即使其中某个命令执行失败,后续命令仍然会尝试执行,这就是所谓的“部分原子性”。
  3. 监控与检查

    • 使用 WATCH 命令可以监控一个或多个 key,当 EXEC 执行前,如果被监控的 key 被其他客户端修改,那么整个事务将会被中止,EXEC 返回 null 表示事务未执行。这是 Redis 实现的一种乐观锁机制,用于在执行事务前检测 key 是否被并发修改。
  4. 事务控制

    • DISCARD 命令可以取消当前事务,清除事务队列,放弃执行事务中的所有命令。
    • UNWATCH 命令取消之前使用 WATCH 设置的所有 key 监控。
  5. 执行

    • 通过 EXEC 命令提交并执行事务队列中的所有命令。如果在 EXEC 之前执行了 DISCARD 或者被 WATCH 的 key 发生改变,EXEC 不会执行任何命令。

由于 Redis 是单线程模型,事务中的命令在执行时彼此间不会互相影响,但并不保证事务内部命令间的原子性,即不能保证事务中的多个命令作为一个整体被原子地执行或回滚。此外,Redis 的事务不存在隔离级别,同一时间内只有一个事务在执行,但不同客户端之间的事务执行是无序的。这意味着在一个事务执行过程中,其他客户端的命令请求只能等待事务结束后才能得到服务。

  • 复制
    Redis 复制(Replication)是一种在多个 Redis 实例之间同步数据的技术,旨在提供数据冗余、读写分离、灾难恢复等能力。Redis 的复制分为主从复制(Master-Slave Replication)和通过哨兵(Sentinel)实现的自动故障转移。

  • 主从复制(Master-Slave Replication)
  1. 主节点(Master)
    • 主节点负责接收客户端的所有写请求,并将数据变化同步给从节点。
  • 它会维护一个复制积压缓冲区(replication backlog),用于在复制过程中临时存储部分数据。
  1. 从节点(Slave)

    • 从节点通过向主节点发送 SYNCPSYNC 命令来启动复制过程。
    • 全量复制(Full Resynchronization):首次连接或复制偏移量相差太大时,主节点会生成一个 RDB 快照文件并发送给从节点,接着将这段时间内的增量命令发送给从节点,从节点加载 RDB 文件并执行这些增量命令来完成同步。
    • 部分复制(Partial Resynchronization):如果从节点与主节点断开连接后短时间内重新连接,主节点可以通过复制积压缓冲区仅发送从节点缺失的部分数据,从而大大减少了同步数据的量。
  2. 复制优势

    • 读写分离:主节点处理写操作,从节点处理只读请求,减轻主节点的压力。
    • 容灾与高可用:主节点出现问题时,可以从从节点中选举新的主节点继续服务。
    • 扩展读能力:增加从节点可以提升系统的总体读取性能。
  • 哨兵(Sentinel)
  • Redis Sentinel 是一个分布式系统,用于监控 Redis 主从集群,当主节点出现故障时,Sentinel 系统能够自动完成故障发现和故障转移,并通知客户端新的主节点地址,从而实现了自动化的高可用性。

    通过哨兵模式,Redis 的复制得以升级,能够更好地应对主节点故障的情况,确保服务的持续可用性。同时,Sentinel 还提供了监控、通知以及主从角色切换等功能。

分布式

Redis 分布式通常指的是 Redis 集群(Redis Cluster),这是一种原生的 Redis 分布式解决方案,允许在多个节点上划分数据并共同提供服务,以达到水平扩展的目的,从而提高系统整体的读写能力和容错性。

Redis 集群的主要特点包括:

  1. 数据分片(Sharding):

    • Redis Cluster 使用哈希槽(hash slot)的方式将数据分布在多个节点上,共有16384个槽位。
    • 每个键都会通过 CRC16 算法计算得出一个槽位号,并映射到特定的节点上。
  2. 节点通信与数据迁移:

    • 集群内部节点间通过 gossip 协议交换状态信息,维持整个集群的配置。
    • 当需要添加或删除节点时,集群能够自动重平衡数据,迁移槽位所关联的数据到其他节点。
  3. 客户端透明路由:

    • 客户端连接任意节点都能访问整个集群的数据,节点会根据键所属的槽位,将请求转发到正确的节点。
    • 客户端也可以直接与目标节点通信,例如使用集群模式下的 Redis 客户端库。
  4. 高可用性:

    • 每个槽位可以有一个主节点和多个从节点,主节点失效时,集群能自动进行故障转移,其中一个从节点会被提升为主节点。
    • Redis Cluster 支持多个主从对,通过主从复制实现数据冗余和故障恢复。
  5. 写操作的限制:

    • Redis Cluster 不支持跨多个键的事务操作和原子操作(如 MSETNX)。
    • 对于涉及多个键的操作,所有键必须位于同一槽位才能执行。

通过 Redis 集群,开发者可以构建大规模、高可用的分布式缓存和存储系统,适用于大数据量、高并发的场景,同时保持Redis本身高性能的特性。

  • Lua脚本
    Redis Lua 脚本是 Redis 提供的一种强大功能,它允许用户在 Redis 服务器端直接执行 Lua 代码,从而实现对 Redis 数据进行更复杂、原子性的操作。Lua 脚本的主要优势在于:
  1. 原子性
    Redis 执行 Lua 脚本时会保证整个脚本的执行是原子性的,也就是说,在执行脚本期间,不会有其他客户端的命令插入执行,从而避免了因并发操作可能导致的数据不一致问题。

  2. 性能
    由于 Lua 脚本在 Redis 服务器端执行,减少了客户端与服务器之间的网络往返延迟,提高了整体性能。同时,Lua 解释器本身小巧且高效,特别适合于嵌入式脚本环境。

  3. 复用性
    Lua 脚本可以被 Redis 缓存并重复使用,从而节省网络带宽和计算资源。通过 SCRIPT LOAD 命令可以将脚本加载到 Redis 中,之后使用 EVALSHA 命令通过脚本的 SHA1 校验和来执行缓存的脚本。

  4. 功能扩展
    Lua 脚本能处理复杂逻辑,例如条件判断、循环、数据处理等,还可以调用 Redis 命令。Redis 提供了 redis.call()redis.pcall() 两个函数,分别用于执行Redis命令并返回结果(前者在遇到错误时会停止脚本执行,后者则捕获错误并返回错误信息)。

  5. 使用实例

    • 示例脚本:一个简单的 Lua 脚本可能用于原子性地递增某个 key 的值,同时检查是否超过了预设的阈值。
    • 分布式锁:Lua 脚本常用于实现分布式锁,确保在多客户端环境下对资源的独占访问。
  6. 命令相关

    • EVALEVALSHA 命令用于执行 Lua 脚本。

    • SCRIPT LOAD 命令用于加载脚本而不执行。

    • SCRIPT EXISTS 检查脚本是否已经被加载过。

    • SCRIPT FLUSH 清除所有已加载的脚本。

    • SCRIPT KILL 终止当前正在执行的长时间运行的脚本。

      Lua 脚本在 Redis 中的应用极大地丰富了 Redis 的功能,特别是在需要执行多条命令组合且需要保持原子性的情景下,是非常理想的解决方案。

LRU回收策略

Redis 的 LRU(Least Recently Used,最近最少使用)回收策略是一种内存管理策略,用于在内存不足以容纳所有数据时,自动选择并删除最近最少使用的数据,以便腾出空间存储新数据。然而,Redis 实现的 LRU 并非完全意义上的 LRU 算法,因为它并未真正记录所有键的使用时间顺序,而是采用了近似 LRU 的方法。

在 Redis 早期版本中,实现了一种随机采样的 LRU 替代算法。Redis 会在每个键上维护一个额外的字段(称为 LFU 利用次数或 LRU 时间标记),并且使用一个循环双向链表来按 LRU 近似原则组织数据。当需要释放内存时,Redis 会选择链表尾部的键进行淘汰,假设这部分键是最久未使用的。

随着 Redis 的发展,尤其是从 Redis 4.0 开始,提供了两种更加精细化的 LRU 算法实现:

  1. volatile-lru:
    仅针对带有过期时间(TTL)的键采用 LRU 策略,也就是说,当内存达到最大限制时,优先从即将过期的键中淘汰最近最少使用的。

  2. allkeys-lru:
    对所有键(无论是否设置过期时间)都采用 LRU 策略,当内存满时,同样会基于最近最少使用的标准淘汰数据。

这两种策略都是通过采样和概率估算的方式来近似实现 LRU,而非真正的 O(1) 查找时间复杂度的 LRU 算法。Redis 通过维护一个小样本集合,跟踪最近一段时间内哪些键被访问过,以此来决定哪些键可能是最近最少使用的。

另外,Redis 还引入了 LFU(Least Frequently Used,最近最不常用)策略,它是基于访问频率而不是访问时间来决定淘汰数据的优先级。volatile-lfu 和 allkeys-lfu 类似于 volatile-lru 和 allkeys-lru,区别在于它们是基于访问频率来淘汰数据。

应用场景

  • 缓存:作为高速缓存层,减轻数据库负载。
  • 计算与统计:因其丰富的数据结构和原子操作,常用于实时排行榜、计数器、分布式锁等场景。
  • 消息队列:利用列表结构实现轻量级的消息队列服务。
  • 社交网络:如赞、踩、粉丝、好友关系等实时数据的存储与检索。

总之,Redis凭借其高性能、易用性以及强大的数据处理能力,成为了一个高度可扩展的解决方案,适用于多种互联网业务场景。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/499758
推荐阅读
相关标签
  

闽ICP备14008679号