一、内部结构

sync.Map主要由两个数据结构组成:

  1. 一个原生的map:用于存储实际的键值对。这个map在读取时无需加锁,但在写入时需要加锁。
  2. 一个read结构体:包含一个只读的原生map(称为dirty map)和一个misses计数器。这个read结构体在读取操作时被优先使用,因为读取它无需加锁,只有在发生读未命中且misses计数达到一定阈值时,才会将dirty map中的内容写入到主map中并重置read结构体。

二、读写操作

  1. 读操作:

    • 首先尝试从read结构体中的dirty map读取键对应的值。如果找到了值,直接返回。
    • 如果在dirty map中未找到值,再尝试从主map中读取。如果找到了值,更新read结构体中的dirty map并返回值。
    • 如果在主map中也未找到值,增加misses计数器。如果misses计数器达到一定阈值,会将dirty map中的内容写入主map,并重置read结构体和misses计数器。
  2. 写操作:

    • 首先锁定主map,然后进行写入操作。
    • 同时,如果read结构体中的dirty map为空,将主map复制一份到dirty map中,以便后续的读操作可以更快地访问。

三、并发安全

一、读写分离设计

  1. 内部包含两个主要的数据结构:
    • 一个是普通的map,用于存储实际的键值对,称为 “dirty map”。这个map在写操作时会被锁定,以确保写操作的原子性和一致性。
    • 另一个是只读的map结构,在执行读操作时优先从这个结构中读取数据,无需加锁,从而允许多个线程并发读取。

二、写操作的同步机制

  1. 互斥锁保护:
    • 当进行写操作(存储新的键值对、更新现有键值对或删除键值对)时,会获取一个互斥锁,确保同一时刻只有一个线程可以对 “dirty map” 进行写操作。
    • 这样可以防止多个线程同时修改map时可能导致的数据不一致和竞争条件。

三、读操作的优化与同步

  1. 优先从只读map读取:

    • 读操作首先尝试从只读的map中获取键对应的值,因为这个map不需要加锁,所以多个线程可以同时进行读操作,提高了读操作的并发性能。
  2. 动态更新只读map:

    • 如果读操作在只读map中未找到键对应的值,会尝试从 “dirty map” 中读取。如果在 “dirty map” 中找到了值,会将这个值更新到只读map中,以便后续的读操作可以更快地获取到这个值。
  3. misses计数器:

    • 每次读操作在只读map和 “dirty map” 中都未找到值时,会增加一个misses计数器。当misses计数器达到一定阈值时,会触发将 “dirty map” 中的内容复制到只读map中,并重置misses计数器。这个机制确保了在一段时间内如果读操作频繁未命中,会动态地更新只读map,以提高后续读操作的效率。

四、删除操作的安全处理

标记删除:

  • 当执行删除操作时,会在 “dirty map” 中标记键值对为已删除状态。这样在后续的读操作中,如果在只读map中未找到键对应的值,并且在 “dirty map” 中发现该键被标记为已删除,就可以确定该键不存在于map中。