Redis 入门指南

1. Redis 是什么?

Redis (Remote Dictionary Server) 是一个开源的、基于内存的 键值对 (Key-Value) 数据库

打个比方:

  • 传统数据库 (MySQL/SQL Server) 就像是 图书馆的仓库,存储在硬盘上,容量大但读取慢。
  • Redis 就像是 图书馆的阅览桌,存储在内存中,空间有限但拿取数据极其快(因为不需要等待磁盘转动)。

核心特点:

  • 速度极快:基于内存操作,读写速度可达 10万次/秒。
  • 支持多种数据结构:不仅仅存字符串,还能存列表、集合等。
  • 持久化:虽然是内存数据库,但它能把数据定期存到硬盘,防止断电丢失。

2. Redis 有什么作用?

  1. 缓存 (Cache):最主要的作用。将热点数据放在 Redis 中,减少数据库查询压力,提升网页响应速度。
  2. 会话存储 (Session):在分布式系统中存储用户的登录状态。
  3. 排行榜:利用自带的排序功能实现游戏或电商排行榜。
  4. 计数器:视频播放量、点赞数(因为 Redis 的自增操作是原子性的,不会出错)。
  5. 分布式锁:在多台服务器之间协调任务,防止冲突。

3. 五大数据类型及适用场景

Redis 最经典的五种数据类型如下。使用 C# (配合 StackExchange.Redis 库) 进行简单演示。

假设我们已经创建了连接对象 IDatabase db:

(1) String (字符串)

最基本类型,Key 对应一个 Value(可以是文本、数字、甚至序列化后的 JSON 对象)。

  • 场景:缓存用户信息(JSON)、验证码、网页点击量计数。

  • C# 示例

    // 1. 简单的键值存储
    db.StringSet("username:1001", "zhangsan");
    string name = db.StringGet("username:1001");
    
    // 2. 计数器 (点赞数)
    db.StringIncrement("article:likes:55"); // 每次调用加1
    

(2) Hash (哈希)

类似于 C# 中的 Dictionary<string, string>,一个 Key 里面存着多个字段和值。

  • 场景:存储对象,例如“用户资料”(修改名字时不需要取出整个对象,只需修改 Name 字段)。

  • C# 示例

    // 存储用户ID为1的资料
    db.HashSet("user:1", new HashEntry[] {
        new HashEntry("name", "李四"),
        new HashEntry("age", 25)
    });
    
    // 只获取年龄
    var age = db.HashGet("user:1", "age");
    

(3) List (列表)

一个有序的字符串链表,可以从头或尾添加元素。

  • 场景:消息队列、最新消息流(朋友圈时间线)、待办事项。

  • C# 示例

    // 左侧推入消息 (由新到旧)
    db.ListLeftPush("msg_queue", "消息A");
    db.ListLeftPush("msg_queue", "消息B");
    
    // 右侧弹出处理
    var msg = db.ListRightPop("msg_queue"); // 取出 "消息A"
    

(4) Set (集合)

无序的字符串集合,自动去重(添加重复元素无效)。

  • 场景:标签系统(Tag)、抽奖(随机抽取)、计算共同好友(利用交集功能)。

  • C# 示例

    // 添加标签,自动去重
    db.SetAdd("user:1:tags", "sport");
    db.SetAdd("user:1:tags", "music");
    db.SetAdd("user:1:tags", "sport"); // 不会重复添加
    
    // 判断是否包含
    bool isMusicFan = db.SetContains("user:1:tags", "music");
    

(5) Sorted Set / ZSet (有序集合)

类似 Set,但每个元素关联一个 分数 (Score),根据分数自动排序。

  • 场景:排行榜(按分数排序)、带权重的任务队列。

  • C# 示例

    // 记录游戏分数
    db.SortedSetAdd("game:rank", "玩家A", 100);
    db.SortedSetAdd("game:rank", "玩家B", 500);
    db.SortedSetAdd("game:rank", "玩家C", 300);
    
    // 获取前三名 (Redis中通常是按分数低到高,高到低用Descending)
    var topPlayers = db.SortedSetRangeByRank("game:rank", 0, 2, Order.Descending);
    

4. 缓存三大经典问题

在面试和实战中,这三个问题非常关键。

(1) 缓存穿透 (Cache Penetration)

  • 是什么:用户查询一个数据,Redis 里没有,数据库里也没有
    • 比如黑客疯狂请求 ID 为 -1 的用户。请求会直接穿过 Redis 打到数据库,导致数据库崩溃。
  • 怎么办
    • 缓存空对象:如果数据库没查到,也在 Redis 存一个 null,并设置较短的过期时间。
    • 布隆过滤器 (Bloom Filter):一种特殊算法,在请求到达 Redis 前先判断该 ID 是否可能存在。

(2) 缓存击穿 (Cache Breakdown)

  • 是什么一个非常热点的 Key(比如微博热搜第一名)突然过期了。
    • 在过期的那一瞬间,成千上万的请求同时发现缓存没了,全部涌向数据库。
  • 怎么办
    • 互斥锁 (Mutex):发现缓存没了,只允许一个线程去查数据库,其他线程等待。
    • 逻辑过期:数据本身不设置过期时间,而是在 Value 里存一个时间戳,代码检测到“过期”后异步更新。

(3) 缓存雪崩 (Cache Avalanche)

  • 是什么大量的 Key 在同一时间集体过期,或者 Redis 服务器直接宕机。
    • 所有请求全部砸向数据库。
  • 怎么办
    • 随机过期时间:存数据时,过期时间设为 1小时 + 随机几分钟,避免集体过期。
    • 高可用集群:使用 Redis Sentinel 或 Cluster 模式,一台挂了另一台顶上。

5. 触发器/钩子 (Triggers / Hooks)

新手需要注意:Redis 主要是数据存储,不像 SQL Server 或 Oracle 那样有完善的“触发器”功能(即数据变动自动触发代码)。

但在 Redis 中,我们通常通过以下方式实现类似“钩子”的效果:

(1) 键空间通知 (Keyspace Notifications)

这是 Redis 最接近“触发器”的功能。

  • 原理:Redis 开启此功能后,当发生命令(如 DEL, EXPIRE, SET)时,Redis 会自动发布一条消息。
  • 场景:订单超时自动取消。
    • 做法:监听 Key 的 Expired 事件。当 Key 过期消失时,你的 C# 代码会收到通知,然后去数据库取消订单。

    • C# 概念代码:

      var sub = redis.GetSubscriber();
      // 订阅过期事件 (需在 redis.conf 中开启 notify-keyspace-events Ex)
      sub.Subscribe("__keyevent@0__:expired", (channel, key) => {
          Console.WriteLine($"Key {key} 刚刚过期了!");
          // 执行业务逻辑...
      });
      

(2) Redis Gears (高级模块)

简单来说,Redis Gears 是 Redis 的一个“大脑”插件。它是一个 动态编程框架/引擎

  • 以前的 Redis:是一个单纯的仓库。你(C# 代码)发指令说“存这个”、“取那个”,Redis 只是照做。
  • 装了 Gears 的 Redis:变成了一个智能仓库。你不仅可以存数据,还可以把一段代码(脚本)上传给 Redis,让 Redis 自己在内部运行这段代码。

核心亮点:它支持 Python!你可以写一段 Python 脚本,让 Redis 服务器直接执行,而不需要把数据拉回到你的 C# 客户端处理。

它比 Lua 脚本强在哪里?

  • Lua 脚本:像是一个 简单的计算器。适合做一些原子性的、短平快的小操作(比如“如果库存够就减1,不够就报错”)。
  • Redis Gears:像是一个 全功能的机器人
    • 它支持 跨分片(Cluster)操作:可以在集群的多个节点间处理数据。
    • 它可以 长期运行:不是运行完就结束,而是可以一直监听。
    • 它支持 非阻塞:可以在后台慢慢跑,不卡住主线程。

核心应用场景:

A. “写后同步” (Write-Behind / Write-Through) —— 自动存入数据库

  • 传统做法:你的 C# 代码先写 Redis,再写 MySQL。如果中间断网了,数据就不一致了。
  • Gears 做法:你的 C# 代码 只管写 Redis。Redis Gears 配置了一个监听器,一旦发现有新数据写入,它 自动 在后台把数据同步写入 MySQL。
    • 好处:你的 C# 代码变得极简,而且读写速度飞快(因为只跟内存打交道)。

B. 超级触发器

  • 场景:你要做一个“实时反欺诈”系统。
  • Gears 做法:每当有新的一笔交易(Key)写入,Gears 脚本自动抓取过去 10 分钟该用户的交易记录,分析是否有风险。如果风险高,直接在 Redis 内部就把账号封锁,整个过程毫秒级完成,不需要你的 C# 后端介入。

6. Redis 持久化:RDB vs AOF

6.1 RDB (Redis DataBase) - 快照模式

是什么?

RDB 就像是给你的作业本 “拍照”
它会在指定的时间间隔内,把内存里所有的数据生成一个文件(通常叫 dump.rdb)存在硬盘上。

工作原理:

Redis 会在后台启动一个子进程,把当前那一瞬间的所有数据“复制”一份写入硬盘。

  • 比如:你设置“每隔 1 小时保存一次”。
  • 在 8:00、9:00、10:00 时,Redis 都会生成一个当时的数据快照文件。

优缺点:

  • 优点(恢复快)
    • 文件紧凑,体积小(因为是二进制压缩文件)。
    • 恢复速度极快,适合大规模的数据恢复(直接把文件读进内存就行)。
  • 缺点(容易丢数据)
    • 安全性较低。如果你在 9:59 停电了,而上次保存是 9:00,那你这 59分钟 的数据就全没了!

6.2 AOF (Append Only File) - 日志模式

是什么?

AOF 就像是 “记流水账”
它不会存数据的最终状态,而是把你执行的每一条 写命令(如 set key valuedel key)都记录到一个文件里(通常叫 appendonly.aof)。

工作原理:

每当你向 Redis 写入数据,Redis 就会把这条命令追加到 AOF 文件的末尾。

  • 恢复数据时,Redis 就像“重放录像”一样,把 AOF 文件里的命令从头到尾再执行一遍。

优缺点:

  • 优点(数据安全)
    • 安全性高。你可以设置“每秒钟保存一次”,就算断电,最多也只丢失这一秒的数据。
  • 缺点(文件大、恢复慢)
    • 文件体积通常比 RDB 大很多(因为它记录的是流水账)。
    • 恢复速度慢。因为要重新执行一遍所有命令,如果数据量大,启动会比较久。

6.3 应该怎么选?

作为新手,你可能会问:“我该用哪一个?”

答案是:通常混合使用。

在 Redis 4.0 之后,官方推荐 混合持久化(默认开启)。

  • 平时:Redis 主要利用 AOF 来保证数据不丢失。
  • 重启/重写时:Redis 会把 AOF 文件里的一部分内容转换成 RDB 格式(为了瘦身和快速加载),新的增量数据继续用 AOF 记录。

建议配置:

  1. 如果你的数据只是 纯缓存(丢了也没事,数据库里还有),甚至可以把持久化 全部关闭,这样 Redis 性能最高。
  2. 如果是生产环境,一般 同时开启 RDB 和 AOF
    • 用 RDB 做定期的冷备份(比如每天凌晨备份一次发给其他服务器)。
    • 用 AOF 保证服务器宕机后的数据实时恢复。

6.4 简单配置示例 (redis.conf)

虽然不需要你死记硬背,但了解一下配置文件长什么样很有帮助:

RDB 配置:

# 格式:save <秒数> <变化次数>
save 900 1      # 900秒内如果有1个键被修改,就保存一次
save 300 10     # 300秒内如果有10个键被修改,就保存一次

AOF 配置:

appendonly yes          # 开启 AOF 功能
appendfsync everysec    # 每秒钟刷盘一次 (折中方案,推荐)
# appendfsync always    # 每次写入都刷盘 (最安全,但慢)
# appendfsync no        # 看操作系统心情刷盘 (最快,但不安全)