async/await仅解决IO等待,不解决CPU争抢、缓存不一致和数据库写冲突;需全链路异步化、避免阻塞调用、合理选用ValueTask/Interlocked/ConcurrentDictionary/Redis分布式锁、写操作入队列、乐观锁升级为版本号或Lua脚本、缓存采用延迟双删策略。

async/await 是高并发的起点,不是终点——它只解决 IO 等待问题,不解决 CPU 争抢、缓存不一致或数据库写冲突。
用 async/await 做对了,但为什么还卡?
很多人以为加了 async/await 就自动高并发了,结果压测时 QPS 上不去、线程池饥饿、GC 频繁。根本原因是:它只释放线程,不减少资源竞争。
- 必须所有 I/O 调用都异步化:
DbContext.SaveChangesAsync()、HttpClient.GetAsync()、FileStream.ReadAsync()—— 混入一个.Result或.Wait()就可能死锁 - 避免在异步方法里调用同步阻塞 API(如
File.ReadAllText()),否则线程池线程被占住,新请求排队等待 - 高频小任务(比如每秒数万次订单校验)建议用
ValueTask替代Task,减少 GC 压力;实测内存分配可降 60%+
库存扣减、计数器更新这类共享操作怎么不出错?
用 lock 最简单,但会串行化,成为瓶颈;真正在意吞吐量时,得换更轻量、更可控的方式。
- 简单整数计数:优先用
Interlocked.Decrement(ref stock),零锁、原子、快 - 对象级状态变更(如订单状态机):用
ConcurrentDictionary+TryRemove/GetOrAdd,比手动锁安全得多 - 分布式场景(多实例部署):必须上
Redis分布式锁,但别用SETNX手写——用StackExchange.Redis的LockTake+ 自动续期,否则容易锁失效
100 个用户同时下单,数据库写崩了怎么办?
数据库是高并发链路中最难横向扩展的一环,不能靠“加索引”或“读写分离”一劳永逸。
mallcloud商城基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离vue的企业级微服务敏捷开发系统架构。并引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手容易,适合学习和企业中使用。真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案,面向互联网设计同时适合B端和C端用户,支持CI/CD多环境部署,并提
- 写操作先过队列:把下单请求发到
RabbitMQ或Kafka,Web 层立刻返回“已受理”,后台消费者按能力消费,削峰填谷 - 避免直接
UPDATE products SET stock = stock - 1 WHERE id = @id AND stock >= 1—— 这种乐观锁在高并发下会大量失败重试;改用带版本号的更新(version字段)或 Redis Lua 脚本原子扣减 - 统计类查询(如“今日销量”)绝不实时算,用定时任务写入
summary_daily_sales表,查时直读
缓存更新策略选错,数据就永远对不上
“先删缓存再更新 DB” 和 “先更新 DB 再删缓存” 看似只差一步,但在并发下行为完全不同。
- 推荐组合:更新数据库 → 延迟双删(删一次 + 100ms 后再删一次),能覆盖大部分缓存穿透+脏读场景
- 绝对不要用“先更新缓存再更新 DB”——DB 更新失败时,缓存就是脏数据,且无法回滚
- Redis 缓存键设计要带业务维度和版本号,比如
order:detail:v2:12345,升级逻辑时直接切版本,避免热更新风险
真正难的从来不是“怎么实现并发”,而是“怎么让并发下的状态始终可预期”。锁、队列、缓存、数据库,每个环节的取舍都会影响最终一致性边界——这点在金融、库存、支付类业务里,错一步就是资损。






