golang线程安全并发哈希映射支持自动分片扩展插件库cmap的使用
golang线程安全并发哈希映射支持自动分片扩展插件库cmap的使用
Go语言中的map
类型不支持并发读写操作。cmap
(concurrent-map)通过分片(map sharding)技术提供了一个高性能的解决方案,最大限度地减少了等待锁的时间。
与标准库的sync.Map
相比,cmap
更适合作为内存数据库使用,而sync.Map
更适合追加(append-only)场景。
安装使用
导入包:
import (
"github.com/lrita/cmap"
)
安装命令:
go get "github.com/lrita/cmap"
示例代码
基本使用
// 创建一个新的map
var m cmap.Cmap
// 存储键值对,将"bar"存储在"foo"键下
m.Store("foo", "bar")
// 从map中检索值
if tmp, ok := m.Load("foo"); ok {
bar := tmp.(string) // 需要类型断言
}
// 删除"foo"键下的项
m.Delete("foo")
使用泛型实现(Go 1.18+)
// 使用泛型创建一个新的map
var n cmap.Map[string, string]
// 存储键值对,将"bar"存储在"foo"键下
n.Store("foo", "bar")
// 从map中检索值
if tmp, ok := n.Load("foo"); ok {
bar := tmp // 不需要类型断言
}
// 删除"foo"键下的项
n.Delete("foo")
性能基准测试
以下是cmap与其他并发map实现的性能对比:
goos: darwin
goarch: amd64
pkg: github.com/lrita/cmap
BenchmarkLoadMostlyHits/*cmap_test.DeepCopyMap-4 50000000 34.5 ns/op
BenchmarkLoadMostlyHits/*cmap_test.RWMutexMap-4 20000000 65.2 ns/op
BenchmarkLoadMostlyHits/*sync.Map-4 50000000 34.8 ns/op
BenchmarkLoadMostlyHits/*cmap.Cmap-4 30000000 53.5 ns/op
BenchmarkLoadMostlyMisses/*cmap_test.DeepCopyMap-4 50000000 26.7 ns/op
BenchmarkLoadMostlyMisses/*cmap_test.RWMutexMap-4 20000000 62.5 ns/op
BenchmarkLoadMostlyMisses/*sync.Map-4 50000000 22.7 ns/op
BenchmarkLoadMostlyMisses/*cmap.Cmap-4 30000000 40.3 ns/op
BenchmarkLoadOrStoreBalanced/*cmap_test.RWMutexMap-4 3000000 437 ns/op
BenchmarkLoadOrStoreBalanced/*sync.Map-4 3000000 546 ns/op
BenchmarkLoadOrStoreBalanced/*cmap.Cmap-4 3000000 497 ns/op
BenchmarkLoadOrStoreUnique/*cmap_test.RWMutexMap-4 2000000 990 ns/op
BenchmarkLoadOrStoreUnique/*sync.Map-4 1000000 1032 ns/op
BenchmarkLoadOrStoreUnique/*cmap.Cmap-4 2000000 892 ns/op
BenchmarkLoadOrStoreCollision/*cmap_test.DeepCopyMap-4 100000000 18.2 ns/op
BenchmarkLoadOrStoreCollision/*cmap_test.RWMutexMap-4 10000000 165 ns/op
BenchmarkLoadOrStoreCollision/*sync.Map-4 100000000 19.6 ns/op
BenchmarkLoadOrStoreCollision/*cmap.Cmap-4 20000000 65.7 ns/op
BenchmarkRange/*cmap_test.DeepCopyMap-4 200000 8646 ns/op
BenchmarkRange/*cmap_test.RWMutexMap-4 20000 62046 ns/op
BenchmarkRange/*sync.Map-4 200000 9317 ns/op
BenchmarkRange/*cmap.Cmap-4 50000 31107 ns/op
BenchmarkAdversarialAlloc/*cmap_test.DeepCopyMap-4 2000000 531 ns/op
BenchmarkAdversarialAlloc/*cmap_test.RWMutexMap-4 20000000 74.3 ns/op
BenchmarkAdversarialAlloc/*sync.Map-4 5000000 390 ns/op
BenchmarkAdversarialAlloc/*cmap.Cmap-4 30000000 53.6 ns/op
BenchmarkAdversarialDelete/*cmap_test.DeepCopyMap-4 5000000 273 ns/op
BenchmarkAdversarialDelete/*cmap_test.RWMutexMap-4 20000000 94.4 ns/op
BenchmarkAdversarialDelete/*sync.Map-4 10000000 137 ns/op
BenchmarkAdversarialDelete/*cmap.Cmap-4 30000000 43.3 ns/op
从基准测试结果可以看出,cmap在大多数场景下提供了不错的性能表现,特别是在删除操作上表现优异。
更多关于golang线程安全并发哈希映射支持自动分片扩展插件库cmap的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang线程安全并发哈希映射支持自动分片扩展插件库cmap的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang线程安全并发哈希映射: cmap库使用指南
cmap是一个Go语言实现的并发安全的分片哈希映射库,它通过分片技术减少锁竞争,在高并发场景下提供更好的性能表现。下面详细介绍cmap的使用方法。
基本特性
- 线程安全的并发访问
- 自动分片,减少锁竞争
- 支持泛型(Go 1.18+)
- 简单的API设计
- 高性能
安装
go get github.com/orcaman/concurrent-map/v2
基本使用
创建map
import cmap "github.com/orcaman/concurrent-map/v2"
// 创建一个键为string,值为int的并发map
m := cmap.New[int]()
基本操作
// 设置键值对
m.Set("key1", 100)
// 获取值
value, ok := m.Get("key1")
if ok {
fmt.Println("value:", value)
}
// 删除键
m.Remove("key1")
// 检查键是否存在
if m.Has("key1") {
fmt.Println("key1 exists")
} else {
fmt.Println("key1 does not exist")
}
原子操作
// 如果键不存在则设置
m.SetIfAbsent("key1", 200)
// 原子性增加数值(对数值类型有效)
m.Upsert("counter", 1, func(exist bool, valueInMap int, newValue int) int {
if exist {
return valueInMap + newValue
}
return newValue
})
高级用法
遍历map
// 使用Iter()方法遍历
for item := range m.Iter() {
fmt.Printf("Key: %s, Value: %v\n", item.Key, item.Val)
}
// 使用回调函数遍历
m.IterCb(func(key string, v int) {
fmt.Printf("Key: %s, Value: %v\n", key, v)
})
自定义分片函数
// 自定义哈希函数
m := cmap.NewWithCustomShardingFunction[int](func(key string) uint32 {
// 实现你自己的哈希逻辑
return someHashFunction(key)
})
// 或者指定分片数量
m := cmap.NewWithShards[int](32) // 创建32个分片的map
性能优化技巧
- 根据预估的键数量设置适当的分片数
- 对于热点键,可以考虑使用单独的sync.Map
- 批量操作时尽量使用原子操作
完整示例
package main
import (
"fmt"
"sync"
"time"
cmap "github.com/orcaman/concurrent-map/v2"
)
func main() {
// 创建一个并发map
m := cmap.New[string]()
// 启动多个goroutine并发读写
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
key := fmt.Sprintf("key_%d", id)
value := fmt.Sprintf("value_%d", id)
// 设置值
m.Set(key, value)
// 模拟一些处理
time.Sleep(time.Millisecond * 100)
// 获取值
if v, ok := m.Get(key); ok {
fmt.Printf("Goroutine %d got value: %s\n", id, v)
}
// 原子更新
m.Upsert(key, "new_"+value, func(exist bool, oldVal string, newVal string) string {
if exist {
return oldVal + "_updated"
}
return newVal
})
}(i)
}
wg.Wait()
// 遍历所有元素
fmt.Println("\nAll items in map:")
m.IterCb(func(key string, val string) {
fmt.Printf("%s: %s\n", key, val)
})
// 获取map大小
fmt.Println("\nMap size:", m.Count())
}
与标准库sync.Map比较
-
cmap优势:
- 更友好的API设计
- 支持泛型
- 对写密集型操作性能更好
- 提供更多实用方法(Iter, Count等)
-
sync.Map优势:
- 标准库内置,无需额外依赖
- 对读多写少的场景更高效
- 更节省内存
总结
cmap是一个优秀的并发安全哈希映射实现,特别适合高并发写操作的场景。通过分片技术有效减少了锁竞争,同时提供了简洁易用的API。对于需要频繁写入的并发字典需求,cmap是一个值得考虑的选择。