雪花算法生成id,返回给前端不一致 | 青训营笔记

492 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第14天。

前言

今天发现后端使用雪花算法生成的 PostId 传给前端之后,居然不一致!

导致文章详情页带参数查询为空!

为啥会这样呢?

什么是雪花算法?

雪花算法,Twitter的分布式自增ID算法

snowflake优缺点:

优点:

  • (1)经测试snowflake每秒能生成26万个自增可排序的ID。
  • (2)snowflake生成的ID结果是一个64bit大小的整数,转换成字符串后长度最多19。[注意这里的 19 位数,后面会用]
  • (3)分布式系统内不会产生ID碰撞(datacenter和workerId作区分)并且效率高。

缺点:依赖机器时钟,如果机器时钟回拨,会导致id重复。由于是部署到分布式环境,每台机器上的时钟不可能完全同步,有时候出现不是全局递增的情况。

Golang 如何实现?

这里使用的是 sonyflake 库,基本实现和 snowflake 差不多,不过是配位不太一样,影响不大。

地址在这里:github.com/sony/sonyfl…

具体使用代码:

package snowflake

import (
	"fmt"
	"time"

	"github.com/sony/sonyflake"
)

var (
	sonyFlake     *sonyflake.Sonyflake	// 实例
	sonyMachineID uint16				// 机器ID
)

func getMachineID() (uint16, error) {  // 返回全局定义的机器ID
	return sonyMachineID, nil
}

// 需传入当前的机器ID
func Init(machineId uint16) (err error) {
	sonyMachineID = machineId
	t, _ := time.Parse("2006-01-02", "2020-01-01")	// 初始化一个开始的时间
	settings := sonyflake.Settings{		// 生成全局配置
		StartTime: t,
		MachineID: getMachineID,								// 指定机器ID
	}
	sonyFlake = sonyflake.NewSonyflake(settings)	// 用配置生成sonyflake节点
	return
}

// GetID 返回生成的id值
func GetID() (id uint64, err error) {		// 拿到sonyflake节点生成id值
	if sonyFlake == nil {
		err = fmt.Errorf("snoy flake not inited")
		return
	}

	id, err = sonyFlake.NextID()
	return
}

func main()  { 
	if err := Init(1);err!=nil{
		fmt.Printf("Init failed,err:%v\n",err)
		return
	}
	id,_ := GetID()
	fmt.Println(id)
}


如果将其封装在 snowflake 包里,就这样直接调用即可:

image.png

如何解决返回前端的id不一致?

看一下哪里不一样:

139677942874636289 ==> 139677942874636290

好家伙怎么就差一位数!我当时以为是精度的问题,上网一查发现Number精度是16位(雪花ID是19位的),所以是JS的Number数据类型导致的精度丢失。

解决方案:后端的ID(uint64) ==> uint64 转 String ==> 前端使用 String 类型的ID,这样精度就不会丢失了。那前端再把String类型的19位数字传回服务端的时候,再转 uint64 。

总结

提前发现 bug 也是一种好事,哈哈。

解决方案:前端用String类型的ID,后端及数据库继续使用 uint64 类型不影响数据库查询执行效率。