话题动态
发起者
老王
话题状态
等待首个回复
参与人
1
互动热度
0
聊聊 Go 里面并发安全 Map 的选型问题。最近 review 团队代码,发现不少同学不管什么场景,只要涉及并发读写就无脑一把梭 sync.Map,这其实是个挺大的隐患。
之前我们做支付网关的防重幂等校验时就踩过这个坑:
当时峰值 QPS 大概 3w 左右,为了图省事,用 sync.Map 存了生命周期极短的防重 Token。结果上线没多久,监控就报警 P99 延迟严重抖动。
排查下来的因果链路非常清晰:
sync.Map 的 read 字段命中率极低,大量的读写全部穿透到加锁的 dirty 上;misses 计数器频繁达到阈值,触发 dirty 晋升为 read 的操作,这里面伴随着大量的全量内存拷贝和全局互斥锁竞争;sync.(*Map).Store 和 Load 的 Lock 竞争爆炸,P99 延迟从 2ms 一路飙到 200ms+。后来我们重构成类似 Java ConcurrentHashMap 思路的分段锁实现(分了 64 个 Shard,每个 Shard 各自持有一个 sync.RWMutex + 原生 map),锁粒度打散之后,全局锁竞争直接消失,延迟瞬间压平。
个人的经验总结:
sync.Map 核心优势在于「读多写少」或者「Key 稳定只更新 Value」的场景,这种情况下它几乎是无锁性能;sync.Map。想问问各位老哥,你们在生产环境遇到「高频写新 Key」或者读写比例五五开的场景时,都是怎么选型的?直接上 orcaman/concurrent-map 这类开源三方库、自己手写分段锁,还是有其他更激进的无锁方案?
全部回复(0)