一、内部结构
sync.Map
主要由两个数据结构组成:
- 一个原生的
map
:用于存储实际的键值对。这个map
在读取时无需加锁,但在写入时需要加锁。 - 一个
read
结构体:包含一个只读的原生map
(称为dirty map
)和一个misses
计数器。这个read
结构体在读取操作时被优先使用,因为读取它无需加锁,只有在发生读未命中且misses
计数达到一定阈值时,才会将dirty map
中的内容写入到主map
中并重置read
结构体。
二、读写操作
-
读操作:
- 首先尝试从
read
结构体中的dirty map
读取键对应的值。如果找到了值,直接返回。 - 如果在
dirty map
中未找到值,再尝试从主map
中读取。如果找到了值,更新read
结构体中的dirty map
并返回值。 - 如果在主
map
中也未找到值,增加misses
计数器。如果misses
计数器达到一定阈值,会将dirty map
中的内容写入主map
,并重置read
结构体和misses
计数器。
- 首先尝试从
-
写操作:
- 首先锁定主
map
,然后进行写入操作。 - 同时,如果
read
结构体中的dirty map
为空,将主map
复制一份到dirty map
中,以便后续的读操作可以更快地访问。
- 首先锁定主
三、并发安全
一、读写分离设计
-
内部包含两个主要的数据结构:
- 一个是普通的
map
,用于存储实际的键值对,称为 “dirty map”。这个map
在写操作时会被锁定,以确保写操作的原子性和一致性。 - 另一个是只读的
map
结构,在执行读操作时优先从这个结构中读取数据,无需加锁,从而允许多个线程并发读取。
- 一个是普通的
二、写操作的同步机制
-
互斥锁保护:
- 当进行写操作(存储新的键值对、更新现有键值对或删除键值对)时,会获取一个互斥锁,确保同一时刻只有一个线程可以对 “dirty map” 进行写操作。
- 这样可以防止多个线程同时修改
map
时可能导致的数据不一致和竞争条件。
三、读操作的优化与同步
-
优先从只读map读取:
- 读操作首先尝试从只读的
map
中获取键对应的值,因为这个map
不需要加锁,所以多个线程可以同时进行读操作,提高了读操作的并发性能。
- 读操作首先尝试从只读的
-
动态更新只读map:
- 如果读操作在只读
map
中未找到键对应的值,会尝试从 “dirty map” 中读取。如果在 “dirty map” 中找到了值,会将这个值更新到只读map
中,以便后续的读操作可以更快地获取到这个值。
- 如果读操作在只读
-
misses计数器:
- 每次读操作在只读
map
和 “dirty map” 中都未找到值时,会增加一个misses
计数器。当misses
计数器达到一定阈值时,会触发将 “dirty map” 中的内容复制到只读map
中,并重置misses
计数器。这个机制确保了在一段时间内如果读操作频繁未命中,会动态地更新只读map
,以提高后续读操作的效率。
- 每次读操作在只读
四、删除操作的安全处理
标记删除:
- 当执行删除操作时,会在 “dirty map” 中标记键值对为已删除状态。这样在后续的读操作中,如果在只读
map
中未找到键对应的值,并且在 “dirty map” 中发现该键被标记为已删除,就可以确定该键不存在于map
中。
...