├── README.md ├── go.mod ├── .gitignore ├── snowflake_test.go ├── LICENSE └── snowflake.go /README.md: -------------------------------------------------------------------------------- 1 | # snowflake 2 | Twitter 的 Snowflake 算法 的 Go 实现 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/polaris1119/snowflake 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /snowflake_test.go: -------------------------------------------------------------------------------- 1 | package snowflake_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/polaris1119/snowflake" 9 | ) 10 | 11 | func TestNextID(t *testing.T) { 12 | sf := snowflake.New() 13 | for i := 0; i < 100; i++ { 14 | go func(i int) { 15 | fmt.Println(i, sf.NextID(), sf.String()) 16 | }(i) 17 | } 18 | 19 | time.Sleep(2e9) 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 polarisxu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /snowflake.go: -------------------------------------------------------------------------------- 1 | // Twitter 的 Snowflake 算法 的实现 2 | /* 3 | SnowFlake 的结构如下(每部分用-分开): 4 | 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 5 | - 最高位是符号位,正数是 0,负数是 1, id 一般是正数,因此最高位固定是 0; 6 | - 41 位时间戳(毫秒级),注意,41 位时间戳不是存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳 - 开始时间戳),这样能存的时间更长,开始时间一般指定为项目启动时间,由程序指定。可以使用 69 年:`(1<<41)/(1000*60*60*24*365)`; 7 | - 10 位的机器相关位,可以部署在 1024 个节点,包括 5 位的 datacenterId(数据中心 ID) 和 5 位 workerId(工作机器 ID); 8 | - 12 位系列号,毫秒内的计数。12 位的计数顺序号支持每个节点每毫秒(同一机器,同一时间戳)产生 4096 个 ID 序号; 9 | */ 10 | package snowflake 11 | 12 | import ( 13 | "fmt" 14 | "net" 15 | "sync" 16 | "time" 17 | ) 18 | 19 | const ( 20 | sequenceMask = 1<<12 - 1 21 | workerMask = 1<<5 - 1 22 | dataCenterMask = 1<<5 - 1 23 | 24 | workerLeftShift = 12 25 | dataCenterLeftShift = 17 26 | timestampLeftShift = 22 27 | ) 28 | 29 | type SnowFlake struct { 30 | mutex sync.Mutex 31 | 32 | sequence int16 33 | dataCenterID uint8 34 | workerID uint8 35 | 36 | // 上次生成 ID 的时间戳(毫秒) 37 | lastTimestamp int64 38 | 39 | startTime time.Time 40 | } 41 | 42 | // NewWith 给定开始时间和可选的 dataCenterID 和 workerID(注意两者的顺序) 43 | // 如果 ids 没传,则使用 machineID 44 | func NewWith(startTime time.Time, ids ...uint8) *SnowFlake { 45 | var dataCenterID, workerID uint8 46 | 47 | idLen := len(ids) 48 | if idLen >= 2 { 49 | dataCenterID, workerID = ids[0], ids[1] 50 | } else if idLen == 1 { 51 | dataCenterID = ids[0] 52 | workerID = ids[0] 53 | } else { 54 | dataCenterID, workerID = machineID() 55 | } 56 | 57 | return &SnowFlake{ 58 | startTime: startTime.UTC(), 59 | dataCenterID: dataCenterID & dataCenterMask, 60 | workerID: workerID & workerMask, 61 | } 62 | } 63 | 64 | func New() *SnowFlake { 65 | now := time.Now() 66 | startTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) 67 | dataCenterID, workerID := machineID() 68 | return NewWith(startTime, dataCenterID, workerID) 69 | } 70 | 71 | // NextID 获取一个 ID 72 | func (s *SnowFlake) NextID() int64 { 73 | now := time.Now().UTC() 74 | millisecond := now.UnixNano() / 1e6 75 | if millisecond < s.lastTimestamp { 76 | panic("Clock moved backwards, Refusing to generate id") 77 | } 78 | 79 | s.mutex.Lock() 80 | 81 | // 同一毫秒,进行毫秒内序号递增 82 | if millisecond == s.lastTimestamp { 83 | s.sequence = (s.sequence + 1) & sequenceMask 84 | // 当前毫秒内序号用完,堵塞到下一毫秒 85 | if s.sequence == 0 { 86 | for millisecond <= s.lastTimestamp { 87 | millisecond = genMillisecond() 88 | } 89 | } 90 | } else { 91 | // 时间戳改变,毫秒内序号重置 92 | s.sequence = 0 93 | } 94 | s.lastTimestamp = millisecond 95 | sequence := s.sequence 96 | 97 | s.mutex.Unlock() 98 | 99 | elaspedMillisecond := millisecond - s.startTime.UnixNano()/1e6 100 | 101 | return elaspedMillisecond<