nestjs如何接入Redis

678 阅读6分钟

在nestjs中,提供了缓存机制,但是数据都储存在内存中的,如果服务器宕机,那么这些数据将会丢失。所以就需要redis这种数据库存储来保存这些数据。

下面我们就来看看如何在nestjs中集成Redis并配合cache-manager来存储数据吧。

Redis

Redis 不是简单的缓存工具,而是实时数据处理的基础平台。不仅是键值存储,更是一个支持多种数据结构的高级内存数据库,被广泛用于缓存、消息代理和实时分析等场景。

  • 持久化存储能力
    • Redis 提供 RDB(在指定的时间间隔内创建数据快照)和 AOF(记录每个写操作,重启时重新执行这些操作)两种持久化机制
    • 即使服务器重启或应用崩溃,缓存数据也不会丢失
    • 支持数据备份和恢复,确保业务连续性
  • 分布式架构支持
    • 在微服务架构中,多个服务实例可以共享同一个 Redis 实例或集群
    • 实现真正的全局缓存,避免数据不一致问题
    • 支持横向扩展,可以构建 Redis 集群应对高并发场景
  • 高性能内存数据库
    • 基于内存的操作,读写延迟通常在微秒级别
    • 单个 Redis 实例可以处理每秒数十万次的读写操作
    • 支持管道(pipelining)批量操作,进一步提升性能
  • 丰富的数据结构支持
    • 字符串(Strings):基本的键值对存储
    • 哈希(Hashes):存储对象,类似 Map 结构
    • 列表(Lists):实现队列和栈等数据结构
    • 集合(Sets):无序不重复元素集合
    • 有序集合(Sorted Sets):带分数的集合
    • 位图(Bitmaps)和 HyperLogLogs 等高级结构
  • 高级缓存策略
    • LRU(最近最少使用)和 LFU(最不经常使用)淘汰策略
    • TTL(Time To Live)自动过期机制
    • 发布/订阅模式实现事件通知

nestjs集成Redis

我们需要先安装依赖库

npm install --save @nestjs-modules/ioredis ioredis

redis服务,我们通过docker进行管理。这里提供一个docker-compose.yml

version: '3.3'
services:
  redis:
    image: redis:latest
    container_name: redis
    restart: always
    volumes:
      - redis_volume_data:/data
    ports:
      - 6379:6379
volumes:
  redis_volume_data:

启动后,我们可以通过Another Redis Desktop Manager来链接Redis进行数据管理。

image.png

导入模块

import { Module } from '@nestjs/common';
import { RedisModule } from '@nestjs-modules/ioredis';
import { AppController } from './app.controller';

@Module({
  imports: [
    RedisModule.forRootAsync({
      useFactory: () => ({
        type: 'single',
        url: 'redis://localhost:6379',
      }),
    }),
  ],
  controllers: [AppController],
})
export class AppModule {}

使用

  constructor(
    @InjectRedis() private readonly redis: Redis,
  ) { }

  @Get()
  async getRedisCache(@Query("token") token: string) {
    const redisToken = await this.redis.get("token");
    console.log("redis有值吗", redisToken, token)
    if (redisToken) return redisToken;
    // 设置过期时间
    await this.redis.set("token", token, "EX", 60);
    return token;
  }

image.png

Cache Manager 结合 Redis

@nestjs/cache-manager 是 NestJS 官方提供的缓存模块,它基于 cache-manager 库构建,提供了统一的缓存接口。Redis 作为其中一个存储后端,通过keyv-rediscache-manager-redis-yet 或 cache-manager-ioredis-yet 等适配器与 Cache Manager 集成。 注意cache-manager@6+需要结合keyv-redis集成Redis存储。

# 基础依赖 
npm install @nestjs/cache-manager cache-manager 

# Redis 存储适配器
npm install @keyv/redis keyv

为了模块清晰,我们这里将封装一个Cache模块。

import { Module } from '@nestjs/common';
import { CacheModule as CacheManagerModule } from '@nestjs/cache-manager';
import { createKeyv } from '@keyv/redis';
import { ConfigService } from '@nestjs/config';
import { REDIS_PORT, REDIS_HOST, CACHE_TTL } from '../consts';

@Module({
  imports: [
    CacheManagerModule.registerAsync({
      isGlobal: true,
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        const redisHost = configService.get(REDIS_HOST);
        const redisPort = configService.get(REDIS_PORT);
        const ttl = configService.get(CACHE_TTL);
        return {
          stores: [createKeyv(`redis://${redisHost}:${redisPort}`)],
          ttl
        };
      },
    }),
  ],
})
export class CacheModule { }

使用


import { CACHE_MANAGER } from '@nestjs/cache-manager';
import type { Cache } from 'cache-manager';

constructor(
    @Inject(CACHE_MANAGER) private cacheManager: Cache
) { }

@Get()
async getCache(@Query("token") token: string) {
const cacheToken = await this.cacheManager.get("token");
console.log("有值吗", cacheToken, token)
if (cacheToken) return cacheToken;
await this.cacheManager.set("token", token);
return token;
}

image.png

image.png

cacheManager还提供了很多装饰器供我们来处理接口的缓存。

  • UseInterceptors设置缓存,可以设置全局,控制器,方法级别接口缓存。一般只会设置控制器和方法,不会设置全局。
import { UseInterceptors } from '@nestjs/common';
import {
  CacheInterceptor
} from '@nestjs/cache-manager';

@UseInterceptors(CacheInterceptor)
  • CacheKey 为缓存项生成唯一标识符,一般设置方法级别接口缓存。 当没有使用 @CacheKey 装饰器时,NestJS 会按照以下规则生成缓存键:
[HTTP_METHOD]_[ROUTE_PATH]_[REQUEST_PARAMETERS]

为了更好的控制,所以一般都是我们自己去设置规则生成。例如<模块>:<实体>:<唯一标识>[:<后缀>]需要根据不同请求参数动态设置标识。

// 静态键
@Get()
@CacheKey('ALL_PRODUCTS')
@CacheTTL(30)
findAll() {
  return this.productsService.findAll();
}

// 动态键
// params参数
@Get(':id')
@CacheKey((req) => `PRODUCT_${req.params.id}`) 
findOne(@Param('id') id: string) {
  return this.productsService.findOne(id);
}

// query参数
@Get('search')
@CacheKey((req) => {
  const { q, page = '1' } = req.query;
  return `SEARCH_${q}_P${page}`;
})
search(@Query() query: { q: string; page?: string }) {
  return this.productsService.search(query);
}
  • CacheTTL设置缓存过期时间,毫秒。覆盖全局设置的ttl
@Get()
@CacheKey('empty')
@CacheTTL(60 * 1000)
async findAll(): Promise<number> {
    console.log("缓存", this.a, new Date().toLocaleTimeString())
    return this.a++;
}

@Get(':id')
@CacheTTL(30 * 1000) // 30秒缓存
findOne(@Param('id') id: string): string {
    console.log(`请求参数id ${id}`)
    return `请求参数id ${id}`
}

image.png

image.png

image.png

往期年度总结

往期文章

专栏文章

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论,    支持一下博主~

公众号:全栈追逐者,不定期的更新内容,关注不错过哦!