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

性能优化技巧

  1. 根据预估的键数量设置适当的分片数
  2. 对于热点键,可以考虑使用单独的sync.Map
  3. 批量操作时尽量使用原子操作

完整示例

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比较

  1. cmap优势

    • 更友好的API设计
    • 支持泛型
    • 对写密集型操作性能更好
    • 提供更多实用方法(Iter, Count等)
  2. sync.Map优势

    • 标准库内置,无需额外依赖
    • 对读多写少的场景更高效
    • 更节省内存

总结

cmap是一个优秀的并发安全哈希映射实现,特别适合高并发写操作的场景。通过分片技术有效减少了锁竞争,同时提供了简洁易用的API。对于需要频繁写入的并发字典需求,cmap是一个值得考虑的选择。

回到顶部