# golang基于雪花算法生成分布式ID {#golang基于雪花算法生成分布式id}
本文总结golang中如何使用雪花算法(snowflake)生成分布式ID。建议使用分布式ID作为数据库表的主键类型,使用分布式ID作为主键类型拥有很多好处,如方便日后的分库分表等扩展操作。本文列举了可以选用的2个库,性能相当,随便选一个即可。
# 1. bwmarrin/snowflake {#_1-bwmarrin-snowflake}
使用方法和性能测试代码如下:
package test
import ( "fmt" "sync" "testing" "time"
"github.com/bwmarrin/snowflake" "github.com/golang/glog"
)
/** 测试snowflake雪花算法 */ func TestSnowflake(t *testing.T) { // Create a new Node with a Node number of 1 node, err := snowflake.NewNode(1) if err != nil { fmt.Println(err) return }
// Generate a snowflake ID. id := node.Generate() // Print out the ID in a few different ways. fmt.Printf("Int64 ID: %d\n", id) fmt.Printf("String ID: %s\n", id) fmt.Printf("Base2 ID: %s\n", id.Base2()) fmt.Printf("Base64 ID: %s\n", id.Base64()) // Print out the ID's timestamp fmt.Printf("ID Time : %d\n", id.Time()) // Print out the ID's node number fmt.Printf("ID Node : %d\n", id.Node()) // Print out the ID's sequence number fmt.Printf("ID Step : %d\n", id.Step()) // Generate and print, all in one. fmt.Printf("ID : %d\n", node.Generate().Int64())
}
/** 测试雪花ID生成库github.com/bwmarrin/snowflake的并发能力 */ func TestBwmarrinSnowflakeLoad(t *testing.T) { var wg sync.WaitGroup
// Create a new Node with a Node number of 1 node, err := snowflake.NewNode(1) if err != nil { fmt.Println(err) return } var check sync.Map t1 := time.Now() for i := 0; i < 200000; i++ { wg.Add(1) go func() { defer wg.Done() node, err = snowflake.NewNode(1) if err != nil { fmt.Println(err) } val := node.Generate() if _, ok := check.Load(val); ok { // id冲突检查 glog.Error(fmt.Errorf("error#unique: val:%v", val)) return } check.Store(val, 0) if val == 0 { glog.Error(fmt.Errorf("error")) return } }() } wg.Wait() elapsed := time.Since(t1) println(int64(elapsed))
}
生成的ID长这样: 1370264507334529131,长度为19位的整数。
如上的并发测试函数执行了3次,耗时分别为1614086843、1289643775、1277146002,单位是纳秒。
# 2. GUAIK-ORG/go-snowflake {#_2-guaik-org-go-snowflake}
使用方法和性能测试代码如下:
package test
import ( "fmt" "sync" "testing" "time"
"github.com/GUAIK-ORG/go-snowflake/snowflake" "github.com/golang/glog"
)
/** 测试雪花ID生成库github.com/GUAIK-ORG/go-snowflake/snowflake的并发能力 */ func TestSnowflakeLoad(t *testing.T) { var wg sync.WaitGroup s, err := snowflake.NewSnowflake(int64(0), int64(0)) if err != nil { glog.Error(err) return } var check sync.Map t1 := time.Now() for i := 0; i < 200000; i++ { wg.Add(1) go func() { defer wg.Done() val := s.NextVal() if _, ok := check.Load(val); ok { // id冲突检查 glog.Error(fmt.Errorf("error#unique: val:%v", val)) return } check.Store(val, 0) if val == 0 { glog.Error(fmt.Errorf("error")) return } }() } wg.Wait() elapsed := time.Since(t1) println(int64(elapsed)) }
生成的ID长这样: 158224334375092829,长度为18位的整数。
如上的并发测试函数执行了3次,耗时分别为1124502969、1250495184、1517730019,单位是纳秒。
# 3. 总结 {#_3-总结}
可以看出,如上的2个库的性能接近,1秒多可以生成20万的不重复的整数。
2个库的区别:
- 生成的整数的位数不同
bwmarrin/snowflake
是19位, 而GUAIK-ORG/go-snowflake
是18位。 - github的star数不同
bwmarrin/snowflake
在github的star数上千,GUAIK-ORG/go-snowflake
是国产。
注意:
- 在多实例(多个snowflake对象)的并发环境下,请确保每个实例的唯一性,否则生成的ID可能冲突。
对于bwmarrin/snowflake
,需要确保每个snowflake对象的Node number
的唯一性。
对于GUAIK-ORG/go-snowflake
,需要确保每个snowflake对象的datacenterid,workerid
的唯一性。- 有很多同学反应,生成的snowflake ID有重复的现象,那么需要从如下几点来分析:
- 多个服务节点中创建的snowflake对象使用的初始化参数是否是相同的?
- 进程内部创建的snowflake对象是不是全局的单例对象?