golang基于雪花算法生成分布式ID

本文总结golang中如何使用雪花算法(snowflake)生成分布式ID。建议使用分布式ID作为数据库表的主键类型,使用分布式ID作为主键类型拥有很多好处,如方便日后的分库分表等扩展操作。本文列举了可以选用的2个库,性能相当,随便选一个即可。

使用方法和性能测试代码如下:

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,单位是纳秒。

使用方法和性能测试代码如下:

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. 总结

可以看出,如上的2个库的性能接近,1秒多可以生成20万的不重复的整数。
2个库的区别:

bwmarrin/snowflakeGUAIK-ORG/go-snowflakebwmarrin/snowflakeGUAIK-ORG/go-snowflake
bwmarrin/snowflakeNode numberGUAIK-ORG/go-snowflakedatacenterid,workerid