[学习笔记]Redis概述

一、为什么要使用 Redis(3 点)
在我们开始设计"易购网"的技术架构时,随着用户量和数据量的增长,我们发现只依赖关系型数据库(如MySQL)会遇到几个明显的瓶颈。为了解决这些瓶颈,我们引入了Redis。主要基于以下三个核心原因:
1.高性能,作为缓存,降低数据库压力
专业性描述:Redis将所有数据存储在内存中,读写速度极快,通常能达到微秒级。将其部署在数据库(如MySQL)之前,作为缓存层,可以拦截大部分高频、简单的数据访问请求。只有当缓存中没有所需数据时,才会去查询较慢的数据库(磁盘存储,毫秒级)。这极大地降低了数据库的负载,并提升了整体系统的响应速度。
2.高并发,应对突发流量峰值
专业性描述:数据库的并发处理能力是有上限的。在面对秒杀、抢购、热点新闻等瞬时超高并发场景时,大量的写请求(如扣减库存)会直接打垮数据库。Redis极高的读写性能(特别是单线程内存操作模型,后续会详述)使其能够轻松应对这类瞬时高并发请求,作为一个"缓冲阀"或"计数器",保护底层数据库。
3.丰富的数据结构,简化业务开发
专业性描述:Redis不仅仅是简单的"键-值"存储,它提供了字符串、哈希、列表、集合、有序集合等多种数据结构。每种数据结构都内置了丰富的原子操作命令。这允许开发者在Redis内部直接完成许多复杂的业务逻辑计算,而无需将数据取回应用层处理,既提升了效率,也保证了数据一致性。
二、什么是 Redis
专业性描述:Redis(Remote Dictionary Server,远程字典服务)是一个开源的、基于内存的键值对存储系统。它常被用作数据库、缓存和消息中间件。与传统的磁盘存储数据库(如MySQL)不同,Redis将主要数据存储在内存(RAM)中,因此读写速度极快。它支持多种数据结构(如字符串、哈希、列表等),并提供了丰富的操作这些数据结构的命令。同时,Redis也提供了持久化到磁盘的机制,以确保数据的安全。
Redis 与 Memcached 的核心区别对比表

|-----------|---------------------------------------------------------------------------|------------------------------------------------|
| 比较维度 | Redis | Memcached |
| 核心定位 | 多功能的内存数据存储 ,可作为缓存、数据库、消息中间件。 | 纯粹的高性能键值缓存 ,设计目标单一。 |
| 数据结构 | 丰富 。支持 String, Hash, List, Set, Sorted Set, Bitmap 等。允许在值中存储结构化数据。 | 单一 。只支持简单的 String 类型的键和值。值只能是字符串或二进制数据。 |
| 数据持久化 | 支持 。提供 RDB 快照和 AOF 日志两种方式,可将内存数据保存到磁盘,重启后可恢复,降低数据丢失风险。 | 不支持 。数据仅存在内存中,重启服务或进程崩溃,数据全部丢失。 |
| 网络模型 | 早期版本为单线程 Reactor 模型(6.0后引入多线程处理网络IO,核心命令执行仍单线程)。 | 多线程 。利用多核并行处理请求,在多核场景下,吞吐量可能更有优势。 |
| 集群模式 | 原生支持 Redis Cluster 分布式模式,具备数据分片和高可用能力。 | 无原生集群支持 。依赖客户端一致性哈希或在代理层(如 Twemproxy)实现分片。 |
| 内存管理 | 提供更精细的内存控制,支持内存淘汰策略和不同数据结构的编码优化。 | 使用简单的 Slab Allocation 内存预分配模型,可能产生一定的内存碎片。 |
| 适用场景 | 复杂业务场景 。如:会话缓存、排行榜(Sorted Set)、消息队列(List/Stream)、购物车(Hash)、地理围栏(GEO)。 | 简单键值缓存场景 。如:缓存 HTML 片段、数据库查询结果等无需结构化的纯缓存。 |

三、Redis 为什么快
Redis 速度极快,是它最核心的特征。在"易购网"的架构中,它能承担缓存和高速计算的职责,根本原因在于其精妙的设计。主要有以下四个原因:
1.基于内存存储
专业性描述:Redis 的所有数据主要存储在计算机的内存(RAM)中。内存的读写速度(纳秒级,约 100 ns)比磁盘(机械硬盘毫秒级,约 10 ms;固态硬盘微秒级,约 100 μs)高出数个数量级。这避免了磁盘 I/O 这一传统数据库最大的性能瓶颈。
2.高效的数据结构与底层优化
专业性描述:Redis 为其支持的数据结构(如 String, Hash, List)设计了高效、紧凑的内存存储结构,并做了大量底层编码优化(如 zipList, intSet, skiplist 等)。同时,它提供了一组原子性的原生命令来操作这些结构,许多复杂操作在服务器端一步完成。
3.单线程的事件循环模型(处理核心操作)
专业性描述:Redis 在处理客户端的命令(数据操作)时,核心部分(命令解析、执行、返回结果)是单线程的。这带来了巨大优势:完全避免了多线程上下文切换和竞争条件(锁)带来的性能消耗和复杂度。所有的命令都是顺序、原子地执行,不会产生并发问题。
4.I/O 多路复用
专业性描述:虽然核心命令处理是单线程,但 Redis 使用了 I/O 多路复用技术(如 Linux 的 epoll)来处理大量的网络连接。这使得一个线程可以同时监视和管理成千上万个客户端的套接字连接。当任何一个连接有数据可读或可写时,Redis 能快速感知并进行处理,最大限度地利用网络带宽和CPU时间,避免在单个空闲连接上阻塞。
四、Redis 是单线程的吗
这是一个非常重要且容易被误解的问题。答案是:Redis 在处理客户端命令的核心部分,仍然是单线程的;但在处理网络 I/O 等辅助任务时,从 6.0 版本开始引入了多线程。
1.Redis 6.0 之前:经典的单线程模型
专业性描述:在此版本之前,Redis 从接收客户端命令、解析命令、执行命令,到将结果返回给客户端的整个核心流程,都是由一个主线程(主处理线程)串行完成的。同时,这个主线程也负责通过 I/O 多路复用技术(如 epoll)来监听和管理所有网络连接。这是一个彻底的、经典的单线程 Reactor 模型。
2.Redis 6.0 及之后版本:支持网络 I/O 多线程(核心命令执行仍为单线程)
专业性描述:随着网络带宽的不断提升,特别是在高并发场景下,处理大量网络数据包的读写和解析(即网络 I/O)本身可能成为一个性能瓶颈。为了更充分地利用多核 CPU 资源以提升吞吐量,Redis 6.0 引入了可选的"多线程网络 I/O"特性。
五、Redis 的数据类型

|-----------------|------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 数据类型 | 核心特点(结构) | 典型适用场景 | "易购网"项目示例 |
| String | 最简单的键值对。值是字符串、数字或二进制数据。一个键最大能存储512MB。它是其他所有数据结构的基础。 | 1. 缓存 :存储序列化的对象或简单值。 2. 计数器 :利用INCR/DECR命令实现原子增减。 3. 分布式锁 :利用SETNX命令实现。 | 1. 缓存商品详情 :SET product:1001 '{"name":"手机", "price":3999}' 2. 商品访问量计数 : INCR page_view:product:1001 |
| Hash | 键值对集合,适合存储对象。一个Hash可以存储多达2^32-1个字段-值对。 | 1. 存储对象信息 :将对象的多个字段聚合成一个键存储,方便部分读写。 2. 购物车 :以用户ID为键,商品ID为字段,数量/规格为值。 | 用户购物车 :HMSET cart:user123 product:456 2 product:789 1。可以单独修改某个商品数量:HINCRBY cart:user123 product:456 1 |
| List | 一个有序、按插入顺序排序的字符串列表。可以在头部(Left)或尾部(Right)进行插入和弹出,类似双向队列。 | 1. 消息队列 :利用LPUSH(生产)和BRPOP(阻塞消费)实现简易队列。 2. 时间线/活动流 :如最新文章、用户动态列表。 3. 记录前N个操作 。 | 最新上架商品ID列表 :每次上新,LPUSH new_arrivals 1001。用户访问新品页时,用LRANGE new_arrivals 0 9获取最新的10个商品ID。 |
| Set | 无序的字符串集合。元素唯一,不允许重复。支持交集、并集、差集等集合运算。 | 1. 去重存储 :如点赞用户的ID、抽奖参与人。 2. 共同关注/好友推荐 :利用集合运算。 3. 随机元素 :如抽奖。 | 用户点赞集合 :用户A点赞了文章1001:SADD like:article:1001 user:A。检查用户A是否点过赞:SISMEMBER like:article:1001 user:A。 |
| Sorted Set | 有序集合。每个元素关联一个分数(Score),集合根据分数从小到大排序。元素唯一,但分数可以重复。 | 1. 排行榜 :如商品销量榜、用户积分榜。 2. 带权重的队列 。 3. 范围查询 :如查询积分在1000-2000的用户。 | 商品销量排行榜 :每卖出一件商品1001,执行ZINCRBY hot_products 1 1001。获取Top 10:ZREVRANGE hot_products 0 9 WITHSCORES。 |
| Bitmap | 本质上是String,但将其视为一个由0和1组成的位数组(bit array),并提供直接操作位的命令。极其节省空间。 | 1. 二值状态统计 :如用户签到(1签/0未签)、活跃用户标记。 2. 大数据量的是/否判断 。 | 用户月度签到 :用户ID=123在1月25日签到:SETBIT sign:202501:123 24 1(将第24位设为1)。统计该月签到天数:BITCOUNT sign:202501:123。 |
| GEO | 地理位置信息。底层基于Sorted Set实现,将经纬度编码为分数存储,提供地理位置计算命令。 | 1. 附近的人/商家 。 2. 计算两点距离 。 3. 地理位置范围查询 。 | 查找附近的配送站 :将配送站坐标加入集合:GEOADD delivery_stations 116.40 39.90 station:A。查找用户位置(116.41, 39.91)5公里内的站点:GEORADIUS delivery_stations 116.41 39.91 5 km WITHDIST。 |
| HyperLogLog | 用于基数统计 (估算集合中不重复元素的数量)的数据结构。特点是占用内存极小(固定约12KB),但存在误差(约0.81%) 。 | 海量数据去重计数 :如网站的日活跃用户数(UV)、大型活动的独立访客数。不要求100%精确,追求极高空间效率的场景。 | 统计"易购网"首页日活跃用户数(UV) :每个用户访问首页时,执行PFADD uv:2025-01-25 user:123。当日结束时,执行PFCOUNT uv:2025-01-25即可获得估算的UV值,无论当天有多少亿次访问,此结构大小不变。 |
| Stream | Redis 5.0引入的持久化的消息队列 数据结构。支持多消费者组、消息回溯、阻塞等待新消息等高级特性,是更可靠的消息队列解决方案。 | 异步消息通信 :如订单创建后发送通知事件、用户行为日志收集,用于解耦系统组件。 | 用户下单后的异步处理 :1. 订单服务创建订单后,向Stream order:events 发布一条消息: XADD order:events * order_id 10001 user_id 123 status created 。 2. 库存服务、物流服务、营销服务分别作为独立的消费者组,同时订阅该Stream,各自独立地处理这条订单消息。 |

六、Redis 的持久化机制
因为Redis的数据主要存储在内存中,一旦服务器重启或宕机,内存中的数据就会丢失。为了解决这个问题,Redis提供了持久化机制,将内存中的数据以某种形式保存到磁盘上,以便在需要时重新加载。
在"易购网"的架构中,我们根据数据的不同重要程度,决定哪些数据需要持久化以及采用何种方式。例如,用户的购物车数据(Hash)可以允许短暂丢失,而重要的商品库存快照(String)我们则希望尽可能保证不丢失。Redis提供了三种主要的持久化方案:

  1. RDB (Redis Database)
    工作原理:在指定的时间点,将内存中整个数据集的快照(Snapshot)保存到一个二进制的RDB文件(通常是dump.rdb)中。
    触发方式:
    手动触发:通过SAVE(同步,会阻塞主进程)或BGSAVE(后台异步,常用)命令。
    自动触发:在配置文件中通过save 规则配置。例如 save 900 1表示在900秒内,如果至少有1个键被修改,则触发一次BGSAVE。
    优点:
    性能高,恢复快:生成RDB文件时,Redis会fork一个子进程来完成,父进程继续服务。RDB是紧凑的二进制文件,恢复大数据集时速度比AOF快。
    适合备份与灾难恢复:可以将单个文件压缩后转移到其他介质上。
    缺点:
    可能丢失较多数据:如果在上一次快照后Redis发生故障,期间的所有数据修改都会丢失。保存点的间隔时间(例如5分钟)决定了你最多可能丢失的数据量。
    fork可能阻塞:当数据集很大时,fork子进程的过程可能会导致主进程短暂阻塞(虽然时间通常很短)。
  2. AOF (Append Only File)
    工作原理:记录服务器执行的每一个写操作命令(例如SET, HSET, LPUSH),并以Redis协议格式追加到一个日志文件的末尾。重启时,通过重新执行AOF文件中的所有命令来重建数据。
    刷盘策略(appendfsync):这是AOF可靠性的关键配置,决定了何时将内存缓冲区中的命令写入磁盘。
    always:每个写命令都同步写入磁盘。数据最安全,但性能最差,因为每个命令都要等待磁盘I/O完成。
    everysec(默认):每秒同步一次。将缓冲区命令写入磁盘,完成后台线程会执行fsync操作。性能和数据安全性的良好折衷,最多丢失1秒的数据。
    no:由操作系统决定何时同步。性能最好,但数据丢失风险最高(通常同步周期为30秒)。
    优点:
    数据安全性更高:根据不同的刷盘策略,通常最多丢失1秒的数据。
    AOF文件易于理解和解析:它是纯文本格式,记录了所有写操作。
    缺点:
    文件通常比RDB大:恢复速度相对RDB更慢。
    长期运行后文件会膨胀:即使对同一个键反复修改,AOF也会记录所有操作。
  3. 混合持久化 (RDB + AOF, Redis 4.0+ 引入)
    工作原理:这是结合了两者优点的模式。在开启AOF功能的前提下,AOF文件在重写时不再是纯命令追加的形式,而是先将当前内存数据以RDB格式写入AOF文件的开头,之后新产生的写命令再以AOF格式继续追加。这样生成的AOF文件是一个"RDB头 + AOF尾"的混合体。
    优点:
    兼具两者优势:既能利用RDB快速恢复的特性,又能利用AOF保证数据高可靠性的特性。
    恢复速度快:重启时,先加载RDB部分快速恢复大部分数据,再重放增量AOF命令,速度比纯AOF快很多。
    缺点:
    AOF文件的可读性变差(开头部分是二进制RDB格式)。
    AOF 重写机制及其作用
    为什么需要重写:随着服务器运行,AOF文件会越来越大。其中可能包含大量冗余命令,例如对一个计数器键执行了100次INCR,AOF会记录100条命令,而重写后只需一条SET key 100命令即可。文件过大会影响恢复速度,并占用过多磁盘空间。
    重写的作用:
    压缩AOF文件体积:创建一个新的、等效的、更小的AOF文件。
    提升恢复效率:更小的文件加载更快。
    如何重写:Redis会fork一个子进程,扫描当前内存中的所有数据,然后将其转换为一系列Redis命令,写入一个临时的新AOF文件。重写期间的新写命令会同时被记录到当前的AOF缓冲区和重写缓冲区。当子进程完成重写后,Redis会将重写缓冲区的内容追加到新文件末尾,然后原子的用新文件替换旧文件。
    三种持久化机制对比表

|-----------|---------------|----------------------------------------------|-------------------------------------|------------------------|
| 机制 | 持久化方式 | 优点 | 缺点 | 适用场景 |
| RDB | 定时生成数据快照 | 1. 文件小,恢复快 2. 对性能影响小(fork子进程) 3. 适合备份与全量复制 | 1. 可能丢失两次快照间的数据 2. 数据集大时,fork可能阻塞 | 可容忍分钟级数据丢失的缓存、备份场景 |
| AOF | 记录每次写操作命令 | 1. 数据安全性高,默认最多丢1秒数据 2. 文件易于解析和修复 | 1. 文件通常比RDB大 2. 恢复速度较慢 3. 长期运行文件会膨胀 | 对数据可靠性要求高的场景,如存储重要状态 |
| 混合持久化 | RDB头部 + AOF尾部 | 1. 恢复速度快(RDB优点) 2. 数据丢失少(AOF优点) 3. 文件相对纯AOF小 | 1. Redis 4.0+ 才支持 2. 文件可读性较差 | 生产环境推荐方案 ,兼顾性能与可靠性 |

七.Redis 的数据过期策略
1.惰性删除 (Lazy Expiration)
专业性描述:当客户端尝试访问一个键时,Redis在访问前会先检查这个键是否设置了过期时间,以及是否已经过期。如果已过期,则Redis会立即删除这个键,然后像这个键不存在一样处理后续逻辑(通常返回nil)。

  • 优点:CPU友好。删除操作只发生在必须的时候,即键被访问时。如果过期键永远不被访问,那么删除动作就永远不会发生,避免了不必要的CPU消耗。
  • 缺点 :内存不友好。大量过期键可能长期占据内存而无法释放。例如,一场秒杀活动后,大量临时活动数据键(如抢购资格标记)过期了,但如果后续没有用户触发访问,它们就不会被删除,成为了"内存垃圾"。
    2.定期删除 (Active Expiration / Periodic Deletion)
    专业性描述:为了弥补惰性删除的不足,Redis会周期性地、主动地随机抽查一部分设置了过期时间的键,删除其中已过期的键。这个过程由Redis的"定时任务"(hz配置控制频率,默认10Hz,即每秒执行约10次)驱动。每次检查,并非遍历所有过期键,而是从过期字典中随机抽取一定数量的键(由ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP定义,默认20)进行检查和删除。这是一种利用有限CPU资源,逐步清理过期数据的折中方案。
  • 优点 :能一定程度上减少因惰性删除导致的内存泄漏问题,定期回收内存。
  • 缺点 :是一个"概率性"的清理过程。难以保证所有过期键都能在过期后立即被删除。如果过期键太多,而定期删除每次抽查的数量有限,可能会导致一部分过期键存活时间比预期的长。同时,它也需要消耗一定的CPU资源。
    八、Redis 的内存淘汰策略

|-------------------------------|------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| 淘汰策略 | 策略含义与工作机制 | 适用场景与"易购网"示例 |
| noeviction | 不淘汰,直接报错 。当内存不足时,所有会引起内存增加的命令(如SET, LPUSH等)都会返回错误,但读请求(如GET)和删除请求(如DEL)可以正常处理。 | 数据不允许丢失,且宁愿写不进去也不淘汰旧数据 。例如,将Redis用作唯一数据存储(非缓存),必须保证数据绝对一致。在"易购网"中,如果Redis存储了关键的、未备份的交易流水,可能会采用此策略,但更常见的是通过集群分片来避免此情况。 |
| allkeys-lru | 从所有键中 ,使用LRU算法 淘汰最近最少使用的键。LRU算法会优先淘汰最久未被访问的键。 | 最常用、最通用的缓存策略 。适用于缓存中的热点数据分布明显,希望长期保留高频访问的数据。例如,"易购网"的商品详情缓存,我们希望淘汰那些很久没人查看的冷门商品缓存,保留热门商品的缓存。 |
| volatile-lru | 从设置了过期时间的键中 ,使用LRU算法 淘汰最近最少使用的键。 | 希望对永久数据和临时数据区别对待 。例如,"易购网"中,用户购物车(Hash)我们可能不设过期时间,但商品详情(String)我们设了5分钟过期。我们希望内存不足时,只从那些有过期时间的商品缓存中淘汰,而不影响用户的购物车数据。 |
| allkeys-random | 从所有键中随机 选择一个键进行淘汰。 | 所有数据被访问的概率大致相同,无明确热点 。例如,如果"易购网"的缓存访问完全随机,没有规律,可以用此策略。但实际生产中较少见。 |
| volatile-random | 从设置了过期时间的键中随机 选择一个键进行淘汰。 | 同volatile-lru,但不关心访问频率,只是随机淘汰。适用于临时数据重要性相同,随机淘汰即可的场景。 |
| volatile-ttl | 从设置了过期时间的键中 ,淘汰剩余存活时间(TTL)最短 的键。即,优先淘汰最快就要过期的键。 | 希望尽快释放即将过期的键所占用的空间 。例如,"易购网"的短信验证码缓存(有效期2分钟),当内存不足时,优先淘汰那些马上就要失效的验证码,因为它们很快也会自动过期。 |
| allkeys-lfu (Redis 4.0+) | 从所有键中 ,使用LFU算法 淘汰访问频率最低的键。LFU统计的是键的访问次数,并随时间衰减,更精准反映"热度"。 | 希望根据长期的访问频率来淘汰 ,能更好地保护真正的热点数据,避免偶发性访问的数据挤占内存。例如,"易购网"的热搜词排行榜,我们希望长期热门词汇的缓存不被新出现的突发新闻临时挤掉。 |
| volatile-lfu (Redis 4.0+) | 从设置了过期时间的键中 ,使用LFU算法 淘汰访问频率最低的键。 | 类似volatile-lru,但使用LFU算法。希望只在临时数据中,淘汰访问次数最少的数据。 |

相关推荐
老毛肚3 小时前
Redis高级
java·数据库·redis
kiku18184 小时前
NoSQL之Redis集群
数据库·redis·nosql
indexsunny5 小时前
互联网大厂Java面试实录:微服务+Spring Boot在电商场景中的应用
java·spring boot·redis·微服务·eureka·kafka·spring security
卢傢蕊5 小时前
NoSQL 之Redis 集群
数据库·redis·nosql
霸道流氓气质6 小时前
Bat中实现简单运维脚本示例-启动redis、检测指定端口是否占用、占用则杀死进程、等待指定秒数、启动jar包
运维·redis·jar
jeCA EURG7 小时前
一、安装Redis(win11环境下)
数据库·redis·缓存
后端漫漫7 小时前
Redis学习框架
数据库·redis·学习
毅炼8 小时前
Redis 常见问题总结
java·数据库·redis·后端