C#中级8、什么是缓存

缓存(Caching) 是一种临时存储数据的技术 ,目的是避免重复计算或重复访问慢速资源 (如数据库、网络 API、文件系统),从而显著提升系统性能和响应速度

✅ 简单说:"把常用的数据放在更快的地方,下次直接拿,不用再算/再查。"


🎯 为什么需要缓存?

操作 典型耗时
CPU 一级缓存访问 ~1 纳秒
内存访问 ~100 纳秒
SSD 读取 ~100 微秒
数据库查询 110 毫秒
网络 API 调用 50500 毫秒

👉 缓存能将"毫秒级"操作变成"微秒甚至纳秒级"!


🔁 缓存的基本工作流程

复制代码
用户请求数据
       │
       ▼
   [缓存中查找]?
       ├── 是 → 直接返回(**缓存命中,Cache Hit**)
       └── 否 → 
               │
               ▼
         [从原始源获取](数据库/API等)
               │
               ▼
         [存入缓存](可选:设置过期时间)
               │
               ▼
            返回数据

🧱 缓存的核心要素

要素 说明
Key(键) 唯一标识缓存项(如 "user:123"
Value(值) 被缓存的数据(对象、字符串、字节等)
TTL(Time-To-Live) 过期时间(避免数据陈旧)
淘汰策略 内存满时如何清理(LRU、LFU 等)

💻 C# / .NET 中的缓存实现方式

1️⃣ 内存缓存(In-Memory Cache)

  • 适用场景:单机应用、开发测试、非关键数据
  • .NET 实现IMemoryCache(来自 Microsoft.Extensions.Caching.Memory
示例:
复制代码
// 注册服务(Program.cs)
builder.Services.AddMemoryCache();

// 使用
public class UserService
{
    private readonly IMemoryCache _cache;
    
    public UserService(IMemoryCache cache) => _cache = cache;

    public User GetUser(int id)
    {
        // 尝试从缓存获取
        if (_cache.TryGetValue($"user:{id}", out User user))
            return user;

        // 缓存未命中:查数据库
        user = _db.Users.Find(id);

        // 存入缓存(5分钟过期)
        _cache.Set($"user:{id}", user, TimeSpan.FromMinutes(5));
        
        return user;
    }
}

⚠️ 缺点:进程重启后丢失;多实例部署时数据不一致。


2️⃣ 分布式缓存(Distributed Cache)

  • 适用场景:Web 集群、微服务、高可用系统
  • 常用中间件:Redis(最流行)、Memcached
  • .NET 实现IDistributedCache(抽象接口)
示例(使用 Redis):
复制代码
// 安装包:StackExchange.Redis.Extensions 或 Microsoft.Extensions.Caching.StackExchangeRedis

// Program.cs
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
});

// 使用
public class ProductService
{
    private readonly IDistributedCache _cache;
    
    public async Task<Product> GetProductAsync(int id)
    {
        var json = await _cache.GetStringAsync($"product:{id}");
        if (json != null)
            return JsonSerializer.Deserialize<Product>(json);

        // 查数据库
        var product = await _db.Products.FindAsync(id);
        
        // 存入 Redis(10分钟过期)
        await _cache.SetStringAsync(
            $"product:{id}", 
            JsonSerializer.Serialize(product),
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) }
        );
        
        return product;
    }
}

优点:多实例共享、持久化(可选)、高并发支持。


3️⃣ HTTP 响应缓存(Response Caching)

  • 适用场景:Web API、静态内容
  • .NET 实现[ResponseCache] 特性 或 中间件
示例:
复制代码
[HttpGet("{id}")]
[ResponseCache(Duration = 60)] // 浏览器/代理缓存 60 秒
public async Task<ActionResult<Product>> Get(int id)
{
    return await _service.GetProductAsync(id);
}

💡 这会在 HTTP 响应头中添加 Cache-Control: public, max-age=60,让客户端或 CDN 缓存结果。


🧠 缓存策略(何时更新/失效?)

策略 说明 适用场景
TTL(过期时间) 固定时间后自动失效 数据允许短暂不一致(如商品详情)
主动刷新 后台定时更新缓存 高频访问 + 数据变化可预测
写穿透(Write-Through) 更新数据库时同步更新缓存 强一致性要求
失效(Invalidate) 数据变更时删除缓存 简单可靠(推荐)
懒加载(Lazy Load) 首次访问时加载 降低启动开销

最佳实践"更新数据库后,删除缓存"(而非更新缓存),避免并发问题。


⚠️ 缓存常见问题与解决方案

问题 说明 解决方案
缓存雪崩 大量缓存同时过期,瞬间压垮数据库 设置随机 TTL(如 5min ± 30s)
缓存穿透 查询不存在的数据,绕过缓存直击 DB 缓存空值(null)+ 布隆过滤器
缓存击穿 热点 key 过期瞬间被大量请求打爆 互斥锁(只让一个线程重建缓存)
数据不一致 DB 和缓存内容不同步 采用"先更新 DB,再删缓存"策略

🌐 缓存层级(从快到慢)

层级 技术 位置
L1 CPU 缓存 硬件
L2 内存缓存 应用进程内
L3 Redis/Memcached 本地或远程服务器
L4 CDN 缓存 边缘节点
L5 数据库查询缓存 DB 内部(如 MySQL Query Cache)

💡 一个请求可能经过多层缓存!


✅ 总结:缓存的核心价值

维度 效果
性能 响应时间降低 10~1000 倍
吞吐量 系统 QPS 显著提升
成本 减少数据库/外部 API 调用,节省资源
用户体验 页面加载更快,操作更流畅

🧠 记住
"缓存不是银弹,但没有缓存,现代高性能系统寸步难行。"

在 .NET 开发中:

  • 单机用 IMemoryCache
  • 分布式用 IDistributedCache + Redis
  • Web API 用 [ResponseCache]
相关推荐
q***04051 小时前
Nginx 缓存清理
运维·nginx·缓存
松涛和鸣2 小时前
14、C 语言进阶:函数指针、typedef、二级指针、const 指针
c语言·开发语言·算法·排序算法·学习方法
movie__movie5 小时前
秒杀库存扣减可以用redis原子自增么
数据库·redis·缓存
智商低情商凑6 小时前
Go学习之 - Goroutines和channels
开发语言·学习·golang
半桶水专家6 小时前
Go 语言时间处理(time 包)详解
开发语言·后端·golang
编程点滴6 小时前
Go 重试机制终极指南:基于 go-retry 打造可靠容错系统
开发语言·后端·golang
实心儿儿7 小时前
C++ —— 模板进阶
开发语言·c++
萧鼎7 小时前
Python PyTesseract OCR :从基础到项目实战
开发语言·python·ocr
二川bro8 小时前
第57节:Three.js企业级应用架构
开发语言·javascript·架构