sync.Map是一个线程安全的map结构,一般用于多读少写的并发操作,下图是sync.Map的数据结构

图引至码农桃花源公众号
type Map struct { |
mu是Map的互斥锁用于对并发操作进行加锁保护,read是用于存储只读内容的,可以提供高并发的读操作。 dirty是一个原始的map结构体,对dirty的操作需要加锁,dirty包涵了全量的数据,在读数据的时候会先读取read,read读取不到再读dirty。 misses 是read读取失败的次数,当多次读取失败后 misses 累计特定值,dirty就会升级成read。sync.Map 这里采用的策略类似数据库常用的”读写分离”,技术都是相通的O(∩_∩)O
sync.Map用法
func main() { |
官方的单元测试用例
从官方单元测试学习 sync.Map 使用:
func TestConcurrentRange(t *testing.T) { |
原理结构
这里从几个方法的源码展开讲解:
Load
func (m *Map) Load(key interface{}) (value interface{}, ok bool) { |
Load方法讲解:首先从只读字段read中读取键值的内容,如果没读到,并且amended为true(dirty有read没有数据)则尝试从dirty中读取,不过这里要做 double check, 然后将此次缓存穿透记录一次到miss字段。
Store
func (m *Map) Store(key, value interface{}) { |
Store方法讲解:存储之前先从只读字段 read 中读取要存储的值,在read中存在键值对的时候,则用 CAS 的方式将新的值存储进去,如果不存在则加锁做个 double check,将新数据写入 dirty 中。如果 dirty 和 read 中都没数据,dirty 和 read 的键值不同步,则将数据直接写入 dirty, 如果 dirty 键值数据和 read 一样,同时 dirty 为 nil ,将 read 浅拷贝 一份到 dirty,为后面赋值可以同时写入 dirty 和 read。
Delete
func (m *Map) Delete(key interface{}) { |
Delete方法讲解:sync.Map的 Delete 方法本质是用的 读取和删除,也就是先读取到数据再对数据进行删除,读的方法和 Load 的方法是一样的,这里就不再赘述了。
Range
func (m *Map) Range(f func(key, value interface{}) bool) { |
Range原理讲解:Range 本质是通过遍历只读字段 read 得到,为了让只读字段包含所有数据,当 dirty 和 read 不相等的时候,将 dirty 升级为 read, 最后再对 read 进行遍历即可。