首页 > web前端 > js教程 > 通过 NestJS 中的高级缓存提高速度和性能:如何使用 AVL 树和 Redis

通过 NestJS 中的高级缓存提高速度和性能:如何使用 AVL 树和 Redis

DDD
发布: 2024-12-26 12:15:19
原创
597 人浏览过

Boosting Speed and Performance with Advanced Caching in NestJS: How to Use AVL Trees and Redis

在当今世界,响应请求的速度和效率对于大规模和高流量的系统至关重要。电子商务网站、社交网络和银行服务等在线平台面临着大量数据和用户请求。这种高要求不仅会给服务器和数据库带来巨大的负载,而且还会显着影响用户体验。在这种情况下,实施缓存系统可以成为提高性能和减少资源负载的有效解决方案。

在本文中,我们探索了结合 AVL 树和 Redis 的高级缓存系统的实现。该系统包括安全机制、TTL(生存时间)管理以及与 Redis 的集成,以增强性能和灵活性。目标是利用这两种技术的优点,同时减轻它们的弱点。

重要提示:本文是在人工智能的帮助下开发的。


基于AVL树的缓存系统与Redis相结合的优点和缺点

优点:

  1. 提高记忆效率:

    • 智能TTL管理:通过使用AVL树来管理数据过期,可以优化内存消耗,并防止保留陈旧数据。这在数据变化快、需要精确过期的场景下特别有用。
  2. 增强的安全性:

    • 令牌验证:添加基于令牌的验证机制增强了Redis的安全性。这个额外的安全层可以防止对缓存的未经授权的访问,从而增强整体系统的安全性。
  3. 高级 TTL 管理:

    • 自定义过期策略: AVL 树允许实施 Redis 可能不支持的更复杂和定制的过期策略。
  4. 多样化的数据结构:

    • 平衡树结​​构:作为一种平衡数据结构,与 Redis 的默认数据结构相比,AVL 树可以为某些需要快速搜索和排序的用例提供更好的性能。
  5. 增加灵活性和定制性:

    • 更好的定制:结合两个系统可以实现更广泛的定制,从而能够开发更精确和针对特定应用的解决方案。

缺点:

  1. 增加的架构复杂性:

    • 管理两个缓存系统:同时使用 Redis 和基于 AVL 树的缓存系统会增加架构复杂性,并且需要两个系统之间的协调管理。
  2. 增加的时间开销:

    • 额外延迟:添加额外的缓存层可能会导致延迟。必须确保性能优势超过这些潜在的延迟。
  3. 数据维护与同步:

    • 数据一致性:维护Redis和AVL树之间的一致性和同步对于防止数据差异至关重要,需要复杂的同步机制。
  4. 更高的开发和维护成本:

    • 费用增加:开发和维护两个缓存系统需要更多资源和多样化的专业知识,可能会增加总体项目成本。
  5. 安全复杂性:

    • 协调安全策略:确保在两个系统中正确且一致地实施安全策略可能具有挑战性。

利用AVL树和Redis实现缓存系统

下面,我们介绍一下这个缓存系统的专业实现。此实现包括用于管理具有 TTL 功能的数据的 AVL 树和用于快速数据存储的 Redis。

1.带有TTL的AVL树

首先,我们实现具有TTL管理能力的AVL树。

// src/utils/avltree.ts

export class AVLNode {
  key: string;
  value: any;
  ttl: number; // Expiration time in milliseconds
  height: number;
  left: AVLNode | null;
  right: AVLNode | null;

  constructor(key: string, value: any, ttl: number) {
    this.key = key;
    this.value = value;
    this.ttl = Date.now() + ttl;
    this.height = 1;
    this.left = null;
    this.right = null;
  }

  isExpired(): boolean {
    return Date.now() > this.ttl;
  }
}

export class AVLTree {
  private root: AVLNode | null;

  constructor() {
    this.root = null;
  }

  private getHeight(node: AVLNode | null): number {
    return node ? node.height : 0;
  }

  private updateHeight(node: AVLNode): void {
    node.height = 1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
  }

  private rotateRight(y: AVLNode): AVLNode {
    const x = y.left!;
    y.left = x.right;
    x.right = y;
    this.updateHeight(y);
    this.updateHeight(x);
    return x;
  }

  private rotateLeft(x: AVLNode): AVLNode {
    const y = x.right!;
    x.right = y.left;
    y.left = x;
    this.updateHeight(x);
    this.updateHeight(y);
    return y;
  }

  private getBalance(node: AVLNode): number {
    return node ? this.getHeight(node.left) - this.getHeight(node.right) : 0;
  }

  insert(key: string, value: any, ttl: number): void {
    this.root = this.insertNode(this.root, key, value, ttl);
  }

  private insertNode(node: AVLNode | null, key: string, value: any, ttl: number): AVLNode {
    if (!node) return new AVLNode(key, value, ttl);

    if (key < node.key) {
      node.left = this.insertNode(node.left, key, value, ttl);
    } else if (key > node.key) {
      node.right = this.insertNode(node.right, key, value, ttl);
    } else {
      node.value = value;
      node.ttl = Date.now() + ttl;
      return node;
    }

    this.updateHeight(node);
    const balance = this.getBalance(node);

    // Balancing the tree
    if (balance > 1 && key < node.left!.key) return this.rotateRight(node);
    if (balance < -1 && key > node.right!.key) return this.rotateLeft(node);
    if (balance > 1 && key > node.left!.key) {
      node.left = this.rotateLeft(node.left!);
      return this.rotateRight(node);
    }
    if (balance < -1 && key < node.right!.key) {
      node.right = this.rotateRight(node.right!);
      return this.rotateLeft(node);
    }

    return node;
  }

  search(key: string): any {
    let node = this.root;
    while (node) {
      if (node.isExpired()) {
        this.delete(key);
        return null;
      }
      if (key === node.key) return node.value;
      node = key < node.key ? node.left : node.right;
    }
    return null;
  }

  delete(key: string): void {
    this.root = this.deleteNode(this.root, key);
  }

  private deleteNode(node: AVLNode | null, key: string): AVLNode | null {
    if (!node) return null;

    if (key < node.key) {
      node.left = this.deleteNode(node.left, key);
    } else if (key > node.key) {
      node.right = this.deleteNode(node.right, key);
    } else {
      if (!node.left || !node.right) return node.left || node.right;
      let minLargerNode = node.right;
      while (minLargerNode.left) minLargerNode = minLargerNode.left;
      node.key = minLargerNode.key;
      node.value = minLargerNode.value;
      node.ttl = minLargerNode.ttl;
      node.right = this.deleteNode(node.right, minLargerNode.key);
    }

    this.updateHeight(node);
    const balance = this.getBalance(node);

    if (balance > 1 && this.getBalance(node.left!) >= 0) return this.rotateRight(node);
    if (balance < -1 && this.getBalance(node.right!) <= 0) return this.rotateLeft(node);
    if (balance > 1 && this.getBalance(node.left!) < 0) {
      node.left = this.rotateLeft(node.left!);
      return this.rotateRight(node);
    }
    if (balance < -1 && this.getBalance(node.right!) > 0) {
      node.right = this.rotateRight(node.right!);
      return this.rotateLeft(node);
    }

    return node;
  }
}
登录后复制
登录后复制

2. 与Redis集成的缓存服务(CacheService)

在本节中,我们实现了利用 AVL 树和 Redis 进行缓存管理的缓存服务。此外,我们还采用了令牌验证机制。

// src/cache/cache.service.ts

import { Injectable, UnauthorizedException, InternalServerErrorException } from '@nestjs/common';
import { AVLTree } from '../utils/avltree';
import { InjectRedis, Redis } from '@nestjs-modules/ioredis';

@Injectable()
export class CacheService {
  private avlTree: AVLTree;
  private authorizedTokens: Set<string> = new Set(['your_authorized_token']); // Authorized tokens

  constructor(@InjectRedis() private readonly redis: Redis) {
    this.avlTree = new AVLTree();
  }

  validateToken(token: string): void {
    if (!this.authorizedTokens.has(token)) {
      throw new UnauthorizedException('Invalid access token');
    }
  }

  async set(key: string, value: any, ttl: number, token: string): Promise<void> {
    this.validateToken(token);
    try {
      // Store in Redis
      await this.redis.set(key, JSON.stringify(value), 'PX', ttl);
      // Store in AVL Tree
      this.avlTree.insert(key, value, ttl);
    } catch (error) {
      throw new InternalServerErrorException('Failed to set cache');
    }
  }

  async get(key: string, token: string): Promise<any> {
    this.validateToken(token);
    try {
      // First, attempt to retrieve from Redis
      const redisValue = await this.redis.get(key);
      if (redisValue) {
        return JSON.parse(redisValue);
      }

      // If not found in Redis, retrieve from AVL Tree
      const avlValue = this.avlTree.search(key);
      if (avlValue) {
        // Re-store in Redis for faster access next time
        // Assuming the remaining TTL is maintained in AVL Tree
        // For simplicity, we set a new TTL
        const newTtl = 60000; // 60 seconds as an example
        await this.redis.set(key, JSON.stringify(avlValue), 'PX', newTtl);
        return avlValue;
      }

      return null;
    } catch (error) {
      throw new InternalServerErrorException('Failed to get cache');
    }
  }

  async delete(key: string, token: string): Promise<void> {
    this.validateToken(token);
    try {
      // Remove from Redis
      await this.redis.del(key);
      // Remove from AVL Tree
      this.avlTree.delete(key);
    } catch (error) {
      throw new InternalServerErrorException('Failed to delete cache');
    }
  }
}
登录后复制
登录后复制

3. API控制器(CacheController)

控制器管理对缓存服务的 API 请求。

// src/cache/cache.controller.ts

import { Controller, Get, Post, Delete, Body, Param, Query, HttpCode, HttpStatus } from '@nestjs/common';
import { CacheService } from './cache.service';

class SetCacheDto {
  key: string;
  value: any;
  ttl: number; // milliseconds
  token: string;
}

@Controller('cache')
export class CacheController {
  constructor(private readonly cacheService: CacheService) {}

  @Post('set')
  @HttpCode(HttpStatus.CREATED)
  async setCache(@Body() body: SetCacheDto) {
    await this.cacheService.set(body.key, body.value, body.ttl, body.token);
    return { message: 'Data cached successfully' };
  }

  @Get('get/:key')
  async getCache(@Param('key') key: string, @Query('token') token: string) {
    const value = await this.cacheService.get(key, token);
    return value ? { value } : { message: 'Key not found or expired' };
  }

  @Delete('delete/:key')
  @HttpCode(HttpStatus.NO_CONTENT)
  async deleteCache(@Param('key') key: string, @Query('token') token: string) {
    await this.cacheService.delete(key, token);
    return { message: 'Key deleted successfully' };
  }
}
登录后复制
登录后复制

4.缓存模块(CacheModule)

定义连接服务和控制器并注入Redis的缓存模块。

// src/cache/cache.module.ts

import { Module } from '@nestjs/common';
import { CacheService } from './cache.service';
import { CacheController } from './cache.controller';
import { RedisModule } from '@nestjs-modules/ioredis';

@Module({
  imports: [
    RedisModule.forRoot({
      config: {
        host: 'localhost',
        port: 6379,
        // Other Redis configurations
      },
    }),
  ],
  providers: [CacheService],
  controllers: [CacheController],
})
export class CacheModule {}
登录后复制
登录后复制

5.Redis配置

要在 NestJS 项目中使用 Redis,我们使用 @nestjs-modules/ioredis 包。首先,安装软件包:

npm install @nestjs-modules/ioredis ioredis
登录后复制
登录后复制

然后,在CacheModule中配置Redis,如上所示。如果您需要更高级的配置,可以使用单独的配置文件。

6. 代币验证机制

为了管理和验证代币,可以采用各种策略。在这个简单的实现中,令牌被维护在一个固定的集合中。对于较大的项目,建议使用 JWT(JSON Web Tokens)或其他高级安全方法。

7. 错误处理和输入验证

在此实现中,DTO(数据传输对象)类用于输入验证和错误管理。此外,缓存服务利用一般错误处理来防止意外问题。

8. 主应用模块(AppModule)

最后,我们将缓存模块添加到主应用程序模块中。

// src/utils/avltree.ts

export class AVLNode {
  key: string;
  value: any;
  ttl: number; // Expiration time in milliseconds
  height: number;
  left: AVLNode | null;
  right: AVLNode | null;

  constructor(key: string, value: any, ttl: number) {
    this.key = key;
    this.value = value;
    this.ttl = Date.now() + ttl;
    this.height = 1;
    this.left = null;
    this.right = null;
  }

  isExpired(): boolean {
    return Date.now() > this.ttl;
  }
}

export class AVLTree {
  private root: AVLNode | null;

  constructor() {
    this.root = null;
  }

  private getHeight(node: AVLNode | null): number {
    return node ? node.height : 0;
  }

  private updateHeight(node: AVLNode): void {
    node.height = 1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
  }

  private rotateRight(y: AVLNode): AVLNode {
    const x = y.left!;
    y.left = x.right;
    x.right = y;
    this.updateHeight(y);
    this.updateHeight(x);
    return x;
  }

  private rotateLeft(x: AVLNode): AVLNode {
    const y = x.right!;
    x.right = y.left;
    y.left = x;
    this.updateHeight(x);
    this.updateHeight(y);
    return y;
  }

  private getBalance(node: AVLNode): number {
    return node ? this.getHeight(node.left) - this.getHeight(node.right) : 0;
  }

  insert(key: string, value: any, ttl: number): void {
    this.root = this.insertNode(this.root, key, value, ttl);
  }

  private insertNode(node: AVLNode | null, key: string, value: any, ttl: number): AVLNode {
    if (!node) return new AVLNode(key, value, ttl);

    if (key < node.key) {
      node.left = this.insertNode(node.left, key, value, ttl);
    } else if (key > node.key) {
      node.right = this.insertNode(node.right, key, value, ttl);
    } else {
      node.value = value;
      node.ttl = Date.now() + ttl;
      return node;
    }

    this.updateHeight(node);
    const balance = this.getBalance(node);

    // Balancing the tree
    if (balance > 1 && key < node.left!.key) return this.rotateRight(node);
    if (balance < -1 && key > node.right!.key) return this.rotateLeft(node);
    if (balance > 1 && key > node.left!.key) {
      node.left = this.rotateLeft(node.left!);
      return this.rotateRight(node);
    }
    if (balance < -1 && key < node.right!.key) {
      node.right = this.rotateRight(node.right!);
      return this.rotateLeft(node);
    }

    return node;
  }

  search(key: string): any {
    let node = this.root;
    while (node) {
      if (node.isExpired()) {
        this.delete(key);
        return null;
      }
      if (key === node.key) return node.value;
      node = key < node.key ? node.left : node.right;
    }
    return null;
  }

  delete(key: string): void {
    this.root = this.deleteNode(this.root, key);
  }

  private deleteNode(node: AVLNode | null, key: string): AVLNode | null {
    if (!node) return null;

    if (key < node.key) {
      node.left = this.deleteNode(node.left, key);
    } else if (key > node.key) {
      node.right = this.deleteNode(node.right, key);
    } else {
      if (!node.left || !node.right) return node.left || node.right;
      let minLargerNode = node.right;
      while (minLargerNode.left) minLargerNode = minLargerNode.left;
      node.key = minLargerNode.key;
      node.value = minLargerNode.value;
      node.ttl = minLargerNode.ttl;
      node.right = this.deleteNode(node.right, minLargerNode.key);
    }

    this.updateHeight(node);
    const balance = this.getBalance(node);

    if (balance > 1 && this.getBalance(node.left!) >= 0) return this.rotateRight(node);
    if (balance < -1 && this.getBalance(node.right!) <= 0) return this.rotateLeft(node);
    if (balance > 1 && this.getBalance(node.left!) < 0) {
      node.left = this.rotateLeft(node.left!);
      return this.rotateRight(node);
    }
    if (balance < -1 && this.getBalance(node.right!) > 0) {
      node.right = this.rotateRight(node.right!);
      return this.rotateLeft(node);
    }

    return node;
  }
}
登录后复制
登录后复制

9. 主应用程序文件(main.ts)

引导 NestJS 的主应用程序文件。

// src/cache/cache.service.ts

import { Injectable, UnauthorizedException, InternalServerErrorException } from '@nestjs/common';
import { AVLTree } from '../utils/avltree';
import { InjectRedis, Redis } from '@nestjs-modules/ioredis';

@Injectable()
export class CacheService {
  private avlTree: AVLTree;
  private authorizedTokens: Set<string> = new Set(['your_authorized_token']); // Authorized tokens

  constructor(@InjectRedis() private readonly redis: Redis) {
    this.avlTree = new AVLTree();
  }

  validateToken(token: string): void {
    if (!this.authorizedTokens.has(token)) {
      throw new UnauthorizedException('Invalid access token');
    }
  }

  async set(key: string, value: any, ttl: number, token: string): Promise<void> {
    this.validateToken(token);
    try {
      // Store in Redis
      await this.redis.set(key, JSON.stringify(value), 'PX', ttl);
      // Store in AVL Tree
      this.avlTree.insert(key, value, ttl);
    } catch (error) {
      throw new InternalServerErrorException('Failed to set cache');
    }
  }

  async get(key: string, token: string): Promise<any> {
    this.validateToken(token);
    try {
      // First, attempt to retrieve from Redis
      const redisValue = await this.redis.get(key);
      if (redisValue) {
        return JSON.parse(redisValue);
      }

      // If not found in Redis, retrieve from AVL Tree
      const avlValue = this.avlTree.search(key);
      if (avlValue) {
        // Re-store in Redis for faster access next time
        // Assuming the remaining TTL is maintained in AVL Tree
        // For simplicity, we set a new TTL
        const newTtl = 60000; // 60 seconds as an example
        await this.redis.set(key, JSON.stringify(avlValue), 'PX', newTtl);
        return avlValue;
      }

      return null;
    } catch (error) {
      throw new InternalServerErrorException('Failed to get cache');
    }
  }

  async delete(key: string, token: string): Promise<void> {
    this.validateToken(token);
    try {
      // Remove from Redis
      await this.redis.del(key);
      // Remove from AVL Tree
      this.avlTree.delete(key);
    } catch (error) {
      throw new InternalServerErrorException('Failed to delete cache');
    }
  }
}
登录后复制
登录后复制

10. 测试和运行应用程序

实现所有组件后,您可以运行应用程序以确保其功能。

// src/cache/cache.controller.ts

import { Controller, Get, Post, Delete, Body, Param, Query, HttpCode, HttpStatus } from '@nestjs/common';
import { CacheService } from './cache.service';

class SetCacheDto {
  key: string;
  value: any;
  ttl: number; // milliseconds
  token: string;
}

@Controller('cache')
export class CacheController {
  constructor(private readonly cacheService: CacheService) {}

  @Post('set')
  @HttpCode(HttpStatus.CREATED)
  async setCache(@Body() body: SetCacheDto) {
    await this.cacheService.set(body.key, body.value, body.ttl, body.token);
    return { message: 'Data cached successfully' };
  }

  @Get('get/:key')
  async getCache(@Param('key') key: string, @Query('token') token: string) {
    const value = await this.cacheService.get(key, token);
    return value ? { value } : { message: 'Key not found or expired' };
  }

  @Delete('delete/:key')
  @HttpCode(HttpStatus.NO_CONTENT)
  async deleteCache(@Param('key') key: string, @Query('token') token: string) {
    await this.cacheService.delete(key, token);
    return { message: 'Key deleted successfully' };
  }
}
登录后复制
登录后复制

11. 样品请求

设置缓存:

// src/cache/cache.module.ts

import { Module } from '@nestjs/common';
import { CacheService } from './cache.service';
import { CacheController } from './cache.controller';
import { RedisModule } from '@nestjs-modules/ioredis';

@Module({
  imports: [
    RedisModule.forRoot({
      config: {
        host: 'localhost',
        port: 6379,
        // Other Redis configurations
      },
    }),
  ],
  providers: [CacheService],
  controllers: [CacheController],
})
export class CacheModule {}
登录后复制
登录后复制

获取缓存:

npm install @nestjs-modules/ioredis ioredis
登录后复制
登录后复制

删除缓存:

// src/app.module.ts

import { Module } from '@nestjs/common';
import { CacheModule } from './cache/cache.module';

@Module({
  imports: [CacheModule],
  controllers: [],
  providers: [],
})
export class AppModule {}
登录后复制

结合 Redis 和 AVL 基于树的缓存系统的合适用例

  1. 银行和金融系统:

    • 管理敏感会话和交易:高安全性和精确的 TTL 管理对于敏感的财务数据至关重要。将令牌安全性和智能 TTL 管理相结合在此领域非常有益。
  2. 高流量电商平台:

    • 存储产品数据和管理购物车:优化内存和提高数据访问速度对于增强亚马逊等大型在线商店的用户体验至关重要。
  3. 消息和社交网络应用程序:

    • 存储实时用户状态:需要快速访问和精确的数据管理来显示用户的在线/离线状态和消息。
  4. 天气和货币兑换应用程序:

    • API 缓存以减少请求负载:通过精确的过期管理存储复杂计算的结果和实时数据,为用户提供最新且快速的信息。
  5. 内容管理系统和媒体平台:

    • 缓存高流量页面和内容:优化对高浏览量内容的访问并减少服务器负载,以提供更流畅的用户体验。
  6. 分析应用程序和实时仪表板:

    • 存储即时分析结果:使用多个缓存提供快速且最新的分析数据,以提高性能和结果准确性。

结论

在本文中,我们在 NestJS 框架内使用 AVL 树和 Redis 实现了高级缓存系统。该系统提供先进的 TTL 管理、基于令牌的安全性和 Redis 集成,为高需求应用程序提供了强大且灵活的解决方案。这两种技术的结合利用了两者的优势,解决了 Redis 的弱点并增强了整体缓存性能。

以上是通过 NestJS 中的高级缓存提高速度和性能:如何使用 AVL 树和 Redis的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板