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 进行遍历即可。