import { Injectable, Inject, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import type { Cache } from 'cache-manager'; import { Redis } from 'ioredis'; import { CacheService } from './cacheService'; export interface MultiLevelCacheOptions { l1Ttl?: number; // L1 缓存时间(秒) l2Ttl?: number; // L2 缓存时间(秒) prefix?: string; } @Injectable() export class MultiLevelCacheService { private readonly logger = new Logger(MultiLevelCacheService.name); private readonly appPrefix = 'wwjcloud'; // 使用固定前缀,避免硬编码 constructor( @Inject(CACHE_MANAGER) private l1Cache: Cache, // 内存缓存 @Inject('REDIS_CLIENT') private l2Cache: Redis, // Redis 缓存 private cacheService: CacheService, ) {} /** * 获取缓存(多级) */ async get(key: string, options?: MultiLevelCacheOptions): Promise { try { const fullKey = this.buildKey(key, options?.prefix); // L1: 内存缓存 const l1Value = await this.l1Cache.get(fullKey); if (l1Value !== undefined && l1Value !== null) { this.logger.debug(`L1 cache hit: ${fullKey}`); return l1Value as T; } // L2: Redis 缓存 const l2Value = await this.l2Cache.get(fullKey); if (l2Value !== null && l2Value !== undefined) { const parsedValue = JSON.parse(l2Value) as T; // 回填到 L1 缓存 await this.l1Cache.set(fullKey, parsedValue, options?.l1Ttl || 60); this.logger.debug(`L2 cache hit: ${fullKey}`); return parsedValue; } this.logger.debug(`Cache miss: ${fullKey}`); return null; } catch (error) { this.logger.error(`Cache get error: ${error.message}`, error.stack); return null; } } /** * 设置缓存(多级) */ async set(key: string, value: T, options?: MultiLevelCacheOptions): Promise { const fullKey = this.buildKey(key, options?.prefix); try { // 并行设置 L1 和 L2 缓存 await Promise.all([ this.l1Cache.set(fullKey, value, options?.l1Ttl || 60), this.l2Cache.setex(fullKey, options?.l2Ttl || 300, JSON.stringify(value)), ]); this.logger.debug(`Multi-level cache set: ${fullKey}`); } catch (error) { this.logger.error(`Multi-level cache set error: ${error.message}`, error.stack); } } /** * 删除缓存(多级) */ async del(key: string, options?: MultiLevelCacheOptions): Promise { const fullKey = this.buildKey(key, options?.prefix); try { // 并行删除 L1 和 L2 缓存 await Promise.all([ this.l1Cache.del(fullKey), this.l2Cache.del(fullKey), ]); this.logger.debug(`Multi-level cache del: ${fullKey}`); } catch (error) { this.logger.error(`Multi-level cache del error: ${error.message}`, error.stack); } } /** * 批量删除缓存 */ async delPattern(pattern: string): Promise { try { // 获取匹配的键 const keys = await this.l2Cache.keys(pattern); if (keys.length > 0) { // 删除 L2 缓存 await this.l2Cache.del(...keys); // 删除 L1 缓存 const l1Promises = keys.map(key => this.l1Cache.del(key)); await Promise.allSettled(l1Promises); this.logger.debug(`Multi-level cache del pattern: ${pattern}, deleted ${keys.length} keys`); } } catch (error) { this.logger.error(`Multi-level cache del pattern error: ${error.message}`, error.stack); } } /** * 获取或设置缓存(缓存穿透保护) */ async getOrSet( key: string, factory: () => Promise, options?: MultiLevelCacheOptions ): Promise { const fullKey = this.buildKey(key, options?.prefix); try { // 先尝试获取缓存 let value = await this.get(fullKey, options); if (value !== null) { return value; } // 缓存未命中,执行工厂函数 value = await factory(); // 设置缓存 await this.set(key, value, options); return value; } catch (error) { this.logger.error(`Multi-level cache getOrSet error: ${error.message}`, error.stack); throw error; } } /** * 预热缓存 */ async warmup( keys: string[], factory: (key: string) => Promise, options?: MultiLevelCacheOptions ): Promise { this.logger.log(`Starting cache warmup for ${keys.length} keys`); const promises = keys.map(async (key) => { try { const value = await factory(key); await this.set(key, value, options); this.logger.debug(`Cache warmed up: ${key}`); } catch (error) { this.logger.error(`Cache warmup failed for key ${key}: ${error.message}`); } }); await Promise.allSettled(promises); this.logger.log('Cache warmup completed'); } /** * 获取缓存统计信息 */ async getStats(): Promise<{ l1Stats: { size: number; hitRate: number }; l2Stats: { size: number; hitRate: number }; totalHitRate: number; }> { try { // 获取 L2 缓存统计 const l2Info = await this.l2Cache.info('memory'); const l2Keys = await this.l2Cache.dbsize(); // 解析 L2 内存使用 const memoryMatch = l2Info.match(/used_memory_human:(\S+)/); const l2Memory = memoryMatch ? memoryMatch[1] : '0B'; return { l1Stats: { size: 0, // 需要实现 L1 缓存大小统计 hitRate: 0, // 需要实现命中率统计 }, l2Stats: { size: this.parseMemoryUsage(l2Memory), hitRate: 0, // 需要实现命中率统计 }, totalHitRate: 0, // 需要实现总命中率统计 }; } catch (error) { this.logger.error(`Multi-level cache stats error: ${error.message}`, error.stack); return { l1Stats: { size: 0, hitRate: 0 }, l2Stats: { size: 0, hitRate: 0 }, totalHitRate: 0, }; } } /** * 清空所有缓存 */ async clear(): Promise { try { // 直接使用 Redis 的 FLUSHDB 命令清空 L2 缓存 // L1 缓存会在下次访问时自动失效 await this.l2Cache.flushdb(); this.logger.debug('Multi-level cache cleared'); } catch (error) { this.logger.error(`Multi-level cache clear error: ${error.message}`, error.stack); } } /** * 构建缓存键 */ private buildKey(key: string, prefix?: string): string { const finalPrefix = prefix ? `${this.appPrefix}:ml:${prefix}` : `${this.appPrefix}:ml`; return `${finalPrefix}:${key}`; } /** * 解析内存使用量 */ private parseMemoryUsage(memoryStr: string): number { const match = memoryStr.match(/^(\d+(?:\.\d+)?)([KMGT]?B)$/); if (!match) return 0; const [, value, unit] = match; const numValue = parseFloat(value); switch (unit) { case 'KB': return numValue * 1024; case 'MB': return numValue * 1024 * 1024; case 'GB': return numValue * 1024 * 1024 * 1024; case 'TB': return numValue * 1024 * 1024 * 1024 * 1024; default: return numValue; } } }