当前位置: 首页 > article >正文

GO:sync.Map

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

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

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 存储了全量内容,需要加锁才能读写数据。

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 拷贝,会导致较大的性能开销。

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

 


http://www.kler.cn/a/473392.html

相关文章:

  • 【电子通识】PWM驱动让有刷直流电机恒流工作
  • SpringBoot3动态切换数据源
  • 单元测试MockitoExtension和SpringExtension
  • Apache Traffic存在SQL注入漏洞(CVE-2024-45387)
  • 卷积神经网络 (CNN, Convolutional Neural Network) 算法详解与PyTorch实现
  • 基于LabVIEW的BeamGage自动化接口应用
  • springboot远程链接spark
  • 力扣 34. 在排序数组中查找元素的第一个和最后一个位置
  • SpringBoot3动态切换数据源
  • 基于springboot的网上商城购物系统
  • 2025.1.8(c++对c语言的扩充——堆区空间,引用,函数)
  • Mysql面试相关
  • 使用 vue3 赋值后视图没变化的问题
  • 蓝桥杯训练
  • T-SQL语言的语法
  • 使用 SQLite3 的基本操作步骤
  • Azkaban其一,介绍、体系架构和安装
  • Linux-----结构体与联合体,大小端模式
  • 高等数学学习笔记 ☞ 函数的求导法则
  • Maven核心与单元测试
  • Linux-Ubuntu之I2C通信
  • iOS 逆向学习 - iOS Architecture Media Layer
  • Ubuntu 上安装 Docker
  • Kotlin OpenCV 画画
  • QPS和TPS 的区别是什么?QPS 大了会有什么问题,怎么解决?
  • Java基础概念