全局唯一id
在单体服务中,我们可以通过数据库的自增id去实现唯一id命名,
但是在分布式服务中,分库分表之后,就无法直接使用自增id了,数据库只能保证当前表的id唯一,
这个时候我们就需要通过其他方式去生成全局唯一id,例如UUID
UUID
uuid通俗来讲,就是针对于 每个机器/机器的线程/当前的时间戳/伪随机数 作为生成凭据,能保证每个机器下的每个线程都会生成不同的id
生成的数据大概为一串无规律的数字/一串无规律的字符串 "2145562313212","e70f1357-f260-46ff-a32d-53a086c57ade"
生成的结果由不同的算法而发生改变,但是算法的原理依据都是如上
uuid的好处在于生成速度快,重复概率低(有可能重复,但是这辈子重复的概率接近0,可以不考虑)
缺点是生成的字符串没有明确的意义,甚至没有排序,另外就是数据过长,无法直接使用
zookeeper实现全局唯一id
通过zookeeper的有序节点类型,可以很方便的实现全局唯一id
package main import ( "errors" "fmt" "github.com/go-zookeeper/zk" "strings" "time" ) func main() { conn, _, err := zk.Connect(\[\]string{"127.0.0.1:10001"}, time.Second) if err != nil { panic(err) } idPath := "/app1/tableName1/id/" createPath(conn, idPath) id,err := getGlobalID(conn, idPath) if err!=nil { fmt.Printf("%v \\n",err) return } fmt.Printf("Global ID: %s",id) } func getGlobalID(conn *zk.Conn, idPath string) (id string,err error) { id, err = conn.Create(idPath+"a_", \[\]byte{}, zk.FlagSequence, zk.WorldACL(zk.PermAll)) if err != nil { return id,errors.New(err.Error()) } fmt.Printf("Global ID:%s\\n", id) return id,nil } func createPath(conn *zk.Conn, path string) (err error) { strArr := strings.Split(path, "/") var node string for _, str := range strArr { if str == "" { continue } node = node + "/" + str exists, _, err := conn.Exists(node) if err != nil { return errors.New(err.Error()) } if exists { continue } else { _, err = conn.Create(node, \[\]byte{}, 0, zk.WorldACL(zk.PermAll)) if err != nil { return errors.New(err.Error()) } } } return err }
将输出:
通过zookeeper生成的id优点是 有序的,可以很清楚的了解到具体的数据值(你还可以把a_改为日期),这样还可以清楚这个id的创建时间等
缺点是zookeeper生成节点很慢(需要同步到其他集群节点),另外如果超过int最大值,则需要重新使用个新的父节点生成