0

0

c# 高并发下的缓存预热和数据同步方案

畫卷琴夢

畫卷琴夢

发布时间:2026-01-12 10:39:11

|

656人浏览过

|

来源于php中文网

原创

缓存预热需在应用启动后、首个请求前同步完成,避免缓存击穿;分布式环境必须使用带版本号的 IDistributedCache 并禁用滑动过期;数据更新须执行“删-更-延时再删”三步法,并用 SemaphoreSlim 控制并发重建。

c# 高并发下的缓存预热和数据同步方案

缓存预热必须在应用启动后、首个请求前完成

高并发下,如果等第一个请求触发缓存加载,必然导致大量线程争抢初始化(即“缓存击穿”),尤其当 GetOrAdd 用的是非线程安全的工厂函数时,可能重复执行耗时操作。正确做法是:在 Program.csStartup.ConfigureServices 中显式调用预热逻辑,并确保其同步阻塞到完成。

  • 使用 Task.Run(() => PreheatCache()).Wait() 强制同步等待(注意不要在 ASP.NET Core 的同步上下文里用 .Result,易死锁)
  • 预热函数内部应避免依赖 IHttpContextAccessor 等请求作用域服务;改用 IServiceScopeFactory 创建独立 scope
  • 若预热数据量大,可分批 + await Task.Delay(1) 防止单次占用主线程太久,但整体仍需在 WebApplication 构建完成前结束

分布式环境必须用带版本号的缓存键 + 原子写入

单机 MemoryCache 在多实例部署下完全失效。必须切换为 IDistributedCache(如 Redis),且不能直接存原始对象——否则多个节点同时更新会覆盖彼此,造成脏数据。

  • 缓存键格式建议为:$"user:profile:v2:{userId}",其中 v2 是业务版本号,每次数据结构变更就升级,强制全量刷新
  • 写入时用 distributedCache.SetStringAsync(key, json, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) }),禁用滑动过期(SlidingExpiration),防止“越用越旧”
  • 关键数据更新后,必须同步调用 distributedCache.RemoveAsync(key),而不是等过期;删除失败要记录告警,不可静默忽略

数据库与缓存一致性靠“先删缓存,再更DB”+ 延迟双删兜底

“先更DB,再删缓存”在并发更新时有概率导致缓存残留旧值(DB 更新成功,但删缓存失败或被覆盖)。生产环境必须用“删除-更新-延迟再删”三步法。

dboxShare 开源企业网盘系统4.0.0.2105
dboxShare 开源企业网盘系统4.0.0.2105

dboxShare 是一款简便易用的免费开源企业网盘,基于 .NET 技术开发,用于构建安全高效的文件云存储及云管理平台。 用户无需改变工作习惯,文件双向同步将会根据相应的权限自动进行上传、下载及版本更替,为共享协作提供便捷高效的解决方案。 系统具有安装简单、部署灵活和维护量小的特点,适用于企业组织及团队搭建安全高效的私有云网盘。

下载
  • 第一步:调用 distributedCache.RemoveAsync(key)
  • 第二步:执行 EF Core 的 SaveChangesAsync()
  • 第三步:启动后台任务,await Task.Delay(TimeSpan.FromSeconds(500)) 后再次 RemoveAsync(key) —— 覆盖因主从延迟、重试机制导致的缓存回写
  • 所有删缓存操作必须包裹 try/catch,失败时写入本地队列(如 ConcurrentQueue),由后台服务重试,不能丢弃

并发读场景下避免 Cache Stampede,用 SemaphoreSlim 限流重建

即使做了预热,缓存过期瞬间仍可能有上百请求同时发现缓存为空,全部涌入 DB。不能靠 GetOrCreateAsync 默认行为扛住——它的 factory 函数不是原子的。

  • 为每个缓存键维护一个 ConcurrentDictionary,键为 cache key,值为独占信号量
  • 读取时:先 cache.TryGetValue(key, out var value);若为空,则 semaphores.GetOrAdd(key, _ => new SemaphoreSlim(1,1)).WaitAsync()
  • 获得信号量后,再次检查缓存(double-check),未命中才重建;完成后 Release() 并移除该 semaphore(避免内存泄漏)
  • 注意:不要用 lock,它跨进程无效;也不要复用同一个 SemaphoreSlim 实例,会导致不同 key 互相阻塞
private static readonly ConcurrentDictionary _semaphores = new();
public async Task GetProfileAsync(int userId)
{
    var key = $"user:profile:v2:{userId}";
    if (_cache.TryGetValue(key, out UserProfile profile))
        return profile;

    var semaphore = _semaphores.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));
    await semaphore.WaitAsync();
    try
    {
        // Double-check after acquiring semaphore
        if (_cache.TryGetValue(key, out profile))
            return profile;

        profile = await _db.Users.FirstAsync(u => u.Id == userId);
        await _cache.SetStringAsync(key, JsonSerializer.Serialize(profile), 
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) });
        return profile;
    }
    finally
    {
        semaphore.Release();
        _semaphores.TryRemove(key, out _);
    }
}
缓存键设计、删除时机、并发重建这三点一旦漏掉任意一个,高并发下的数据不一致就会变成偶发性线上事故——而这类问题往往在压测时不出,上线后半夜爆发。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

323

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

231

2023.10.07

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

408

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

532

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

309

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

74

2025.09.10

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

51

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

98

2025.10.23

Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

2

2026.01.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 6.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号