Redis 入门教程

目录

  1. [Redis 基础入门](#Redis 基础入门 "#%E4%B8%80redis-%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8")
  2. [Redis 数据类型与命令](#Redis 数据类型与命令 "#%E4%BA%8Credis-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%B8%8E%E5%91%BD%E4%BB%A4")
  3. [Node.js 操作 Redis](#Node.js 操作 Redis "#%E4%B8%89nodejs-%E6%93%8D%E4%BD%9C-redis")
  4. [Nest.js 集成 Redis](#Nest.js 集成 Redis "#%E5%9B%9Bnestjs-%E9%9B%86%E6%88%90-redis")
  5. [cache-manager vs 直接操作 Redis](#cache-manager vs 直接操作 Redis "#%E4%BA%94cache-manager-vs-%E7%9B%B4%E6%8E%A5%E6%93%8D%E4%BD%9C-redis")

一、Redis 基础入门

1.1 什么是 Redis?

Redis 是一个开源的内存数据库,采用 key-value 键值对的形式存储数据。

1.2 为什么需要 Redis?

MySQL 的问题:

  • 数据存储在硬盘,读写速度慢
  • 需要解析和执行 SQL 语句,有性能开销
  • 高并发场景下容易成为性能瓶颈

Redis 的优势:

  • 数据存储在内存,读写速度极快
  • 支持丰富的数据结构
  • 支持数据持久化
存储介质 读取速度
内存 (RAM) ~100 ns
SSD ~100 μs
HDD ~10 ms

1.3 Redis 的用途

  1. 数据库缓存:将 MySQL 查询结果缓存到 Redis,减少数据库查询
  2. 直接存储:某些场景下直接用 Redis 存储数据(成本较高)
  3. 分布式锁:实现分布式系统的并发控制
  4. 消息队列:使用 List 或 Stream 实现简单消息队列
  5. 会话管理:存储用户 Session 信息

1.4 启动 Redis

使用 Docker 启动

bash 复制代码
docker run -d --name redis \
  -p 6379:6379 \
  -v /path/to/data:/data \
  redis:latest

使用 redis-cli 连接

bash 复制代码
redis-cli

1.5 GUI 工具 - RedisInsight

Redis 官方提供的可视化工具:RedisInsight

功能:

  • 可视化查看所有 key 和 value
  • 执行 Redis 命令
  • 监控 Redis 性能
  • 支持多种数据类型的展示

二、Redis 数据类型与命令

2.1 String 字符串

最基本的数据类型,可以存储字符串、数字。

bash 复制代码
# 设置值
SET name "张三"
SET age 25

# 获取值
GET name
GET age

# 递增(常用于计数器:阅读量、点赞量)
INCR age
INCRBY age 5

# 查询所有 key
KEYS *

# 删除 key
DEL name

使用场景: 缓存、计数器、分布式锁

2.2 List 列表

有序的字符串列表,支持从两端操作。

bash 复制代码
# 从左边添加
LPUSH list1 111
LPUSH list1 222
LPUSH list1 333

# 从右边添加
RPUSH list1 444
RPUSH list1 555

# 从左边取出
LPOP list1

# 从右边取出
RPOP list1

# 查询列表(0 到 -1 表示全部)
LRANGE list1 0 -1

# 获取列表长度
LLEN list1

使用场景: 消息队列、最新消息列表

2.3 Set 集合

无序、不重复的字符串集合。

bash 复制代码
# 添加元素(自动去重)
SADD set1 111
SADD set1 111  # 重复,不会添加
SADD set1 222
SADD set1 333

# 判断是否是集合中的元素
SISMEMBER set1 111  # 返回 1(存在)
SISMEMBER set1 444  # 返回 0(不存在)

# 获取所有元素
SMEMBERS set1

# 获取元素个数
SCARD set1

# 删除元素
SREM set1 111

使用场景: 标签、好友关系、去重

2.4 Sorted Set (ZSet) 有序集合

每个元素关联一个分数(score),按分数排序。

bash 复制代码
# 添加元素(带分数)
ZADD zset1 5 feng
ZADD zset1 4 dong
ZADD zset1 3 xxx
ZADD zset1 6 yyyy

# 获取排名前 N 的元素(按分数升序)
ZRANGE zset1 0 2

# 获取排名前 N 的元素(按分数降序)
ZREVRANGE zset1 0 2

# 获取元素的分数
ZSCORE zset1 feng

# 获取元素的排名
ZRANK zset1 feng

# 删除元素
ZREM zset1 xxx

使用场景: 排行榜、优先级队列

2.5 Hash 哈希表

类似 Map 的结构,存储字段-值的映射。

bash 复制代码
# 设置字段值
HSET hash1 key1 1
HSET hash1 key2 2
HSET hash1 key3 3

# 获取字段值
HGET hash1 key1

# 获取所有字段和值
HGETALL hash1

# 删除字段
HDEL hash1 key1

# 判断字段是否存在
HEXISTS hash1 key1

使用场景: 存储对象、用户信息

2.6 Geo 地理位置

存储地理位置信息,支持距离计算和范围查询。

bash 复制代码
# 添加地理位置(经度 纬度 名称)
GEOADD loc 13.361389 38.115556 "fengfeng"
GEOADD loc 15.087269 37.502669 "dongdong"

# 计算两个位置的距离
GEODIST loc fengfeng dongdong

# 搜索某半径内的位置
GEORADIUS loc 15 37 100 km
GEORADIUS loc 15 37 200 km

使用场景: 附近的人、附近店铺

2.7 过期时间

bash 复制代码
# 设置 key 的过期时间(秒)
EXPIRE key 30

# 查看剩余过期时间
TTL key

# 取消过期时间
PERSIST key

使用场景: 缓存过期、验证码有效期、Session 管理

2.8 常用命令速查表

命令 说明
SET key value 设置字符串值
GET key 获取字符串值
DEL key 删除 key
EXPIRE key seconds 设置过期时间
TTL key 查看剩余过期时间
KEYS pattern 查找 key(pattern 如 *
EXISTS key 判断 key 是否存在
INCR key 数值递增
LPUSH/RPUSH 列表左/右添加
LPOP/RPOP 列表左/右弹出
LRANGE key start stop 获取列表范围
SADD 集合添加
SISMEMBER 判断是否是集合元素
ZADD 有序集合添加
ZRANGE 获取有序集合范围
HSET/HGET 哈希表设置/获取

三、Node.js 操作 Redis

3.1 客户端选择

Node.js 操作 Redis 的流行包:

包名 特点
redis 官方推荐,API 现代化
ioredis 社区流行,功能丰富

3.2 使用 redis 包

安装

bash 复制代码
npm install redis

基本用法

typescript 复制代码
import { createClient } from 'redis';

// 创建客户端
const client = createClient({
  socket: {
    host: 'localhost',
    port: 6379
  }
});

// 监听错误
client.on('error', err => console.log('Redis Client Error', err));

// 连接
await client.connect();

// 执行命令
const keys = await client.keys('*');
console.log(keys);

// 设置值
await client.set('name', '张三');

// 获取值
const name = await client.get('name');
console.log(name);

// 操作 Hash
await client.hSet('user:1', 'name', '张三');
await client.hSet('user:1', 'age', '25');
const user = await client.hGetAll('user:1');

// 断开连接
await client.disconnect();

3.3 使用 ioredis 包

安装

bash 复制代码
npm install ioredis

基本用法

typescript 复制代码
import Redis from 'ioredis';

// 创建客户端(默认连接 localhost:6379)
const redis = new Redis();

// 执行命令
const keys = await redis.keys('*');
console.log(keys);

// 操作 Hash
await redis.hset('user:1', 'name', '张三');
const name = await redis.hget('user:1', 'name');

// 断开连接
redis.disconnect();

3.4 redis vs ioredis 对比

特性 redis ioredis
维护者 NodeRedis 官方 社区
Promise 支持
Cluster 支持
Sentinel 支持
Pipeline
Lua 脚本

四、Nest.js 集成 Redis

4.1 方式一:自定义 Provider(推荐)

安装依赖

bash 复制代码
npm install redis

配置 Provider

typescript 复制代码
// app.module.ts
import { Module } from '@nestjs/common';
import { createClient } from 'redis';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: 'REDIS_CLIENT',
      async useFactory() {
        const client = createClient({
          socket: {
            host: 'localhost',
            port: 6379
          },
          database: 0  // 指定使用的数据库
        });
        await client.connect();
        return client;
      }
    }
  ],
})
export class AppModule {}

注入使用

typescript 复制代码
// app.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { RedisClientType } from 'redis';

@Injectable()
export class AppService {
  @Inject('REDIS_CLIENT')
  private redisClient: RedisClientType;

  async getHello() {
    // 获取所有 key
    const keys = await this.redisClient.keys('*');
    console.log(keys);

    // 设置值
    await this.redisClient.set('key', 'value');

    // 获取值
    const value = await this.redisClient.get('key');

    return 'Hello World!';
  }
}

4.2 方式二:封装 RedisModule(推荐生产使用)

创建 Redis 模块

typescript 复制代码
// redis/redis.module.ts
import { Module, Global } from '@nestjs/common';
import { createClient } from 'redis';
import { RedisService } from './redis.service';

@Global()
@Module({
  providers: [
    RedisService,
    {
      provide: 'REDIS_CLIENT',
      async useFactory() {
        const client = createClient({
          socket: {
            host: 'localhost',
            port: 6379
          }
        });
        await client.connect();
        return client;
      }
    }
  ],
  exports: [RedisService]
})
export class RedisModule {}

创建 Redis 服务

typescript 复制代码
// redis/redis.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { RedisClientType } from 'redis';

@Injectable()
export class RedisService {
  @Inject('REDIS_CLIENT')
  private client: RedisClientType;

  async get(key: string) {
    return await this.client.get(key);
  }

  async set(key: string, value: string, ttl?: number) {
    if (ttl) {
      await this.client.setEx(key, ttl, value);
    } else {
      await this.client.set(key, value);
    }
  }

  async del(key: string) {
    await this.client.del(key);
  }

  async hSet(key: string, field: string, value: string) {
    await this.client.hSet(key, field, value);
  }

  async hGet(key: string, field: string) {
    return await this.client.hGet(key, field);
  }

  async hGetAll(key: string) {
    return await this.client.hGetAll(key);
  }
}

在模块中使用

typescript 复制代码
// app.module.ts
import { Module } from '@nestjs/common';
import { RedisModule } from './redis/redis.module';

@Module({
  imports: [RedisModule],
  // ...
})
export class AppModule {}
typescript 复制代码
// app.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { RedisService } from './redis/redis.service';

@Injectable()
export class AppService {
  @Inject(RedisService)
  private redisService: RedisService;

  async getHello() {
    await this.redisService.set('name', '张三');
    const name = await this.redisService.get('name');
    return `Hello ${name}!`;
  }
}

4.3 封装动态模块(支持配置)

typescript 复制代码
// redis/redis.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common';
import { createClient } from 'redis';
import { RedisService } from './redis.service';

@Global()
@Module({})
export class RedisModule {
  static forRoot(options: { host: string; port: number }): DynamicModule {
    return {
      module: RedisModule,
      providers: [
        RedisService,
        {
          provide: 'REDIS_CLIENT',
          async useFactory() {
            const client = createClient({
              socket: options
            });
            await client.connect();
            return client;
          }
        }
      ],
      exports: [RedisService]
    };
  }
}

使用:

typescript 复制代码
@Module({
  imports: [
    RedisModule.forRoot({
      host: 'localhost',
      port: 6379
    })
  ],
})
export class AppModule {}

五、cache-manager vs 直接操作 Redis

5.1 cache-manager 简介

Nest.js 官方文档推荐使用 cache-manager 来操作 Redis。

bash 复制代码
npm install @nestjs/cache-manager cache-manager

基本用法

typescript 复制代码
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';

@Controller()
export class AppController {
  @Inject(CACHE_MANAGER)
  private cacheManager: Cache;

  @Get('set')
  async set(@Query('value') value: string) {
    await this.cacheManager.set('key', value);
    return 'done';
  }

  @Get('get')
  async get() {
    return this.cacheManager.get('key');
  }

  @Get('del')
  async del() {
    await this.cacheManager.del('key');
    return 'done';
  }
}

CacheInterceptor 自动缓存

typescript 复制代码
import { CacheInterceptor } from '@nestjs/cache-manager';

@Get('aaa')
@UseInterceptors(CacheInterceptor)
aaa(@Query('a') a: string) {
  return 'aaa';
}

5.2 为什么不用 cache-manager?

特性 cache-manager 直接用 redis
get/set
List 操作
Hash 操作
Set 操作
ZSet 操作
Geo 操作
所有 Redis 命令
CacheInterceptor 需自己实现

结论: cache-manager 只支持 get/set,无法使用 Redis 丰富的数据结构和命令。实际项目中,大多数场景需要使用 List、Hash、ZSet 等,所以建议直接操作 Redis。

5.3 自己实现 CacheInterceptor

如果需要接口缓存功能,可以自己实现:

typescript 复制代码
// my-cache.interceptor.ts
import { CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor } from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
import { RedisClientType } from 'redis';
import { of, tap } from 'rxjs';

@Injectable()
export class MyCacheInterceptor implements NestInterceptor {
  @Inject('REDIS_CLIENT')
  private redisClient: RedisClientType;

  @Inject(HttpAdapterHost)
  private httpAdapterHost: HttpAdapterHost;

  async intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest();
    
    // 生成缓存 key(使用请求 URL)
    const key = this.httpAdapterHost.httpAdapter.getRequestUrl(request);

    // 查询缓存
    const value = await this.redisClient.get(key);

    if (!value) {
      // 没有缓存,执行 handler 并缓存结果
      return next.handle().pipe(tap((res) => {
        this.redisClient.set(key, typeof res === 'object' ? JSON.stringify(res) : res);
      }));
    } else {
      // 有缓存,直接返回
      return of(JSON.parse(value));
    }
  }
}

使用:

typescript 复制代码
@Get('aaa')
@UseInterceptors(MyCacheInterceptor)
aaa(@Query('a') a: string) {
  console.log('handler executed');
  return { data: 'aaa' };
}

六、最佳实践

6.1 Key 命名规范

bash 复制代码
业务名:对象名:id:[属性]

示例:

less 复制代码
user:token:123
order:detail:456
product:list:page:1

6.2 过期时间策略

  • 缓存数据:设置合理的过期时间(如 30 分钟)
  • 会话数据:根据业务需求设置(如 24 小时)
  • 计数器:不设置过期时间或设置较长

6.3 内存管理

  • 定期清理过期 key
  • 监控内存使用情况
  • 设置 maxmemory 和淘汰策略

6.4 常见使用场景

场景 数据类型 说明
缓存 String 缓存数据库查询结果
计数器 String 阅读量、点赞量
排行榜 ZSet 用户积分排名
消息队列 List 简单的消息队列
用户信息 Hash 存储对象属性
标签 Set 文章标签、用户标签
附近的人 Geo 地理位置查询
分布式锁 String 实现并发控制

七、命令速查

字符串操作

bash 复制代码
SET key value          # 设置值
GET key                # 获取值
MSET k1 v1 k2 v2      # 批量设置
MGET k1 k2             # 批量获取
INCR key               # 自增
DECR key               # 自减
APPEND key value       # 追加
STRLEN key             # 获取长度

列表操作

bash 复制代码
LPUSH key v1 v2        # 左边添加
RPUSH key v1 v2        # 右边添加
LPOP key               # 左边弹出
RPOP key               # 右边弹出
LRANGE key 0 -1        # 获取全部
LLEN key               # 获取长度
LINDEX key index       # 获取指定位置

集合操作

bash 复制代码
SADD key v1 v2         # 添加元素
SREM key v1            # 删除元素
SMEMBERS key           # 获取所有元素
SISMEMBER key v1       # 判断是否存在
SCARD key              # 获取元素个数
SINTER key1 key2       # 交集
SUNION key1 key2       # 并集
SDIFF key1 key2        # 差集

有序集合操作

bash 复制代码
ZADD key score v1      # 添加元素
ZREM key v1            # 删除元素
ZRANGE key 0 -1        # 获取范围
ZREVRANGE key 0 -1     # 逆序获取
ZSCORE key v1          # 获取分数
ZRANK key v1           # 获取排名
ZINCRBY key score v1   # 增加分数

哈希操作

bash 复制代码
HSET key f1 v1         # 设置字段
HGET key f1            # 获取字段
HGETALL key            # 获取所有字段
HDEL key f1            # 删除字段
HEXISTS key f1         # 判断字段存在
HINCRBY key f1 1       # 字段递增

参考文档:

相关推荐
吃饱了得干活3 小时前
Redis 从单机到集群:持久化、主从复制、哨兵与集群完全指南
redis·后端
星星落进兜里4 小时前
Redis 内存缓存,面试补充
数据库·redis·面试
小程故事多_805 小时前
企业AI对话记忆架构实战,告别单一Redis存储,搭建长短效协同的记忆体系
人工智能·redis·架构
谷无姜7 小时前
QuizForge:在不停踩坑后的技术决策复盘
前端·nestjs
盟道科技7 小时前
Redis+Lua 实现滑动窗口限流:从固定窗口踩坑到生产可用的完整方案
redis·junit·lua
三8448 小时前
SSRF 从入门到实战(下篇):gopher 打 Redis 实现 RCE
数据库·redis·缓存
驾驭人生1 天前
Hangfire.Redis.StackExchange 已归档停更!生产任务卡死、重复执行、网络抖动问题解决方案
数据库·redis·缓存
吃饱了得干活1 天前
Redis 不是死脑筋,它是一套“会进化”的存储系统
redis·后端
盟道科技1 天前
小程序电商订单超时自动取消的三种实现方案:定时扫描、Redis 过期监听、延迟消息对比与生产落地
数据库·redis·小程序