shardmap

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README

shard-map

shard-map 是一个基于 Go 泛型的并发安全分片 Map,适合读写并发较高的内存缓存、计数器和会话状态场景。

特性

  • 32 个独立分片锁,降低读写竞争。
  • 仅支持具有内建安全哈希的键类型:stringbool、整数、无符号整数、uintptr、浮点数和复数。
  • 字符串哈希按逻辑长度逐块读取,不会访问字符串边界之外的内存。
  • 零值可用;内部 map 与 Range 缓冲区按需初始化。
  • Range 在锁外调用回调,避免长回调阻塞写入。

结构体、指针、interface,以及定义的新类型(例如 type ID string)不能作为键。语言别名可以使用,例如 type ID = string

安装

go get github.com/451008604/shard-map

使用

package main

import (
	"fmt"

	shardmap "github.com/451008604/shard-map"
)

func main() {
	var m shardmap.ShardMap[string, int]
	m.Set("foo", 42)

	if value, ok := m.Get("foo"); ok {
		fmt.Println(value)
	}

	m.Compute("requests", func(old int, loaded bool) int {
		if loaded {
			return old + 1
		}
		return 1
	})
}

NewShardMap[K, V]() 也可用于创建实例,但不是必需的。

API 语义

方法 行为
Set / Get / Delete 单键并发安全读写。
LoadOrStore 原子地读取已有值或写入新值。
LoadOrCompute 计算函数在锁外执行;竞争时可能被调用多次,最终仅一个值被存储。
Compute 原子读-改-写;回调在对应分片写锁内执行,不能重入可能访问同一分片的方法。
Swap 原子替换并返回旧值及存在标志。
Len 无锁汇总分片计数;与并发写入同时调用时不是全局一致快照。
Range 逐分片快照遍历;回调返回 false 可提前停止,不保证全局一致快照。

验证与基准

在仓库根目录运行:

go test ./...
go test -race ./...
go vet ./...
go test -run '^$' -fuzz '^FuzzShardMapEqualStringsUseSameShard$' -fuzztime=20s
go test -run '^$' -fuzz '^FuzzShardMapOperations$' -fuzztime=20s
go test -bench=. -benchmem ./...

基准结果依赖 Go 版本、CPU、并发度和键分布。修改哈希或锁策略后,请重新测量,不要沿用历史数据。

Docker 基准结果

以下结果采集于 2026-07-20,环境为 Go 1.21.13、Linux/arm64、4 vCPU、GOMAXPROCS=4。镜像为 golang:1.21.13-bookworm(digest sha256:c6a5b9308b3f3095e8fde83c8bf4d68bd101fce606c1a0a1394522542509dda9)。 每个基准运行 5 次、每次至少 500 ms;表中为 ns/op 的中位数,越低越好。

复现命令:

docker run --rm --cpus=4 -e GOMAXPROCS=4 \
  -v "$PWD:/src:ro" -w /src \
  golang@sha256:c6a5b9308b3f3095e8fde83c8bf4d68bd101fce606c1a0a1394522542509dda9 \
  go test -run '^$' -bench=. -benchmem -benchtime=500ms -count=5 ./...

单线程操作:

操作 ShardMap 单锁 map
Set 229.2 171.6
Get 28.45 21.70
Delete 148.7 134.3

固定 worker 数的并发操作:

Worker ShardMap 写 单锁 map 写 写入加速比 ShardMap 读 单锁 map 读 读取加速比
1 41.49 29.33 0.71x 31.65 23.86 0.75x
4 55.00 85.83 1.56x 24.69 88.75 3.59x
16 59.78 119.1 1.99x 25.75 85.73 3.33x
64 69.48 158.7 2.28x 25.95 84.31 3.25x
256 70.69 155.3 2.20x 28.19 81.70 2.90x
1024 70.11 271.3 3.87x 25.79 88.21 3.42x

Range 遍历:

键数量 ns/op allocs/op
100 2,176 0
1,000 13,301 0
10,000 112,513 0

加速比按“单锁 map / ShardMap”计算,大于 1 表示 ShardMap 更快。这些数字仅代表上述容器资源和当前测试键分布;单线程路径会承担分片选择和长度计数成本,并发收益会随 CPU、调度、读写比例及热点键分布变化。

许可证

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Key added in v1.3.0

type Key interface {
	bool | string | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | uintptr | float32 | float64 | complex64 | complex128
}

Key is the set of key types with a built-in, equality-compatible hash. Defined types, structs, pointers, and interfaces are intentionally excluded.

type ShardMap added in v1.1.0

type ShardMap[K Key, V any] struct {
	// contains filtered or unexported fields
}

ShardMap 为一个拥有 32 个分片的并发安全 map。零值可直接使用。 每个分片拥有独立的读写锁,以降低竞争并实现高并发读写。 键通过与 Go 相等语义一致的哈希均匀分布到各分片。

func NewShardMap added in v1.1.0

func NewShardMap[K Key, V any]() *ShardMap[K, V]

NewShardMap 创建一个空的 ShardMap 实例。

func (*ShardMap[K, V]) Compute added in v1.2.0

func (sm *ShardMap[K, V]) Compute(key K, fn func(old V, loaded bool) V) V

Compute 原子地对键执行读-修改-写操作。 fn 接收旧值和是否存在标志,返回新值。 新值总是被存储,fn 的返回值不应为零值(除非有意存储零值)。 fn 在持有对应分片写锁时执行,以保证整个读-修改-写操作原子;它不得 调用同一个 ShardMap 上可能访问同一分片的方法。

func (*ShardMap[K, V]) Delete added in v1.1.0

func (sm *ShardMap[K, V]) Delete(key K)

Delete 删除对应分片中的键,使用写锁。

func (*ShardMap[K, V]) Get added in v1.1.0

func (sm *ShardMap[K, V]) Get(key K) (V, bool)

Get 从对应分片读取键值,使用读锁以支持并发读取。

func (*ShardMap[K, V]) Len added in v1.1.0

func (sm *ShardMap[K, V]) Len() int

Len 返回整个 ShardMap 中所有键的总数。 使用原子计数器,无需获取任何锁;与并发写入同时调用时,它不是全局一致快照。

func (*ShardMap[K, V]) LoadOrCompute added in v1.2.0

func (sm *ShardMap[K, V]) LoadOrCompute(key K, fn func() V) (actual V, loaded bool)

LoadOrCompute 原子地获取或计算键值对。 如果键已存在,返回现有值和 true;否则调用 fn 计算值,存储并返回。 fn 可能不会被调用(如果另一个 goroutine 先插入了值),也可能在 并发竞争时被多个 goroutine 调用。fn 在不持有分片锁时执行,避免回调重入死锁。

func (*ShardMap[K, V]) LoadOrStore added in v1.2.0

func (sm *ShardMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool)

LoadOrStore 原子地获取或存储键值对。 如果键已存在,返回现有值和 true;否则存储新值并返回新值和 false。 使用先读后写模式避免不必要的写锁竞争。

func (*ShardMap[K, V]) Range added in v1.1.0

func (sm *ShardMap[K, V]) Range(fn func(key K, value V) bool)

Range 以并发安全的方式遍历所有键值对。 每个分片的数据在持有读锁期间被复制出来,回调函数在释放读锁后执行, 避免长耗时回调阻塞写操作或导致死锁。 使用 sync.Pool 复用 entry slice,减少内存分配和 GC 压力。

func (*ShardMap[K, V]) Set added in v1.1.0

func (sm *ShardMap[K, V]) Set(key K, value V)

Set 将键值对写入对应分片,使用写锁保证互斥。

func (*ShardMap[K, V]) Swap added in v1.2.0

func (sm *ShardMap[K, V]) Swap(key K, value V) (previous V, loaded bool)

Swap 原子地替换键的值,返回旧值和是否存在的标志。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL