GO:sync.Map

sync.Map 是 Go 语言 sync 包中提供的一个内置的并发安全的 map 类型。它在设计上考虑了高并发场景,尽量避免加锁操作从而提升读写性能。

这里是一段关于sync.map使用的简单记录

Go 复制代码
package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    // 存储
    m.Store("key1", "value1")
    m.Store("key2", 2)
    // 读取
    value, ok := m.Load("key1")
    if ok {
        fmt.Println("Value:", value)
    }
    // 删除
    m.Delete("key1")
    value, ok = m.Load("key1")
    if ok {
        fmt.Println("Value:", value)
    } else {
        fmt.Println("Key not found")
    }
}

sync.map的底层数据结构 最主要的构成两个部分

1 read 无锁map 存储了部分写入 Map 的内容,用来加速读操作。

2 dirty 加锁读写map 存储了全量内容,需要加锁才能读写数据。

Go 复制代码
type Map struct {
    mu Mutex
    read atomic.Pointer[readOnly] // 无锁读map
    dirty map[any]*entry // 加锁读写map
    misses int
}

// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
    m       map[any]*entry
    amended bool // true if the dirty map contains some key not in m.
}

当有 key-value 值写入时:

如果这个 key 在 read 中不存在,接下来就要做新增操作,它会加锁写入 dirty map 中,并且将 amended 标记设置为 true。而 amended 标记用于表示 dirty 中是否有不在 read 中的 key-value 值。

如果这个 key 在 read 中存在,则会进行更新操作,由于 read map 和 dirty map 里面存储的值是 entry 类型的指针,且 entry 类型的成员变量也是 atomic.Pointer 类型

因此在更新时可以直接用 CAS 无锁操作替换指针 p 指向的变量,而无需做加锁操作。
当读取 key 对应的值时:

会先从 read 中读取,当 read 中读不到,并且 amended 为 true 时,则会加锁从 dirty map 中读。这里可能导致从 sync.Map 读取的性能劣化,因为它既要从 read 中读一遍,又要加锁从 dirty map 中读一遍。

同时,每次 read 读不到,从 dirty map 中读时,它会调用 missLocked 方法,这个方法用于将 map 的 misses 字段加 1,misses 字段用于表示 read 读未命中次数,如果 misses 值比较大,说明 read map 的数据可能比 dirty map 少了很多。为了提升读性能,missLocked 方法里会将 dirty map 变成新的 read map,代码如下。
缺点:

sync.Map 是通过两个 map 来实现读写分离,从而达到高性能读的目的。不过它存在下面几个缺点。

1 由于有两个 map,因此占用内存会比较高。

2 更适用于读多写少的场景,当由于写比较多或者本地缓存没有全量数据时,会导致读 map 经常读不到数据,而需要加锁再读一次,从而导致读性能退化。

3 当数据量比较大时,如果写入触发读 map 向写 map 拷贝,会导致较大的性能开销。

所以在大规模数据缓存时 我们最好选用 分段锁

相关推荐
郑州光合科技余经理10 分钟前
海外国际版同城服务系统开发:PHP技术栈
java·大数据·开发语言·前端·人工智能·架构·php
Yorelee.12 分钟前
ms-swift在训练时遇到的部分问题及解决方案
开发语言·nlp·transformer·swift
行走的bug...15 分钟前
python项目管理
开发语言·python
appearappear20 分钟前
Mac 上重新安装了Cursor 2.2.30,重新配置 springboot 过程记录
java·spring boot·后端
CryptoRzz29 分钟前
日本股票 API 对接实战指南(实时行情与 IPO 专题)
java·开发语言·python·区块链·maven
yugi98783830 分钟前
基于M序列的直扩信号扩频码生成方法及周期长码直扩信号的MATLAB实现方案
开发语言·matlab
谷哥的小弟36 分钟前
Spring Framework源码解析——RequestContext
java·后端·spring·框架·源码
乾元37 分钟前
基于时序数据的异常预测——短期容量与拥塞的提前感知
运维·开发语言·网络·人工智能·python·自动化·运维开发
江上清风山间明月38 分钟前
使用python将markdown文件生成pdf文件
开发语言·python·pdf
j_xxx404_41 分钟前
C++算法入门:二分查找合集(二分查找|在排序数组中查找元素的第一个和最后一个位置)
开发语言·c++