zid

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2025 License: MIT Imports: 12 Imported by: 4

README

🌟 Enhanced Snowflake ID Generator

A high-performance, zero-allocation, zero-dependency, low-latency, and customizable Snowflake ID generation library
Deeply optimized clock drift handling
Supports fully customizable bit layout
Balances flexibility and high availability.

中文文档

🔑 Use this library if you have any of the following requirements:

  • Flexible bit allocation for shorter globally unique IDs
  • Automatic clock drift protection with minimal impact on uniqueness
  • Overload tolerance under instantaneous high concurrency
  • Extremely high single-node concurrency
  • Massively scaled nodes or IoT scenarios
  • Automatic WorkerId assignment

🛠 Usage Guide

1. Install Dependency
go get github.com/zohu/zid
2. Configuration Reference
Field Name Type Description
BaseTime int64 Base timestamp in milliseconds; must not exceed current system time. Default: 2025-10-01
WorkerId int64 Machine identifier. Maximum value: (2^WorkerIdBitLength - 1)
WorkerIdBitLength byte Bit length for WorkerId. Default: 4. Range: [1~19, 'f'] (Requirement: SeqBitLength + WorkerIdBitLength ≤ 22). Setting to 'f' disables WorkerId.
SeqBitLength byte Bit length for sequence number. Default: 6. Range: [3~21, 22] (Requirement: SeqBitLength + WorkerIdBitLength ≤ 22). Only when WorkerIdBitLength='f' can this be set to 22.
MaxSeqNumber uint32 Maximum sequence number (inclusive). Range: [MinSeqNumber, 2^SeqBitLength - 1]. Default 0 means use max value (2^SeqBitLength - 1).
MinSeqNumber uint32 Minimum sequence number (inclusive). Default: 5. Range: [5, MaxSeqNumber]. The first 5 sequence numbers per millisecond (0–4) are reserved: 0 for manual assignment, 1–4 for clock drift fallback.
TopOverCostCount uint32 Maximum allowed drift count (inclusive). Default: 2000. Recommended range: 500–10000 (for high-concurrency fault tolerance).
ShardedMode bool High-performance single-node mode. Default: false. If enabled, WorkerId is ignored, and WorkerIdBitLength controls the number of shards.
3. Basic Usage
// Monolithic services can use directly (WorkerId defaults to 0)
zid.NextInt()           // → 1222633405189
zid.NextString()        // → "1222633405189"
zid.NextHex()           // → "11caaa13805" (hexadecimal)
zid.NextBase36()        // → "flo4d0n9" (Base36, short ID, case-insensitive)
zid.NextBase62()        // → "lwyFau1" (Base62, even shorter ID, case-sensitive)

// Want even shorter IDs? Customize WorkerIdBitLength and SeqBitLength.
// SeqBitLength affects QPS. If unsure, refer to test cases to find the best fit for your scenario.
// Shortest ID is achieved by setting BaseTime as late as possible, WorkerIdBitLength='f', and SeqBitLength=3.

// Parse ID information
zid.ExtractTime(id)           // → time.Time
zid.ExtractWorkerId(id)       // → int64
// Parsing also supports Hex / Base36 / Base62
zid.ExtractTimeHex("...")
zid.ExtractWorkerIdHex("...")

// Override global configuration (e.g., custom WorkerId or performance tuning)
zid.WithOptions(&zid.Options{"..."})
id := idGen.NextId()

// Create a separate instance, isolated from the global generator
idGen := NewDefaultIdGenerator(&zid.Options{"..."})
id := idGen.NextId()

// Create a sharded instance
idGen := NewShardedGenerator(&zid.Options{"..."})
id := idGen.NextId()
4. Large-Scale Nodes (Over 1024 nodes; standard Snowflake insufficient—e.g., edge computing or IoT devices. Full runtime required; TinyGo not supported)
// Increase WorkerIdBitLength, e.g., to 19 → supports up to 524,288 nodes
// You must balance WorkerIdBitLength and SeqBitLength yourself
5. Ultra-High Single-Node Concurrency (e.g., high-performance logging, event tracing, sensor streams—scenarios requiring reversible timestamp extraction)
// Option 1: Disable WorkerId entirely by setting WorkerIdBitLength='f'
// WorkerId bits are fully reallocated to SeqBitLength (22 bits), ensuring near-monotonic IDs
zid.WithOptions(&Options{
    WorkerIdBitLength: 'f',
    SeqBitLength:      22,
})

// Option 2: Option 1 uses locking, which degrades performance under concurrency.
// For concurrent scenarios, enable sharded mode for lock-free, allocation-free, atomic-free routing
// Uses fastrand for significantly higher concurrency performance; IDs remain roughly monotonic per millisecond
zid.WithOptions(&Options{
    WorkerIdBitLength: 16,
    SeqBitLength:      6,
    ShardedMode:       true,
})
4. Automatic WorkerId Assignment (Distributed Scenarios)

Built-in Redis-based Auto Assignment (recommended for bare metal or Docker):

zid.WithOptionsAndWorkerManager(
    zid.NewRedisManager(r redis.UniversalClient),
    &zid.Options{},
)
id := zid.NextId()

// You can also specify a key prefix
zid.WithOptionsAndWorkerManager(
    zid.NewRedisManager(r redis.UniversalClient, "zid"),
    &zid.Options{},
)

Built-in Kubernetes Lease + TTL Auto Cleanup (recommended for Kubernetes):

zid.WithOptionsAndWorkerManager(
    zid.NewKubernetesManager(&zid.KubernetesOptions{
        PodUID          string // Auto-read from POD_UID env var if unset
        Namespace       string // Auto-read from NAMESPACE env var if unset
        LeaseNamePrefix string // Defaults to "snowflake-" if unset
        Config          *rest.Config // Auto-read from InClusterConfig() if unset
    }),
    &zid.Options{},
)
id := zid.NextId()
  • Auto-inject UID and NAMESPACE (example):
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: your-ns
  name: your-name
spec:
  replicas: 5
  selector:
    matchLabels:
      app: your-app
  template:
    metadata:
      labels:
        app: your-app
    spec:
      containers:
      - name: app
        image: your-app:latest
        env:
        - name: POD_UID
          valueFrom:
            fieldRef:
              fieldPath: metadata.uid
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
  • RBAC Permissions (example):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: your-ns
  name: lease-manager
rules:
- apiGroups: ["coordination.k8s.io"]
  resources: ["leases"]
  verbs: ["get", "list", "create", "update"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: lease-manager-binding
  namespace: your-ns
subjects:
- kind: ServiceAccount
  name: default
roleRef:
  kind: Role
  name: lease-manager
  apiGroup: rbac.authorization.k8s.io

Custom Manager:

// Just implement the WorkerIdManager interface
type WorkerIdManager interface {
    Acquire(ctx context.Context, max int64) error // Acquire a WorkerId
    StartRenewal()  // Start auto-renewal
    Stop()          // Stop
    GetWorkerId() int64
}

// Example: Auto-assign based on IP + MAC
type IpMACWorkerIdManager struct {
    workerId int64
}
func NewIPMACWorkerIdManager() WorkerIdManager {
    return &IpMACWorkerIdManager{}
}
func (m *IpMACWorkerIdManager) Acquire(ctx context.Context, max int64) error{
    ip, mac := getPrimaryIPAndMAC()
    key := ip + "|" + mac
    hash := sha256.Sum256([]byte(key))
    m.workerId = int64(binary.BigEndian.Uint64(hash[:8]) % uint64(max+1))
    return nil
}
func (m *IpMACWorkerIdManager) StartRenewal() {}
func (m *IpMACWorkerIdManager) Stop() {}
func (m *IpMACWorkerIdManager) GetWorkerId() int64 {
    return m.workerId
}

Examples

snowflake_test.go:15: int64  id=1235891879941, len=13, time=2025-10-15 07:15:25.664 +0800 CST, workerId=0
snowflake_test.go:18: hex    id=11fc0e58006,   len=11, time=2025-10-15 07:15:25.664 +0800 CST, workerId=0
snowflake_test.go:21: base36 id=frre45qf,      len=8,  time=2025-10-15 07:15:25.664 +0800 CST, workerId=0
snowflake_test.go:24: base62 id=lL1Wnt6,       len=7,  time=2025-10-15 07:15:25.664 +0800 CST, workerId=0

Performance

goos: darwin
goarch: arm64
pkg: zid
cpu: Apple M1 Max
BenchmarkZid_Single_4_18-10         	19951174	        58.23 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Single_6_16-10         	20591342	        58.20 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Single_8_14-10         	20350092	        58.80 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Single_f_22-10         	20612992	        58.58 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Shard_8_14_10g-10      	33330787	        34.05 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Shard_10_12_50g-10     	33920462	        34.15 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Shard_12_10_100g-10    	39730275	        33.52 ns/op	       0 B/op	       0 allocs/op
BenchmarkZid_Shard_16_6_100g-10     	40579326	        26.91 ns/op	       0 B/op	       0 allocs/op

Future

  • Current version already meets most use cases. A CAS-based lock-free version shows similar concurrency performance and is pending optimization.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtractTime

func ExtractTime(id int64) time.Time

ExtractTime @Description: 提取ID时间 @param id @return time.Time

func ExtractTimeBase36

func ExtractTimeBase36(base36 string) time.Time

func ExtractTimeBase62

func ExtractTimeBase62(base62 string) time.Time

func ExtractTimeHex

func ExtractTimeHex(hex string) time.Time

func ExtractWorkerId

func ExtractWorkerId(id int64) int64

ExtractWorkerId @Description: 提取工作节点ID @param id @return int64

func ExtractWorkerIdBase36

func ExtractWorkerIdBase36(base36 string) int64

func ExtractWorkerIdBase62

func ExtractWorkerIdBase62(base62 string) int64

func ExtractWorkerIdHex

func ExtractWorkerIdHex(hex string) int64

func NextBase36

func NextBase36() string

NextBase36 @Description: 适用于不区分大小写场景 @return string

func NextBase62

func NextBase62() string

NextBase62 @Description: 适用于区分大小写场景 @return string

func NextHex

func NextHex() string

NextHex @Description: 16进制ID字符串 @return string

func NextInt

func NextInt() int64

NextInt @Description: 10进制ID @return int64

func NextString

func NextString() string

NextString @Description: 10进制ID字符串 @return string

func WithOptions

func WithOptions(options *Options)

func WithOptionsAndWorkerManager

func WithOptionsAndWorkerManager(manager WorkerIdManager, options *Options) error

Types

type DefaultIdGenerator

type DefaultIdGenerator struct {
	SnowWorker ISnowflake
}

func (DefaultIdGenerator) ExtractTime

func (dig DefaultIdGenerator) ExtractTime(id int64) time.Time

func (DefaultIdGenerator) ExtractWorkerId

func (dig DefaultIdGenerator) ExtractWorkerId(id int64) int64

func (DefaultIdGenerator) NextId

func (dig DefaultIdGenerator) NextId() int64

type ISnowflake

type ISnowflake interface {
	NextId() int64
	ExtractTime(int64) time.Time
	ExtractWorkerId(id int64) int64
}

func NewDefaultIdGenerator

func NewDefaultIdGenerator(options *Options) ISnowflake

func NewShardedGenerator

func NewShardedGenerator(options *Options) ISnowflake

func NewSnowflake

func NewSnowflake(options *Options) ISnowflake

type Options

type Options struct {
	BaseTime          int64  // 基础时间(ms单位),不能超过当前系统时间
	WorkerId          int64  // 机器码,最大值 2^WorkerIdBitLength-1
	WorkerIdBitLength byte   // 机器码位长,默认值4,取值范围 [1~19,f](要求:序列数位长+机器码位长不超过22)
	SeqBitLength      byte   // 序列数位长,默认值6,取值范围 [3~21,22](要求:序列数位长+机器码位长不超过22)
	MaxSeqNumber      uint32 // 最大序列数(含),设置范围 [MinSeqNumber, 2^SeqBitLength-1],默认值0,表示最大序列数取最大值(2^SeqBitLength-1])
	MinSeqNumber      uint32 // 最小序列数(含),默认值5,取值范围 [5, MaxSeqNumber],每毫秒的前5个序列数对应编号0-4是保留位,其中1-4是时间回拨相应预留位,0是手工新值预留位
	TopOverCostCount  uint32 // 最大漂移次数(含),默认2000,推荐范围500-10000(与计算能力有关)
	ShardedMode       bool   // 单机高性能模式,默认false,如果开启,WorkerId将被忽略,WorkerIdBitLength用来控制分片量
}

func (*Options) MaxWorkerIdNumber

func (o *Options) MaxWorkerIdNumber() int64

func (*Options) Validate

func (o *Options) Validate() error

type ShardedGenerator

type ShardedGenerator struct {
	// contains filtered or unexported fields
}

func (*ShardedGenerator) ExtractTime

func (s *ShardedGenerator) ExtractTime(id int64) time.Time

func (*ShardedGenerator) ExtractWorkerId

func (s *ShardedGenerator) ExtractWorkerId(id int64) int64

func (*ShardedGenerator) NextId

func (s *ShardedGenerator) NextId() int64

type Snowflake

type Snowflake struct {
	sync.Mutex
	// contains filtered or unexported fields
}

func (*Snowflake) CalcId

func (sw *Snowflake) CalcId(useTimeTick int64) int64

func (*Snowflake) CalcTurnBackId

func (sw *Snowflake) CalcTurnBackId(useTimeTick int64) int64

func (*Snowflake) ExtractTime

func (sw *Snowflake) ExtractTime(id int64) time.Time

func (*Snowflake) ExtractWorkerId

func (sw *Snowflake) ExtractWorkerId(id int64) int64

func (*Snowflake) GetCurrentTimeTick

func (sw *Snowflake) GetCurrentTimeTick() int64

func (*Snowflake) GetNextTimeTick

func (sw *Snowflake) GetNextTimeTick() int64

func (*Snowflake) NextId

func (sw *Snowflake) NextId() int64

func (*Snowflake) NextNormalId

func (sw *Snowflake) NextNormalId() int64

func (*Snowflake) NextOverCostId

func (sw *Snowflake) NextOverCostId() int64

type SnowflakeLockFree

type SnowflakeLockFree struct {
}

type WorkerIdManager

type WorkerIdManager interface {
	Acquire(ctx context.Context, max int64) error
	StartRenewal()
	Stop()
	GetWorkerId() int64
}

Jump to

Keyboard shortcuts

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