这是我参与「第四届青训营 」笔记创作活动的第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 包里,就这样直接调用即可:
如何解决返回前端的id不一致?
看一下哪里不一样:
139677942874636289 ==> 139677942874636290
好家伙怎么就差一位数!我当时以为是精度的问题,上网一查发现Number精度是16位(雪花ID是19位的),所以是JS的Number数据类型导致的精度丢失。
解决方案:后端的ID(uint64) ==> uint64 转 String ==> 前端使用 String 类型的ID,这样精度就不会丢失了。那前端再把String类型的19位数字传回服务端的时候,再转 uint64 。
总结
提前发现 bug 也是一种好事,哈哈。
解决方案:前端用String类型的ID,后端及数据库继续使用 uint64 类型不影响数据库查询执行效率。