package main import ( "errors" "fmt" "sync" "time" ) const ( // 位数分配 (Bit Allocation) timestampBits uint8 = 41 businessIDBits uint8 = 3 workerIDBits uint8 = 10 sequenceBits uint8 = 9 // 64 - 1 (sign) - 41 (timestamp) - 3 (business) - 10 (worker) = 9 // 最大值 (Max Values) maxBusinessID int64 = -1 ^ (-1 << businessIDBits) // 2^3 - 1 = 7 maxWorkerID int64 = -1 ^ (-1 << workerIDBits) // 2^10 - 1 = 1023 maxSequence int64 = -1 ^ (-1 << sequenceBits) // 2^9 - 1 = 511 // 位移量 (Bit Shifts) workerIDShift = sequenceBits // 9 businessIDShift = sequenceBits + workerIDBits // 9 + 10 = 19 timestampShift = sequenceBits + workerIDBits + businessIDBits // 9 + 10 + 3 = 22 // 自定义纪元 (Epoch), 单位毫秒. (可以设置为项目上线的日期) // 例如: 2025-01-01 00:00:00 UTC customEpoch int64 = 1735689600000 // time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli() ) // ErrClockMovedBackwards indicates that the system clock moved backwards. var ErrClockMovedBackwards = errors.New("clock moved backwards, refusing to generate id") // ErrInvalidBusinessID indicates the business ID is out of range. var ErrInvalidBusinessID = fmt.Errorf("business ID must be between 0 and %d", maxBusinessID) // ErrInvalidWorkerID indicates the worker ID is out of range. var ErrInvalidWorkerID = fmt.Errorf("worker ID must be between 0 and %d", maxWorkerID) // Generator is the core ID generator structure. type Generator struct { mu sync.Mutex lastTimestamp int64 workerID int64 businessID int64 sequence int64 } // NewGenerator creates a new ID generator instance. func NewGenerator(workerID, businessID int64) (*Generator, error) { if workerID < 0 || workerID > maxWorkerID { return nil, ErrInvalidWorkerID } if businessID < 0 || businessID > maxBusinessID { return nil, ErrInvalidBusinessID } return &Generator{ workerID: workerID, businessID: businessID, }, nil } // currentTimeMillis returns the current time in milliseconds since the custom epoch. func currentTimeMillis() int64 { return time.Now().UnixMilli() - customEpoch } // tilNextMillis waits until the next millisecond. func tilNextMillis(lastTimestamp int64) int64 { timestamp := currentTimeMillis() for timestamp <= lastTimestamp { // Spin wait is generally discouraged, but for millisecond precision, // time.Sleep(1 * time.Millisecond) might overshoot too much. // A brief sleep can reduce CPU churn if clock skew is minor. time.Sleep(time.Microsecond * 100) // Sleep briefly timestamp = currentTimeMillis() } return timestamp } // NextID generates the next unique ID. func (g *Generator) NextID() (int64, error) { g.mu.Lock() defer g.mu.Unlock() timestamp := currentTimeMillis() // 时钟回拨检查 (Clock moved backwards check) if timestamp < g.lastTimestamp { // 可以选择: // 1. 返回错误 (Recommended for safety) return 0, fmt.Errorf("%w: current: %d, last: %d", ErrClockMovedBackwards, timestamp, g.lastTimestamp) // 2. 等待时钟追上 (Potentially blocks, less safe if clock jump is large) // timestamp = tilNextMillis(g.lastTimestamp) } // 同一毫秒内 (Within the same millisecond) if timestamp == g.lastTimestamp { g.sequence = (g.sequence + 1) & maxSequence // 序列号溢出,等待下一毫秒 (Sequence overflow, wait for next millisecond) if g.sequence == 0 { timestamp = tilNextMillis(g.lastTimestamp) // 等待后重置序列号 (Reset sequence after waiting) // g.sequence = 0 // Reset is implicit as it overflowed to 0 } } else { // 新的毫秒,重置序列号 (New millisecond, reset sequence) g.sequence = 0 } // 更新最后时间戳 (Update last timestamp) g.lastTimestamp = timestamp // 组合 ID (Assemble the ID) id := (timestamp << timestampShift) | // 时间戳左移 (g.businessID << businessIDShift) | // 业务ID左移 (g.workerID << workerIDShift) | // Worker ID左移 g.sequence // 序列号 return id, nil } // ParseID decomposes an ID into its components. Useful for debugging or analysis. func ParseID(id int64) (timestampMsSinceEpoch int64, businessID int64, workerID int64, sequence int64, genTimeUTC time.Time) { timestampMsSinceEpoch = (id >> timestampShift) & (int64(-1) ^ (int64(-1) << timestampBits)) businessID = (id >> businessIDShift) & maxBusinessID workerID = (id >> workerIDShift) & maxWorkerID sequence = id & maxSequence // Calculate generation time in UTC genTimeUTC = time.UnixMilli(timestampMsSinceEpoch + customEpoch).UTC() return } func main() { // !!! IMPORTANT: Worker ID and Business ID MUST be unique per instance/purpose !!! // These should typically come from configuration. workerID := int64(1) // Example: Get from config/env businessIDOrder := int64(1) // Example: 1 for Order businessIDPayment := int64(2) // Example: 2 for Payment // Create generators for different business types orderGenerator, err := NewGenerator(workerID, businessIDOrder) if err != nil { panic(err) } paymentGenerator, err := NewGenerator(workerID, businessIDPayment) if err != nil { panic(err) } // Generate some IDs for i := 0; i < 5; i++ { orderID, err := orderGenerator.NextID() if err != nil { fmt.Println("Error generating order ID:", err) time.Sleep(1 * time.Millisecond) // Wait before retry on clock issues continue } fmt.Printf("Generated Order ID: %d\n", orderID) // You can optionally add a human-readable prefix when displaying/logging fmt.Printf(" Readable Order ID: ORD-%d\n", orderID) // Parse it back (for demonstration) ts, biz, wkr, seq, genTime := ParseID(orderID) fmt.Printf(" Parsed: Timestamp=%d, Business=%d, Worker=%d, Sequence=%d, GenTime=%s\n", ts, biz, wkr, seq, genTime.Format(time.RFC3339Nano)) paymentID, err := paymentGenerator.NextID() if err != nil { fmt.Println("Error generating payment ID:", err) continue } fmt.Printf("Generated Payment ID: %d\n", paymentID) fmt.Printf(" Readable Payment ID: PAY-%d\n", paymentID) tsP, bizP, wkrP, seqP, genTimeP := ParseID(paymentID) fmt.Printf(" Parsed: Timestamp=%d, Business=%d, Worker=%d, Sequence=%d, GenTime=%s\n\n", tsP, bizP, wkrP, seqP, genTimeP.Format(time.RFC3339Nano)) time.Sleep(5 * time.Millisecond) // Simulate time passing } // Example of how to get shard key (assuming 64 shards) orderIDForSharding, _ := orderGenerator.NextID() shardIndex := orderIDForSharding % 64 fmt.Printf("\nOrder ID %d would route to Shard Index %d\n", orderIDForSharding, shardIndex) }