缓存基础
概述
缓存是提升数据库应用性能的关键技术,通过将频繁访问的数据存储在更快的存储介质中,减少对数据库的直接访问,从而提高响应速度和系统吞吐量。
缓存策略
1. 缓存读取模式
javascript
// Cache-Aside 模式
class CacheAsidePattern {
constructor(cache, database) {
this.cache = cache;
this.database = database;
}
async get(key) {
// 先从缓存读取
let data = await this.cache.get(key);
if (data === null) {
// 缓存未命中,从数据库读取
data = await this.database.get(key);
if (data !== null) {
// 写入缓存
await this.cache.set(key, data, 3600); // 1小时过期
}
}
return data;
}
async set(key, data) {
// 更新数据库
await this.database.set(key, data);
// 删除缓存,让下次读取时重新加载
await this.cache.delete(key);
}
}
// Read-Through 模式
class ReadThroughPattern {
constructor(cache, database) {
this.cache = cache;
this.database = database;
}
async get(key) {
return await this.cache.getWithLoader(key, async (key) => {
return await this.database.get(key);
});
}
}
// Write-Through 模式
class WriteThroughPattern {
constructor(cache, database) {
this.cache = cache;
this.database = database;
}
async set(key, data) {
// 同时写入缓存和数据库
await Promise.all([
this.cache.set(key, data),
this.database.set(key, data)
]);
}
}
// Write-Behind 模式
class WriteBehindPattern {
constructor(cache, database) {
this.cache = cache;
this.database = database;
this.writeQueue = [];
this.flushInterval = 5000; // 5秒
this.startBatchWrite();
}
async set(key, data) {
// 立即写入缓存
await this.cache.set(key, data);
// 加入写队列
this.writeQueue.push({ key, data, timestamp: Date.now() });
}
startBatchWrite() {
setInterval(async () => {
if (this.writeQueue.length > 0) {
const batch = this.writeQueue.splice(0);
await this.flushToDatabase(batch);
}
}, this.flushInterval);
}
async flushToDatabase(batch) {
try {
for (const item of batch) {
await this.database.set(item.key, item.data);
}
console.log(`批量写入 ${batch.length} 条数据到数据库`);
} catch (error) {
console.error('批量写入失败:', error);
// 重新加入队列重试
this.writeQueue.unshift(...batch);
}
}
}
2. 缓存失效策略
javascript
// 缓存失效管理器
class CacheInvalidationManager {
constructor(cache) {
this.cache = cache;
this.taggedKeys = new Map(); // 标签到键的映射
this.keyTags = new Map(); // 键到标签的映射
}
// 设置带标签的缓存
async setWithTags(key, data, ttl, tags = []) {
await this.cache.set(key, data, ttl);
// 记录标签关系
this.keyTags.set(key, tags);
tags.forEach(tag => {
if (!this.taggedKeys.has(tag)) {
this.taggedKeys.set(tag, new Set());
}
this.taggedKeys.get(tag).add(key);
});
}
// 按标签失效缓存
async invalidateByTag(tag) {
const keys = this.taggedKeys.get(tag);
if (!keys) return 0;
let invalidatedCount = 0;
for (const key of keys) {
await this.cache.delete(key);
this.removeKeyFromTags(key);
invalidatedCount++;
}
this.taggedKeys.delete(tag);
return invalidatedCount;
}
// 按模式失效缓存
async invalidateByPattern(pattern) {
const keys = await this.cache.keys(pattern);
let invalidatedCount = 0;
for (const key of keys) {
await this.cache.delete(key);
this.removeKeyFromTags(key);
invalidatedCount++;
}
return invalidatedCount;
}
// 时间基础失效
async invalidateExpired() {
const expiredKeys = await this.findExpiredKeys();
let invalidatedCount = 0;
for (const key of expiredKeys) {
await this.cache.delete(key);
this.removeKeyFromTags(key);
invalidatedCount++;
}
return invalidatedCount;
}
removeKeyFromTags(key) {
const tags = this.keyTags.get(key);
if (tags) {
tags.forEach(tag => {
const taggedKeys = this.taggedKeys.get(tag);
if (taggedKeys) {
taggedKeys.delete(key);
if (taggedKeys.size === 0) {
this.taggedKeys.delete(tag);
}
}
});
this.keyTags.delete(key);
}
}
async findExpiredKeys() {
// 实际实现需要根据缓存系统的特性
return [];
}
}
缓存实现
1. 内存缓存
javascript
// 内存缓存实现
class MemoryCache {
constructor(options = {}) {
this.cache = new Map();
this.maxSize = options.maxSize || 1000;
this.defaultTTL = options.defaultTTL || 3600000; // 1小时
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0
};
// 启动过期清理
this.startExpirationCleanup();
}
async get(key) {
const item = this.cache.get(key);
if (!item) {
this.stats.misses++;
return null;
}
if (this.isExpired(item)) {
this.cache.delete(key);
this.stats.misses++;
return null;
}
// 更新访问时间(用于LRU)
item.lastAccessed = Date.now();
this.stats.hits++;
return item.value;
}
async set(key, value, ttl = this.defaultTTL) {
// 检查缓存大小限制
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
this.evictLRU();
}
const item = {
value: value,
expiresAt: Date.now() + ttl,
lastAccessed: Date.now(),
createdAt: Date.now()
};
this.cache.set(key, item);
this.stats.sets++;
}
async delete(key) {
const deleted = this.cache.delete(key);
if (deleted) {
this.stats.deletes++;
}
return deleted;
}
async clear() {
this.cache.clear();
}
async keys(pattern) {
const keys = Array.from(this.cache.keys());
if (!pattern) return keys;
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
return keys.filter(key => regex.test(key));
}
isExpired(item) {
return Date.now() > item.expiresAt;
}
evictLRU() {
let oldestKey = null;
let oldestTime = Date.now();
for (const [key, item] of this.cache) {
if (item.lastAccessed < oldestTime) {
oldestTime = item.lastAccessed;
oldestKey = key;
}
}
if (oldestKey) {
this.cache.delete(oldestKey);
this.stats.evictions++;
}
}
startExpirationCleanup() {
setInterval(() => {
for (const [key, item] of this.cache) {
if (this.isExpired(item)) {
this.cache.delete(key);
}
}
}, 60000); // 每分钟清理一次
}
getStats() {
const total = this.stats.hits + this.stats.misses;
return {
...this.stats,
hitRate: total > 0 ? (this.stats.hits / total) * 100 : 0,
size: this.cache.size,
maxSize: this.maxSize
};
}
}
2. 分布式缓存
javascript
// 分布式缓存管理器
class DistributedCacheManager {
constructor(nodes) {
this.nodes = nodes;
this.hashRing = this.createHashRing(nodes);
}
// 创建一致性哈希环
createHashRing(nodes) {
const ring = new Map();
const virtualNodes = 150; // 每个物理节点对应150个虚拟节点
nodes.forEach(node => {
for (let i = 0; i < virtualNodes; i++) {
const hash = this.hash(`${node.id}:${i}`);
ring.set(hash, node);
}
});
return new Map([...ring.entries()].sort((a, b) => a[0] - b[0]));
}
// 获取负责的节点
getNode(key) {
const keyHash = this.hash(key);
for (const [hash, node] of this.hashRing) {
if (keyHash <= hash) {
return node;
}
}
// 如果没找到,返回第一个节点
return this.hashRing.values().next().value;
}
// 哈希函数
hash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // 转换为32位整数
}
return Math.abs(hash);
}
async get(key) {
const node = this.getNode(key);
try {
return await node.cache.get(key);
} catch (error) {
console.error(`节点 ${node.id} 读取失败:`, error);
return null;
}
}
async set(key, value, ttl) {
const node = this.getNode(key);
try {
await node.cache.set(key, value, ttl);
// 可选:复制到备份节点
const backupNode = this.getBackupNode(key);
if (backupNode && backupNode !== node) {
await backupNode.cache.set(key, value, ttl);
}
} catch (error) {
console.error(`节点 ${node.id} 写入失败:`, error);
throw error;
}
}
async delete(key) {
const node = this.getNode(key);
try {
await node.cache.delete(key);
// 删除备份
const backupNode = this.getBackupNode(key);
if (backupNode && backupNode !== node) {
await backupNode.cache.delete(key);
}
} catch (error) {
console.error(`节点 ${node.id} 删除失败:`, error);
}
}
getBackupNode(key) {
const primaryNode = this.getNode(key);
const nodes = Array.from(this.hashRing.values());
const primaryIndex = nodes.indexOf(primaryNode);
return nodes[(primaryIndex + 1) % nodes.length];
}
}
缓存优化
1. 缓存预热
javascript
// 缓存预热管理器
class CacheWarmupManager {
constructor(cache, database) {
this.cache = cache;
this.database = database;
this.warmupStrategies = [];
}
// 添加预热策略
addWarmupStrategy(strategy) {
this.warmupStrategies.push(strategy);
}
// 执行预热
async warmup() {
console.log('开始缓存预热...');
const startTime = Date.now();
for (const strategy of this.warmupStrategies) {
try {
await this.executeStrategy(strategy);
} catch (error) {
console.error(`预热策略 ${strategy.name} 执行失败:`, error);
}
}
const duration = Date.now() - startTime;
console.log(`缓存预热完成,耗时 ${duration}ms`);
}
async executeStrategy(strategy) {
console.log(`执行预热策略: ${strategy.name}`);
switch (strategy.type) {
case 'popular_data':
await this.warmupPopularData(strategy);
break;
case 'recent_data':
await this.warmupRecentData(strategy);
break;
case 'user_specific':
await this.warmupUserSpecificData(strategy);
break;
case 'static_data':
await this.warmupStaticData(strategy);
break;
default:
console.warn(`未知的预热策略类型: ${strategy.type}`);
}
}
// 预热热门数据
async warmupPopularData(strategy) {
const popularItems = await this.database.query(strategy.query);
for (const item of popularItems) {
const cacheKey = strategy.keyGenerator(item);
await this.cache.set(cacheKey, item, strategy.ttl);
}
console.log(`预热了 ${popularItems.length} 条热门数据`);
}
// 预热最近数据
async warmupRecentData(strategy) {
const recentItems = await this.database.query(strategy.query);
for (const item of recentItems) {
const cacheKey = strategy.keyGenerator(item);
await this.cache.set(cacheKey, item, strategy.ttl);
}
console.log(`预热了 ${recentItems.length} 条最近数据`);
}
// 预热用户特定数据
async warmupUserSpecificData(strategy) {
const users = await this.database.query(strategy.userQuery);
let totalItems = 0;
for (const user of users) {
const userItems = await this.database.query(strategy.dataQuery, [user.id]);
for (const item of userItems) {
const cacheKey = strategy.keyGenerator(user, item);
await this.cache.set(cacheKey, item, strategy.ttl);
totalItems++;
}
}
console.log(`为 ${users.length} 个用户预热了 ${totalItems} 条数据`);
}
// 预热静态数据
async warmupStaticData(strategy) {
const staticData = await this.database.query(strategy.query);
for (const item of staticData) {
const cacheKey = strategy.keyGenerator(item);
await this.cache.set(cacheKey, item, strategy.ttl || 86400000); // 24小时
}
console.log(`预热了 ${staticData.length} 条静态数据`);
}
}
// 预热策略示例
const warmupStrategies = [
{
name: '热门商品',
type: 'popular_data',
query: 'SELECT * FROM products ORDER BY view_count DESC LIMIT 100',
keyGenerator: (item) => `product:${item.id}`,
ttl: 3600000 // 1小时
},
{
name: '最新文章',
type: 'recent_data',
query: 'SELECT * FROM articles WHERE created_at > NOW() - INTERVAL 1 DAY',
keyGenerator: (item) => `article:${item.id}`,
ttl: 1800000 // 30分钟
},
{
name: '用户配置',
type: 'user_specific',
userQuery: 'SELECT id FROM users WHERE active = 1 LIMIT 1000',
dataQuery: 'SELECT * FROM user_settings WHERE user_id = ?',
keyGenerator: (user, item) => `user_settings:${user.id}`,
ttl: 7200000 // 2小时
}
];
2. 缓存监控
javascript
// 缓存监控器
class CacheMonitor {
constructor(cache) {
this.cache = cache;
this.metrics = {
hitRate: [],
responseTime: [],
memoryUsage: [],
keyCount: []
};
this.alerts = [];
this.thresholds = {
hitRateMin: 80, // 最低命中率80%
responseTimeMax: 100, // 最大响应时间100ms
memoryUsageMax: 85 // 最大内存使用率85%
};
}
// 开始监控
startMonitoring(interval = 60000) { // 1分钟
setInterval(() => {
this.collectMetrics();
this.checkAlerts();
}, interval);
}
// 收集指标
async collectMetrics() {
const stats = this.cache.getStats();
const timestamp = Date.now();
// 记录命中率
this.metrics.hitRate.push({
timestamp,
value: stats.hitRate
});
// 记录键数量
this.metrics.keyCount.push({
timestamp,
value: stats.size
});
// 测量响应时间
const responseTime = await this.measureResponseTime();
this.metrics.responseTime.push({
timestamp,
value: responseTime
});
// 保持最近100条记录
Object.keys(this.metrics).forEach(key => {
if (this.metrics[key].length > 100) {
this.metrics[key].shift();
}
});
}
// 测量响应时间
async measureResponseTime() {
const testKey = 'monitor_test_key';
const testValue = 'test_value';
const startTime = process.hrtime.bigint();
await this.cache.set(testKey, testValue, 1000);
await this.cache.get(testKey);
await this.cache.delete(testKey);
const endTime = process.hrtime.bigint();
return Number(endTime - startTime) / 1000000; // 转换为毫秒
}
// 检查告警
checkAlerts() {
const currentStats = this.getCurrentStats();
// 检查命中率告警
if (currentStats.hitRate < this.thresholds.hitRateMin) {
this.triggerAlert('LOW_HIT_RATE', {
current: currentStats.hitRate,
threshold: this.thresholds.hitRateMin
});
}
// 检查响应时间告警
if (currentStats.responseTime > this.thresholds.responseTimeMax) {
this.triggerAlert('HIGH_RESPONSE_TIME', {
current: currentStats.responseTime,
threshold: this.thresholds.responseTimeMax
});
}
}
// 触发告警
triggerAlert(type, data) {
const alert = {
type,
data,
timestamp: new Date(),
severity: this.getAlertSeverity(type, data)
};
this.alerts.push(alert);
console.warn('缓存告警:', alert);
// 保持最近50条告警
if (this.alerts.length > 50) {
this.alerts.shift();
}
}
getAlertSeverity(type, data) {
switch (type) {
case 'LOW_HIT_RATE':
return data.current < 50 ? 'critical' : 'warning';
case 'HIGH_RESPONSE_TIME':
return data.current > 500 ? 'critical' : 'warning';
default:
return 'info';
}
}
// 获取当前统计
getCurrentStats() {
const hitRate = this.metrics.hitRate.length > 0 ?
this.metrics.hitRate[this.metrics.hitRate.length - 1].value : 0;
const responseTime = this.metrics.responseTime.length > 0 ?
this.metrics.responseTime[this.metrics.responseTime.length - 1].value : 0;
return { hitRate, responseTime };
}
// 生成监控报告
generateReport() {
const report = {
timestamp: new Date(),
summary: this.getCurrentStats(),
trends: this.calculateTrends(),
alerts: this.alerts.slice(-10), // 最近10条告警
recommendations: this.generateRecommendations()
};
return report;
}
calculateTrends() {
return {
hitRate: this.calculateTrend(this.metrics.hitRate),
responseTime: this.calculateTrend(this.metrics.responseTime),
keyCount: this.calculateTrend(this.metrics.keyCount)
};
}
calculateTrend(data) {
if (data.length < 10) return 'stable';
const recent = data.slice(-10);
const first = recent[0].value;
const last = recent[recent.length - 1].value;
const change = ((last - first) / first) * 100;
if (change > 10) return 'increasing';
if (change < -10) return 'decreasing';
return 'stable';
}
generateRecommendations() {
const recommendations = [];
const stats = this.getCurrentStats();
if (stats.hitRate < 70) {
recommendations.push('命中率较低,检查缓存策略和TTL设置');
}
if (stats.responseTime > 50) {
recommendations.push('响应时间较长,考虑优化缓存实现或增加缓存节点');
}
return recommendations;
}
}
总结
缓存是提升数据库应用性能的重要手段:
- 缓存策略:选择合适的缓存模式和失效策略
- 实现方案:内存缓存、分布式缓存的设计和实现
- 缓存预热:提前加载热点数据提升命中率
- 监控告警:实时监控缓存性能和健康状态
- 优化调整:基于监控数据持续优化缓存配置
- 最佳实践:遵循缓存设计和使用的最佳实践
通过合理的缓存设计和管理,可以显著减少数据库负载,提升应用响应速度。