sync.map.delete在每秒超50次并发删除时性能通常低于加锁普通map,因其惰性删除、原子操作、内存屏障及dirty map提升开销大,且delete与store同属高成本写路径。

sync.Map.Delete 在每秒超 50 次并发删除时,性能通常比加锁的普通 map 更差,别硬扛高频删。
sync.Map.Delete 为什么一高并发就变慢
它不是“带锁的 map”,而是靠原子操作 + 内存屏障 + dirty map 提升来实现线程安全。Delete 并不真正释放内存,只是打标记(惰性删除);后续 Load 或 Range 才可能触发清理,带来不可预测延迟。更关键的是:每次 Delete 都要跳指针、做原子判断、检查 read/dirty 状态,写多时开销远超一次 mutex.Lock + delete + mutex.Unlock。
- 每秒写入(含 Store/Delete)超 50 次,sync.Map 吞吐常低于
sync.RWMutex包裹的原生map - 大量 Delete 后紧接
Range,会强制提升 dirty map,引发一次性扩容成本 - 热点 key 高频删除时,read map 的 entry 被反复标记为 deleted,导致后续 LoadOrStore 失效、Store 强制降级到 dirty,进一步放大开销
CompareAndDelete 是唯一推荐的条件删除方式(Go 1.20+)
如果你要“值匹配才删”,必须用 CompareAndDelete,自己 Load + if + Delete 是竞态漏洞——中间可能被其他 goroutine 覆盖值。
-
CompareAndDelete(key, old)返回bool,失败不 panic,必须显式判断 -
old必须是同一地址或深度相等(结构体建议传指针);nil不支持比较,需先Load判断再调用 - 错误写法:
if v, ok := m.Load(k); ok && v == expected { m.Delete(k) }—— 这段代码在并发下完全不原子
高频删除场景该换什么
sync.Map 本质只适合「创建一次、读一百次、删零次」的缓存类场景。真要每秒删几百 key,它就是错选。
- 分片锁(sharded map):把大 map 拆成 32/64 个子 map,每个配独立
sync.Mutex,key 哈希路由,吞吐通常高 20–40% -
concurrent-map(第三方库):提供Remove、Count、Keys等缺失能力,API 更贴近预期 - 重建实例:若删除是批量、周期性的(比如每分钟清空过期 session),不如定时新建
sync.Map{},旧实例让 GC 回收 - 避免误用:
Range不是遍历工具,它是快照 + 全局锁,不能边删边遍历,也不能中断
最容易被忽略的一点:sync.Map 的 Delete 成本和 LoadOrStore 类似,都是“写路径”操作。很多人以为删得轻,其实它和 Store 一样触发 dirty map 升级逻辑。高频删的本质是高频写,而 sync.Map 就不是为这个设计的。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











