一、内部结构
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中。
...