advanced-cache.ts•16.4 kB
/**
* Advanced Caching System
* 高级缓存优化系统
*
* 功能:
* - 智能缓存策略
* - 缓存预热
* - 缓存失效机制
* - 多层缓存架构
* - 缓存性能监控
*/
import { Logger } from '../utils/logger.js';
import { SearchResult, EnhancedSearchResult } from '../types.js';
import * as fs from 'fs/promises';
import * as path from 'path';
import * as crypto from 'crypto';
export interface CacheConfig {
// 缓存策略
strategy: 'lru' | 'lfu' | 'ttl' | 'adaptive';
// 容量配置
maxMemoryItems: number; // 内存缓存最大条目数
maxDiskSize: number; // 磁盘缓存最大大小 (MB)
// 时间配置
defaultTTL: number; // 默认TTL (毫秒)
maxTTL: number; // 最大TTL (毫秒)
minTTL: number; // 最小TTL (毫秒)
// 预热配置
preWarmEnabled: boolean; // 启用缓存预热
preWarmQueries: string[]; // 预热查询列表
// 性能配置
compressionEnabled: boolean; // 启用压缩
encryptionEnabled: boolean; // 启用加密
// 目录配置
cacheDir: string; // 缓存目录
}
export interface CacheEntry {
key: string;
data: any;
timestamp: number;
ttl: number;
accessCount: number;
lastAccessed: number;
size: number;
compressed: boolean;
encrypted: boolean;
metadata: {
query: string;
source: string;
resultCount: number;
quality: number;
};
}
export interface CacheStats {
// 基础统计
totalEntries: number;
memoryEntries: number;
diskEntries: number;
// 大小统计
memorySize: number;
diskSize: number;
totalSize: number;
// 性能统计
hitRate: number;
missRate: number;
totalRequests: number;
totalHits: number;
totalMisses: number;
// 时间统计
averageAccessTime: number;
averageWriteTime: number;
// 策略统计
evictions: number;
expirations: number;
// 时间范围
since: Date;
}
export class AdvancedCache {
private logger: Logger;
private config: CacheConfig;
// 内存缓存
private memoryCache = new Map<string, CacheEntry>();
// 访问统计
private accessStats = {
requests: 0,
hits: 0,
misses: 0,
accessTimes: [] as number[],
writeTimes: [] as number[],
evictions: 0,
expirations: 0,
startTime: new Date()
};
// LRU 访问顺序
private accessOrder: string[] = [];
// LFU 频率计数
private frequencyCount = new Map<string, number>();
constructor(config?: Partial<CacheConfig>) {
this.logger = new Logger('AdvancedCache');
this.config = {
strategy: 'adaptive',
maxMemoryItems: 1000,
maxDiskSize: 500, // 500MB
defaultTTL: 60 * 60 * 1000, // 1小时
maxTTL: 24 * 60 * 60 * 1000, // 24小时
minTTL: 5 * 60 * 1000, // 5分钟
preWarmEnabled: true,
preWarmQueries: [
'artificial intelligence',
'machine learning',
'deep learning',
'neural networks',
'natural language processing'
],
compressionEnabled: true,
encryptionEnabled: false,
cacheDir: './cache',
...config
};
this.initializeCache();
}
/**
* 获取缓存数据
*/
async get(key: string): Promise<any | null> {
const startTime = performance.now();
this.accessStats.requests++;
try {
// 生成缓存键
const cacheKey = this.generateCacheKey(key);
// 首先检查内存缓存
const memoryEntry = this.memoryCache.get(cacheKey);
if (memoryEntry && this.isEntryValid(memoryEntry)) {
this.updateAccessStats(memoryEntry);
this.accessStats.hits++;
const accessTime = performance.now() - startTime;
this.accessStats.accessTimes.push(accessTime);
this.logger.debug(`Cache hit (memory): ${key}`);
return this.deserializeData(memoryEntry.data, memoryEntry);
}
// 检查磁盘缓存
const diskEntry = await this.getDiskEntry(cacheKey);
if (diskEntry && this.isEntryValid(diskEntry)) {
// 将热点数据提升到内存缓存
this.promoteToMemory(diskEntry);
this.updateAccessStats(diskEntry);
this.accessStats.hits++;
const accessTime = performance.now() - startTime;
this.accessStats.accessTimes.push(accessTime);
this.logger.debug(`Cache hit (disk): ${key}`);
return this.deserializeData(diskEntry.data, diskEntry);
}
// 缓存未命中
this.accessStats.misses++;
this.logger.debug(`Cache miss: ${key}`);
return null;
} catch (error) {
this.logger.error(`Cache get error for ${key}:`, error);
this.accessStats.misses++;
return null;
}
}
/**
* 设置缓存数据
*/
async set(
key: string,
data: any,
options?: {
ttl?: number;
metadata?: Partial<CacheEntry['metadata']>;
priority?: 'low' | 'normal' | 'high';
}
): Promise<void> {
const startTime = performance.now();
try {
const cacheKey = this.generateCacheKey(key);
const ttl = options?.ttl || this.calculateAdaptiveTTL(data, options?.metadata);
const entry: CacheEntry = {
key: cacheKey,
data: await this.serializeData(data),
timestamp: Date.now(),
ttl,
accessCount: 1,
lastAccessed: Date.now(),
size: this.calculateDataSize(data),
compressed: this.config.compressionEnabled,
encrypted: this.config.encryptionEnabled,
metadata: {
query: key,
source: 'unknown',
resultCount: Array.isArray(data) ? data.length : 1,
quality: 0.8,
...options?.metadata
}
};
// 根据优先级决定存储策略
const priority = options?.priority || 'normal';
if (priority === 'high' || this.shouldStoreInMemory(entry)) {
await this.setMemoryEntry(cacheKey, entry);
} else {
await this.setDiskEntry(cacheKey, entry);
}
const writeTime = performance.now() - startTime;
this.accessStats.writeTimes.push(writeTime);
this.logger.debug(`Cache set: ${key} (TTL: ${ttl}ms, Size: ${entry.size} bytes)`);
} catch (error) {
this.logger.error(`Cache set error for ${key}:`, error);
}
}
/**
* 删除缓存条目
*/
async delete(key: string): Promise<boolean> {
try {
const cacheKey = this.generateCacheKey(key);
// 从内存缓存删除
const memoryDeleted = this.memoryCache.delete(cacheKey);
this.removeFromAccessOrder(cacheKey);
this.frequencyCount.delete(cacheKey);
// 从磁盘缓存删除
const diskDeleted = await this.deleteDiskEntry(cacheKey);
this.logger.debug(`Cache delete: ${key}`);
return memoryDeleted || diskDeleted;
} catch (error) {
this.logger.error(`Cache delete error for ${key}:`, error);
return false;
}
}
/**
* 清空缓存
*/
async clear(): Promise<void> {
try {
// 清空内存缓存
this.memoryCache.clear();
this.accessOrder = [];
this.frequencyCount.clear();
// 清空磁盘缓存
await this.clearDiskCache();
// 重置统计
this.accessStats = {
requests: 0,
hits: 0,
misses: 0,
accessTimes: [],
writeTimes: [],
evictions: 0,
expirations: 0,
startTime: new Date()
};
this.logger.info('Cache cleared');
} catch (error) {
this.logger.error('Cache clear error:', error);
}
}
/**
* 获取缓存统计信息
*/
getStats(): CacheStats {
const memorySize = Array.from(this.memoryCache.values())
.reduce((total, entry) => total + entry.size, 0);
return {
totalEntries: this.memoryCache.size,
memoryEntries: this.memoryCache.size,
diskEntries: 0, // TODO: 实现磁盘条目计数
memorySize,
diskSize: 0, // TODO: 实现磁盘大小计算
totalSize: memorySize,
hitRate: this.accessStats.requests > 0 ?
this.accessStats.hits / this.accessStats.requests : 0,
missRate: this.accessStats.requests > 0 ?
this.accessStats.misses / this.accessStats.requests : 0,
totalRequests: this.accessStats.requests,
totalHits: this.accessStats.hits,
totalMisses: this.accessStats.misses,
averageAccessTime: this.accessStats.accessTimes.length > 0 ?
this.accessStats.accessTimes.reduce((a, b) => a + b, 0) / this.accessStats.accessTimes.length : 0,
averageWriteTime: this.accessStats.writeTimes.length > 0 ?
this.accessStats.writeTimes.reduce((a, b) => a + b, 0) / this.accessStats.writeTimes.length : 0,
evictions: this.accessStats.evictions,
expirations: this.accessStats.expirations,
since: this.accessStats.startTime
};
}
/**
* 缓存预热
*/
async preWarm(): Promise<void> {
if (!this.config.preWarmEnabled) return;
this.logger.info('Starting cache pre-warming...');
for (const query of this.config.preWarmQueries) {
try {
// 这里应该调用实际的搜索逻辑来预热缓存
// 暂时跳过实际实现
this.logger.debug(`Pre-warming cache for query: ${query}`);
} catch (error) {
this.logger.warn(`Pre-warm failed for query ${query}:`, error);
}
}
this.logger.info('Cache pre-warming completed');
}
// 私有方法
private async initializeCache(): Promise<void> {
try {
// 创建缓存目录
await fs.mkdir(this.config.cacheDir, { recursive: true });
// 启动清理任务
this.startCleanupTask();
// 启动预热
if (this.config.preWarmEnabled) {
setTimeout(() => this.preWarm(), 5000); // 5秒后开始预热
}
this.logger.info('Advanced cache initialized');
} catch (error) {
this.logger.error('Cache initialization error:', error);
}
}
private generateCacheKey(key: string): string {
return crypto.createHash('sha256').update(key).digest('hex');
}
private isEntryValid(entry: CacheEntry): boolean {
const now = Date.now();
return (now - entry.timestamp) < entry.ttl;
}
private updateAccessStats(entry: CacheEntry): void {
entry.accessCount++;
entry.lastAccessed = Date.now();
// 更新LRU顺序
this.updateAccessOrder(entry.key);
// 更新LFU频率
this.frequencyCount.set(entry.key, (this.frequencyCount.get(entry.key) || 0) + 1);
}
private calculateAdaptiveTTL(data: any, metadata?: Partial<CacheEntry['metadata']>): number {
let ttl = this.config.defaultTTL;
// 根据数据质量调整TTL
if (metadata?.quality) {
if (metadata.quality > 0.9) {
ttl = Math.min(ttl * 2, this.config.maxTTL); // 高质量数据缓存更久
} else if (metadata.quality < 0.5) {
ttl = Math.max(ttl / 2, this.config.minTTL); // 低质量数据缓存较短
}
}
// 根据结果数量调整TTL
if (metadata?.resultCount) {
if (metadata.resultCount > 10) {
ttl = Math.min(ttl * 1.5, this.config.maxTTL); // 丰富结果缓存更久
}
}
return ttl;
}
private shouldStoreInMemory(entry: CacheEntry): boolean {
// 高质量、小尺寸的数据优先存储在内存
return entry.metadata.quality > 0.8 && entry.size < 10000; // 10KB
}
private async setMemoryEntry(key: string, entry: CacheEntry): Promise<void> {
// 检查内存容量
if (this.memoryCache.size >= this.config.maxMemoryItems) {
await this.evictMemoryEntry();
}
this.memoryCache.set(key, entry);
this.updateAccessOrder(key);
}
private async evictMemoryEntry(): Promise<void> {
let keyToEvict: string | null = null;
switch (this.config.strategy) {
case 'lru':
keyToEvict = this.accessOrder[0];
break;
case 'lfu':
keyToEvict = this.findLFUKey();
break;
case 'ttl':
keyToEvict = this.findExpiredKey();
break;
case 'adaptive':
keyToEvict = this.findAdaptiveEvictionKey();
break;
}
if (keyToEvict) {
const entry = this.memoryCache.get(keyToEvict);
if (entry) {
// 将被驱逐的条目移到磁盘
await this.setDiskEntry(keyToEvict, entry);
}
this.memoryCache.delete(keyToEvict);
this.removeFromAccessOrder(keyToEvict);
this.accessStats.evictions++;
}
}
private updateAccessOrder(key: string): void {
// 移除旧位置
this.removeFromAccessOrder(key);
// 添加到末尾(最近访问)
this.accessOrder.push(key);
}
private removeFromAccessOrder(key: string): void {
const index = this.accessOrder.indexOf(key);
if (index > -1) {
this.accessOrder.splice(index, 1);
}
}
private findLFUKey(): string | null {
let minFreq = Infinity;
let keyToEvict: string | null = null;
for (const [key, freq] of this.frequencyCount.entries()) {
if (freq < minFreq) {
minFreq = freq;
keyToEvict = key;
}
}
return keyToEvict;
}
private findExpiredKey(): string | null {
const now = Date.now();
for (const [key, entry] of this.memoryCache.entries()) {
if (!this.isEntryValid(entry)) {
return key;
}
}
return null;
}
private findAdaptiveEvictionKey(): string | null {
// 综合考虑访问频率、时间和质量
let bestScore = -1;
let keyToEvict: string | null = null;
for (const [key, entry] of this.memoryCache.entries()) {
const freq = this.frequencyCount.get(key) || 1;
const age = Date.now() - entry.lastAccessed;
const quality = entry.metadata.quality;
// 计算驱逐分数(越高越应该被驱逐)
const score = age / (freq * quality * 1000);
if (score > bestScore) {
bestScore = score;
keyToEvict = key;
}
}
return keyToEvict;
}
private async serializeData(data: any): Promise<string> {
let serialized = JSON.stringify(data);
if (this.config.compressionEnabled) {
// TODO: 实现压缩
}
if (this.config.encryptionEnabled) {
// TODO: 实现加密
}
return serialized;
}
private async deserializeData(data: string, entry: CacheEntry): Promise<any> {
let deserialized = data;
if (entry.encrypted) {
// TODO: 实现解密
}
if (entry.compressed) {
// TODO: 实现解压缩
}
return JSON.parse(deserialized);
}
private calculateDataSize(data: any): number {
return JSON.stringify(data).length * 2; // 粗略估算(UTF-16)
}
private promoteToMemory(entry: CacheEntry): void {
if (this.memoryCache.size < this.config.maxMemoryItems) {
this.memoryCache.set(entry.key, entry);
this.updateAccessOrder(entry.key);
}
}
// 磁盘缓存方法(简化实现)
private async getDiskEntry(key: string): Promise<CacheEntry | null> {
// TODO: 实现磁盘缓存读取
return null;
}
private async setDiskEntry(key: string, entry: CacheEntry): Promise<void> {
// TODO: 实现磁盘缓存写入
}
private async deleteDiskEntry(key: string): Promise<boolean> {
// TODO: 实现磁盘缓存删除
return false;
}
private async clearDiskCache(): Promise<void> {
// TODO: 实现磁盘缓存清空
}
private startCleanupTask(): void {
// 每10分钟清理一次过期条目
setInterval(() => {
this.cleanupExpiredEntries();
}, 10 * 60 * 1000);
}
private cleanupExpiredEntries(): void {
const now = Date.now();
let cleanedCount = 0;
for (const [key, entry] of this.memoryCache.entries()) {
if (!this.isEntryValid(entry)) {
this.memoryCache.delete(key);
this.removeFromAccessOrder(key);
this.frequencyCount.delete(key);
cleanedCount++;
this.accessStats.expirations++;
}
}
if (cleanedCount > 0) {
this.logger.debug(`Cleaned up ${cleanedCount} expired cache entries`);
}
}
}