📖 数据密集型设计

分布式缓存策略与优化

深入探讨分布式缓存架构与多级缓存策略

一、分布式缓存概述

分布式缓存是在分布式系统中用于缓存数据的组件,能够显著提高系统性能和降低数据库压力。在数据密集型应用中,分布式缓存是构建高性能系统的关键组件。

二、缓存系统对比

2.1 缓存系统对比表

缓存系统 类型 数据结构 持久化 集群支持 适用场景
Redis 内存缓存 String, Hash, List, Set, ZSet RDB/AOF 主从/集群 通用缓存
Memcached 内存缓存 String 分布式哈希 简单缓存
Caffeine 本地缓存 多种数据结构 本地 进程内缓存
Guava Cache 本地缓存 多种数据结构 本地 进程内缓存

2.2 多级缓存架构

graph TD A[请求] --> B[浏览器缓存] B --> C[CDN缓存] C --> D[应用层缓存] D --> E[分布式缓存] E --> F[数据库] D --> D1[本地缓存] D --> D2[进程缓存] E --> E1[Redis集群] E --> E2[热点数据] E --> E3[冷数据]

三、Redis缓存策略

3.1 Cache-Aside策略

public class CacheAsideService
{
    public async Task GetAsync(string key, Func> fetchFunction, TimeSpan? expiration = null)
    {
        var cached = await _redisService.GetAsync(key);
        
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonSerializer.Deserialize(cached);
        }
        
        var data = await fetchFunction();
        
        if (data != null)
        {
            await _redisService.SetAsync(key, JsonSerializer.Serialize(data), expiration ?? TimeSpan.FromMinutes(30));
        }
        
        return data;
    }
    
    public async Task UpdateAsync(string key, T data)
    {
        await _databaseService.UpdateAsync(data);
        await _redisService.RemoveAsync(key);
    }
    
    public async Task DeleteAsync(string key)
    {
        await _databaseService.DeleteAsync(key);
        await _redisService.RemoveAsync(key);
    }
}

3.2 Read-Through策略

public class ReadThroughCacheService
{
    public async Task GetAsync(string key)
    {
        var cached = await _redisService.GetAsync(key);
        
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonSerializer.Deserialize(cached);
        }
        
        var data = await _databaseService.GetAsync(key);
        
        if (data != null)
        {
            await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
        }
        
        return data;
    }
    
    public async Task GetWithFallbackAsync(string key, Func> fallback)
    {
        var cached = await _redisService.GetAsync(key);
        
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonSerializer.Deserialize(cached);
        }
        
        var data = await fallback();
        
        if (data != null)
        {
            await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
        }
        
        return data;
    }
}

3.3 Write-Through策略

public class WriteThroughCacheService
{
    public async Task UpdateAsync(string key, T data)
    {
        await _databaseService.UpdateAsync(data);
        await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
    }
    
    public async Task InsertAsync(string key, T data)
    {
        await _databaseService.InsertAsync(data);
        await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
    }
    
    public async Task DeleteAsync(string key)
    {
        await _databaseService.DeleteAsync(key);
        await _redisService.RemoveAsync(key);
    }
}

3.4 Write-Behind策略

public class WriteBehindCacheService
{
    private readonly Queue _writeQueue = new Queue();
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
    
    public async Task UpdateAsync(string key, T data)
    {
        await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
        
        await _semaphore.WaitAsync();
        try
        {
            _writeQueue.Enqueue(new WriteOperation
            {
                Key = key,
                Data = data,
                OperationType = OperationType.Update
            });
        }
        finally
        {
            _semaphore.Release();
        }
        
        _backgroundWriter.Enqueue();
    }
    
    private async Task ProcessWriteQueueAsync()
    {
        while (true)
        {
            await _semaphore.WaitAsync();
            WriteOperation operation = null;
            
            if (_writeQueue.Count > 0)
            {
                operation = _writeQueue.Dequeue();
            }
            _semaphore.Release();
            
            if (operation == null)
            {
                await Task.Delay(100);
                continue;
            }
            
            try
            {
                switch (operation.OperationType)
                {
                    case OperationType.Update:
                        await _databaseService.UpdateAsync(operation.Data);
                        break;
                    case OperationType.Insert:
                        await _databaseService.InsertAsync(operation.Data);
                        break;
                    case OperationType.Delete:
                        await _databaseService.DeleteAsync(operation.Key);
                        break;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to process write operation");
            }
        }
    }
}

public enum OperationType { Insert, Update, Delete }

四、缓存问题与解决方案

4.1 缓存穿透

public class CachePenetrationService
{
    private readonly BloomFilter _bloomFilter;
    
    public async Task GetWithPenetrationProtectionAsync(string key, Func> fetchFunction)
    {
        if (!_bloomFilter.Contains(key))
        {
            return default;
        }
        
        var cached = await _redisService.GetAsync(key);
        
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonSerializer.Deserialize(cached);
        }
        
        var data = await fetchFunction();
        
        if (data != null)
        {
            await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
            _bloomFilter.Add(key);
        }
        
        return data;
    }
}

public class BloomFilter
{
    private readonly BitArray _bitArray;
    private readonly int _hashCount;
    private readonly HashAlgorithm[] _hashAlgorithms;
    
    public bool Contains(string key)
    {
        foreach (var hashAlgorithm in _hashAlgorithms)
        {
            var hash = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(key));
            var index = BitConverter.ToUInt32(hash, 0) % _bitArray.Length;
            
            if (!_bitArray[(int)index])
            {
                return false;
            }
        }
        
        return true;
    }
    
    public void Add(string key)
    {
        foreach (var hashAlgorithm in _hashAlgorithms)
        {
            var hash = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(key));
            var index = BitConverter.ToUInt32(hash, 0) % _bitArray.Length;
            
            _bitArray[(int)index] = true;
        }
    }
}

4.2 缓存击穿

public class CacheBreakdownService
{
    private readonly ConcurrentDictionary _locks = new ConcurrentDictionary();
    
    public async Task GetWithBreakdownProtectionAsync(string key, Func> fetchFunction)
    {
        var cached = await _redisService.GetAsync(key);
        
        if (!string.IsNullOrEmpty(cached))
        {
            return JsonSerializer.Deserialize(cached);
        }
        
        var semaphore = _locks.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));
        
        try
        {
            await semaphore.WaitAsync();
            
            cached = await _redisService.GetAsync(key);
            
            if (!string.IsNullOrEmpty(cached))
            {
                return JsonSerializer.Deserialize(cached);
            }
            
            var data = await fetchFunction();
            
            if (data != null)
            {
                await _redisService.SetAsync(key, JsonSerializer.Serialize(data));
            }
            
            return data;
        }
        finally
        {
            semaphore.Release();
            _locks.TryRemove(key, out _);
        }
    }
}

4.3 缓存雪崩

public class CacheAvalancheService
{
    public async Task SetWithRandomExpirationAsync(string key, T data, TimeSpan baseExpiration)
    {
        var random = new Random();
        var extraExpiration = TimeSpan.FromSeconds(random.Next(60));
        
        await _redisService.SetAsync(key, JsonSerializer.Serialize(data), baseExpiration + extraExpiration);
    }
    
    public async Task SetWithDistributedExpirationAsync(string key, T data, TimeSpan baseExpiration)
    {
        var hash = key.GetHashCode();
        var distributedExpiration = TimeSpan.FromSeconds(Math.Abs(hash) % 300);
        
        await _redisService.SetAsync(key, JsonSerializer.Serialize(data), baseExpiration + distributedExpiration);
    }
    
    public async Task SetWithWarmupAsync(string key, T data)
    {
        await _redisService.SetAsync(key, JsonSerializer.Serialize(data), TimeSpan.FromHours(1));
        
        await _warmupScheduler.ScheduleWarmup(key, TimeSpan.FromMinutes(50));
    }
}

五、多级缓存实现

5.1 多级缓存架构

graph TD A[请求] --> B[本地缓存] B --> C[分布式缓存] C --> D[数据库] B --> B1[命中] B --> B2[未命中] C --> C1[命中] C --> C2[未命中] D --> D1[返回数据] D1 --> C3[写入分布式缓存] C3 --> B3[写入本地缓存]

5.2 多级缓存实现

public class MultiLevelCacheService
{
    private readonly IMemoryCache _localCache;
    private readonly IRedisCache _redisCache;
    private readonly IDatabaseService _databaseService;
    
    public async Task GetAsync(string key, Func> fetchFunction, TimeSpan? localExpiration = null, TimeSpan? remoteExpiration = null)
    {
        var localCached = _localCache.Get(key);
        
        if (localCached != null)
        {
            return localCached;
        }
        
        var remoteCached = await _redisCache.GetAsync(key);
        
        if (!string.IsNullOrEmpty(remoteCached))
        {
            var data = JsonSerializer.Deserialize(remoteCached);
            _localCache.Set(key, data, localExpiration ?? TimeSpan.FromMinutes(5));
            
            return data;
        }
        
        var data = await fetchFunction();
        
        if (data != null)
        {
            await _redisCache.SetAsync(key, JsonSerializer.Serialize(data), remoteExpiration ?? TimeSpan.FromMinutes(30));
            _localCache.Set(key, data, localExpiration ?? TimeSpan.FromMinutes(5));
        }
        
        return data;
    }
    
    public async Task InvalidateAsync(string key)
    {
        _localCache.Remove(key);
        await _redisCache.RemoveAsync(key);
    }
    
    public async Task InvalidateLocalAsync(string key)
    {
        _localCache.Remove(key);
    }
    
    public async Task InvalidateRemoteAsync(string key)
    {
        await _redisCache.RemoveAsync(key);
    }
}

5.3 热点数据处理

public class HotDataService
{
    public async Task GetHotDataAsync(string key, Func> fetchFunction)
    {
        var localKey = $"hot:{key}";
        var localCached = _localCache.Get(localKey);
        
        if (localCached != null)
        {
            return localCached;
        }
        
        var remoteKey = $"hot:{key}";
        var remoteCached = await _redisCache.GetAsync(remoteKey);
        
        if (!string.IsNullOrEmpty(remoteCached))
        {
            var data = JsonSerializer.Deserialize(remoteCached);
            _localCache.Set(localKey, data, TimeSpan.FromMinutes(10));
            
            return data;
        }
        
        var data = await fetchFunction();
        
        if (data != null)
        {
            await _redisCache.SetAsync(remoteKey, JsonSerializer.Serialize(data), TimeSpan.FromHours(1));
            _localCache.Set(localKey, data, TimeSpan.FromMinutes(10));
        }
        
        return data;
    }
    
    public async Task WarmupHotDataAsync(List keys)
    {
        foreach (var key in keys)
        {
            await GetHotDataAsync(key, () => _databaseService.GetAsync(key));
        }
    }
}
            
            

六、缓存监控与优化

6.1 缓存监控

public class CacheMonitor
{
    public async Task GetMetricsAsync()
    {
        var redisInfo = await _redisService.GetInfoAsync();
        
        return new CacheMetrics
        {
            Hits = redisInfo.KeyspaceHits,
            Misses = redisInfo.KeyspaceMisses,
            HitRate = redisInfo.KeyspaceHits / (double)(redisInfo.KeyspaceHits + redisInfo.KeyspaceMisses) * 100,
            MemoryUsage = redisInfo.UsedMemory,
            MemoryUsagePercent = redisInfo.UsedMemoryPercent,
            KeyCount = redisInfo.KeyCount,
            ExpiredKeys = redisInfo.ExpiredKeys
        };
    }
    
    public async Task MonitorAsync()
    {
        var metrics = await GetMetricsAsync();
        
        if (metrics.HitRate < 80)
        {
            await _alertService.SendAlert("缓存命中率低", 
                $"命中率: {metrics.HitRate:F1}%");
        }
        
        if (metrics.MemoryUsagePercent > 90)
        {
            await _alertService.SendAlert("缓存内存使用过高", 
                $"内存使用: {metrics.MemoryUsagePercent:F1}%");
        }
    }
}

public class CacheMetrics
{
    public long Hits { get; set; }
    public long Misses { get; set; }
    public double HitRate { get; set; }
    public long MemoryUsage { get; set; }
    public double MemoryUsagePercent { get; set; }
    public long KeyCount { get; set; }
    public long ExpiredKeys { get; set; }
}

6.2 缓存优化

public class CacheOptimizer
{
    public async Task OptimizeAsync()
    {
        await OptimizeMemoryUsageAsync();
        await OptimizeHitRateAsync();
        await OptimizeExpirationAsync();
    }
    
    private async Task OptimizeMemoryUsageAsync()
    {
        var memoryInfo = await _redisService.GetMemoryInfoAsync();
        
        if (memoryInfo.UsedMemoryPercent > 80)
        {
            await _redisService.ConfigureMaxMemoryPolicy("allkeys-lru");
            await _redisService.SetMaxMemory(2 * 1024 * 1024 * 1024);
        }
    }
    
    private async Task OptimizeHitRateAsync()
    {
        var slowQueries = await _redisService.GetSlowQueriesAsync();
        
        foreach (var query in slowQueries)
        {
            await _redisService.AddIndex(query.Key);
        }
    }
    
    private async Task OptimizeExpirationAsync()
    {
        var keysWithNoExpiration = await _redisService.GetKeysWithNoExpirationAsync();
        
        foreach (var key in keysWithNoExpiration)
        {
            await _redisService.SetExpiration(key, TimeSpan.FromHours(24));
        }
    }
}

七、缓存最佳实践

7.1 缓存设计原则

  • 合理选择缓存策略
  • 设置合理的过期时间
  • 使用多级缓存
  • 处理缓存一致性问题
  • 监控缓存状态

7.2 缓存键设计

public class CacheKeyGenerator
{
    public string GenerateKey(string prefix, params object[] parts)
    {
        var keyParts = new List { prefix };
        keyParts.AddRange(parts.Select(p => p?.ToString() ?? "null"));
        
        return string.Join(":", keyParts);
    }
    
    public string GenerateUserKey(long userId)
    {
        return GenerateKey("user", userId);
    }
    
    public string GenerateProductKey(long productId)
    {
        return GenerateKey("product", productId);
    }
    
    public string GenerateOrderKey(long orderId)
    {
        return GenerateKey("order", orderId);
    }
    
    public string GenerateHotKey(string key)
    {
        return GenerateKey("hot", key);
    }
}

7.3 缓存一致性

public class CacheConsistencyService
{
    public async Task UpdateWithConsistencyAsync(string key, T data)
    {
        await _databaseService.UpdateAsync(data);
        
        await _redisCache.RemoveAsync(key);
        _localCache.Remove(key);
        
        await Task.Delay(100);
        
        var freshData = await _databaseService.GetAsync(key);
        
        if (freshData != null)
        {
            await _redisCache.SetAsync(key, JsonSerializer.Serialize(freshData));
            _localCache.Set(key, freshData);
        }
    }
    
    public async Task RebuildCacheAsync(string key)
    {
        var data = await _databaseService.GetAsync(key);
        
        if (data != null)
        {
            await _redisCache.SetAsync(key, JsonSerializer.Serialize(data));
            _localCache.Set(key, data);
        }
    }
}
            
            

八、总结

分布式缓存是数据密集型应用中提高系统性能的关键组件。通过合理选择缓存策略、设置多级缓存、处理缓存问题(穿透、击穿、雪崩),能够显著提高系统性能和降低数据库压力。建立完善的监控体系,定期优化缓存配置,确保缓存系统高效运行。