sync.Map的使用
正常情况下,在函数内记录哈希关系我们会使用到map,简单高效;
但在使用全局hash结构时,高并发场景下,会有map读写panic的问题;
解决的方法常有:
给map加锁(互斥锁,读写锁)
使用golang 1.9之后的sync.Map
使用外部开源安全缓存,比如cache等
本次主要介绍sync.Map的一些用法
var m sync.Map
// 1. 写入
m.Store("testmap", 888)
// 2. 读取
age, ok := m.Load("testmap")
if ok{
fmt.Println(age.(int))
}
// 3. 遍历
m.Range(func(key, value interface{}) bool {
name := key.(string)
age := value.(int)
fmt.Println(name, age)
return true
})
// 4. 删除
m.Delete("testmap")
age, ok := m.Load("testmap")
fmt.Println(age, ok)
// 5. 读取或写入
m.LoadOrStore("testmap", 168)
age, _ = m.Load("testmap")
fmt.Println(age)
sync.map原理分析
主要是以下几个函数
func (m *Map) Store(key, value interface{}) {
read, _ := m.read.Load().(readOnly)
//如果read中存在,并且状态不是删除,则直接进行原子性操作置换新数据后返回
//此处应该是更新的情况直接更新地址指向的内容
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
//加锁后,再次进行检查
if e, ok := read.m[key]; ok {
//检查是否已经有删除标记
if e.unexpungeLocked() {
// The entry was previously expunged, which implies that there is a
// non-nil dirty map and this entry is not in it.
m.dirty[key] = e
}
e.storeLocked(&value)//更新二级缓存
} else if e, ok := m.dirty[key]; ok {
e.storeLocked(&value)//更新二级缓存
} else {
//没有未更新的情况,同步
if !read.amended {
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked() //更新dirty,从一级缓存到二级缓存,read到dirty
m.read.Store(readOnly{m: read.m, amended: true})//重新标记有未更新的
}
m.dirty[key] = newEntry(value)//设置dirty
}
m.mu.Unlock()
}
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
// Avoid reporting a spurious miss if m.dirty got promoted while we were
// blocked on m.mu. (If further loads of the same key will not miss, it's
// not worth copying the dirty map for this key.)
//(复看是因为存在二级缓存向一级缓存同步数据的情况),有就返回对应value
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
m.missLocked()//这里会会进行读击穿次数检查,将dirty置换给read,并且dirty和miss会置空,miss下次会重新计数
}
m.mu.Unlock()
}
if !ok {
return nil, false
}
return e.load()
}
func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]//复检
if !ok && read.amended {//有同步情况则直接删除dirty中
e, ok = m.dirty[key]
delete(m.dirty, key)
// Regardless of whether the entry was present, record a miss: this key
// will take the slow path until the dirty map is promoted to the read
// map.
m.missLocked()//修改也会对miss新增
}
m.mu.Unlock()
}
if ok {//不存在有更新的情况直接删除read中的就行
return e.delete()
}
return nil, false
}
// Delete deletes the value for a key.
func (m *Map) Delete(key interface{}) {
m.LoadAndDelete(key)
}
func (e *entry) delete() (value interface{}, ok bool) {
for {
p := atomic.LoadPointer(&e.p)
if p == nil || p == expunged {
return nil, false
}
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
return *(*interface{})(p), true
}
}
}