.NET 7.0内存缓存与客户端缓存实现及原理咨询
内存缓存与客户端缓存的机制验证与疑问
背景与实现
我正尝试在.NET 7.0环境下验证**内存缓存(In-memory)与客户端缓存(Client Side Caching)**的工作机制。
内存缓存实现
将数据库查询结果存储在缓存对象中,缓存过期时重新拉取数据:
[HttpGet] [Route("/user/GetUsers")] public async Task<List<User>> GetUserDetails() { myStopWatch.Start(); Task<List<User>> users = GetCachedResponse(CacheKey.users, () => _user.GetUsers()); myStopWatch.Stop(); double total = myStopWatch.Elapsed.TotalSeconds; return await users; } private async Task<List<User>> GetCachedResponse(string cacheKey, Func<Task<List<User>>> func) { List<User> users = new List<User>(); try { users = _cacheProvider.GetFromCache<List<User>>(cacheKey); if (users != null) return users; users = await func(); _cacheProvider.SetCache(cacheKey, users, cacheEntryOptions); } catch (Exception ex) { ex.ToString(); } return users; }
后续根据建议修正了StopWatch的使用方式:
[HttpGet] [Route("/user/GetUsers")] public async Task<List<User>> GetUserDetails() { myStopWatch.Start(); var users = await GetCachedResponse(CacheKey.users, () => _user.GetUsers()); myStopWatch.Stop(); double total = myStopWatch.Elapsed.TotalSeconds; return users; }
客户端缓存实现
Startup.cs配置
public void ConfigureServices(IServiceCollection services) { services.AddControllers(options => { options.CacheProfiles.Add("UserCacheForOneMin", new CacheProfile() { Duration = 60, Location = ResponseCacheLocation.Client }); }); } public void Configure(WebApplication app, IWebHostEnvironment env) { app.UseResponseCaching(); }
接口实现
[HttpGet] [Route("/user/GetAllUsers")] [ResponseCache(CacheProfileName = "UserCacheForOneMin")] public async Task<List<User>> GetAllUsers() { myStopWatch.Start(); Task<List<User>> users = _user.GetUsers(); myStopWatch.Stop(); double total = myStopWatch.Elapsed.TotalSeconds; return await users; }
修正StopWatch后的版本:
[HttpGet] [Route("/user/GetAllUsers")] [ResponseCache(CacheProfileName = "UserCacheForOneMin")] public async Task<List<User>> GetAllUsers() { myStopWatch.Start(); var users = await _user.GetUsers(); myStopWatch.Stop(); double total = myStopWatch.Elapsed.TotalSeconds; return users; }
观察到的现象
- 性能差异:客户端缓存加载速度比内存缓存更快
- 数据更新差异:内存缓存过期前返回旧数据,过期后拉取新数据;客户端缓存每次都会查询数据库,但响应时长短于内存缓存
疑问
- 客户端缓存的底层工作机制是什么?
- 我的客户端缓存实现是否正确?
- 为何会出现上述性能与数据更新的差异?
问题解答
1. 客户端缓存的底层工作机制
客户端缓存核心依赖HTTP响应头控制浏览器/客户端存储响应内容,无需服务端维护缓存存储:
- 当配置
ResponseCacheLocation.Client和Duration时,ASP.NET Core会自动在响应头中添加Cache-Control: private, max-age=60(private表示仅客户端可缓存,max-age指定缓存有效期为60秒) - 浏览器收到响应后,会将响应内容存入自身本地缓存(如磁盘或内存)
- 在
max-age有效期内,浏览器再次请求该接口时,会直接从本地缓存返回内容,不会发起实际HTTP请求到服务端;只有缓存过期后,才会重新向服务端发起请求
2. 客户端缓存实现的正确性
你的配置部分是正确的,但存在测试逻辑的问题:
- 接口内的StopWatch仅能反映服务端执行时间,如果浏览器命中客户端缓存,根本不会调用到服务端的接口方法,所以你的计时数据仅代表浏览器未命中缓存时的服务端耗时
- 另外,
app.UseResponseCaching()是服务端响应缓存中间件,对于ResponseCacheLocation.Client的配置来说并非必须(它主要用于服务端层面的响应缓存),但添加后不会影响客户端缓存的正常工作
3. 性能与数据更新差异的原因
性能差异的原因
你观察到的"客户端缓存响应时长短于内存缓存",本质是测试场景的误解:
- 浏览器首次请求客户端缓存接口时,服务端确实会执行数据库查询,此时耗时应与内存缓存的首次请求相近
- 后续请求中,若浏览器命中客户端缓存,不会走到服务端接口方法,你看到的"短耗时"其实是浏览器直接返回本地缓存的时间,但你的StopWatch在服务端代码中,这种情况下应该看不到计时输出——如果你的测试中每次都有计时,说明浏览器未命中缓存(比如每次清空了浏览器缓存,或者请求头包含
Cache-Control: no-cache等强制跳过缓存的指令)
数据更新差异的原因
- 内存缓存:数据存储在服务端内存中,只有缓存过期或被主动清除时,服务端才会重新查询数据库,因此过期前会持续返回旧数据
- 客户端缓存:每个客户端的缓存相互独立,服务端无法主动更新客户端缓存。数据库新增记录后,只有当客户端缓存过期并发起新请求时,才能拿到新数据;如果你的测试中每次都查询数据库,说明浏览器未命中客户端缓存,需检查请求上下文或缓存头配置是否正确
内容的提问来源于stack exchange,提问作者AT-2017
相关产品推荐
相关产品推荐

