golang key map 所有_golang 并发安全Map以及分段锁的实现方法

涉及概念

并发安全Map

分段锁

sync.Map

CAS ( Compare And Swap )

双检查

分断锁

type SimpleCache struct {

mu sync.RWMutex

items map[interface{}]*simpleItem

}

在日常开发中, 上述这种数据结构肯定不少见,因为golang的原生map是非并发安全的,所以为了保证map的并发安全,最简单的方式就是给map加锁。

之前使用过两个本地内存缓存的开源库, gcache, cache2go,其中存储缓存对象的结构都是这样,对于轻量级的缓存库,为了设计简洁(包含清理过期对象等 ) 再加上当需要缓存大量数据时有redis,memcache等明星项目解决。 但是如果抛开这些因素遇到真正数量巨大的数据量时,直接对一个map加锁,当map中的值越来越多,访问map的请求越来越多,大家都竞争这一把锁显得并发访问控制变重。 在go1.9引入sync.Map 之前,比较流行的做法就是使用分段锁,顾名思义就是将锁分段,将锁的粒度变小,将存储的对象分散到各个分片中,每个分片由一把锁控制,这样使得当需要对在A分片上的数据进行读写时不会影响B分片的读写。

分段锁的实现

// Map 分片

type ConcurrentMap []*ConcurrentMapShared

// 每一个Map 是一个加锁的并发安全Map

type ConcurrentMapShared struct {

items map[string]interface{}

sync.RWMutex // 各个分片Map各自的锁

}

主流的分段锁,即通过hash取模的方式找到当前访问的key处于哪一个分片之上,再对该分片进行加锁之后再读写。分片定位时,常用有BKDR, FNV32等hash算法得到key的hash值。

func New() ConcurrentMap {

// SHARD_COUNT 默认32个分片

m := make(ConcurrentMap, SHARD_COUNT)

for i := 0; i < SHARD_COUNT; i++ {

m[i] = &ConcurrentMapShared{

items: make(map[string]interface{}),

}

}

return m

}

在初始化好分片后, 对分片上的数据进行读写时就需要用hash取模进行分段定位来确认即将要读写的分片。

获取段定位

func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {

return m[uint(fnv32(key))%uint(SHARD_COUNT)]

}

// FNV hash

func fnv32(key string) uint32 {

hash := uint32(2166136261)

const prime32 = uint32(16777619)

for i := 0; i < len(key); i++ {

hash *= prime32

hash ^= uint32(key[i])

}

return hash

}

之后对于map的GET SET 就简单顺利成章的完成

Set And Get

func (m ConcurrentMap) Set(key string, value interface{}) {

shard := m.GetShard(key) // 段定位找到分片

shard.Lock() // 分片上锁

shard.items[key] = value // 分片操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值