从golang从GMP模型到分布式架构:无锁化思想的高并发实践
背景
在高并发场景下,无锁化一定是最高效的,golang中gmp模型中每个p维护一个自己队列,避免锁竞争的思想真是个天才的想法。
Go语言GMP模型中,每个P维护独立的本地运行队列,从而避免全局锁竞争,这一思想极具启发性。那么,在大促等高并发场景下,我们能否借鉴这一思路来提升系统并发能力?例如,在库存扣减、用户金额扣减、商户金额增加等典型业务中,又该如何融入无锁化思想?
GMP原理与引申
GMP 本地队列: “分而治之,减少共享”。每个 P 维护一个本地 Goroutine 队列,大部分调度工作都在本地完成,只有窃取或全局操作时才需要锁。这本质上是将全局竞争拆分为多个无竞争的本地操作。
基于这个思想,我们在大促中提高并发能力的指导原则是:将“竞争”转化为“协同”或“消除”。
方案设计:大促高并发实战中的无锁化融入
在大促中,库存扣减、金额变动是典型的“热点”问题,大量请求会同时竞争同一份数据。
应用架构层 - 借鉴 GMP 分治思想
目标: 将全局流量拆分为本地流量,减少对中心存储的冲击。
实践方案:
-
数据分片(Sharding)
-
思路: 像 GMP 有多个 P 一样,将商品库存或用户账户数据分散到不同的数据库实例或表中。比如,商品 ID 取模分到 16 个数据库,一个商品的流量只会打到其中一个库,压力变为 1/16。
-
效果: 将全局竞争降级为多个局部竞争,水平扩展能力极强。
-
-
本地缓存 + 批量合并
-
思路: 这是 GMP 本地队列思想的极致体现。对于库存扣减,可以在应用层为每个商品维护一个本地库存缓存。扣减时先扣减本地内存,通过消息队列的方式通知库存扣减,比如,把库存扣减信息写kafka,消费者异步消费把库存写入数据库,实现持久化。
-
示例:
-
商品 A 总库存 1000,给 10 台服务器每台分配 100 的本地库存。
-
用户请求打到服务器 1,直接扣减其本地库存(无锁操作,因为只有本线程处理)。
-
服务器 1 的本地库存降到阈值(如 20)时,向redis缓存申请新的库存(如再要 100)。
-
数据库负责全局库存的分配和最终一致性检查。
-
-
效果: 99% 的扣减请求都在应用本地内存中完成,无任何锁竞争,性能极
-
