go语言后端开发学习(六) ——基于雪花算法生成用户ID

前言

在我们日常进行开发的时候,不可避免的会出现对用户表的操作,而为了保证每一个用户的唯一性,这就需要我们创建一个唯一性的id,但是现在有一个问题,如果我们仅仅像通过自增这样方式来创建唯一的id,这无疑是非常不合适的,他人可以通过自己新创建账号的id进而大致推算出当前网站大致的用户量,这样会对网站的安全造成极大的威胁,那我们可以如何去避免这种情况呢?这就是我们今天所要介绍的内容:分布式ID生成。

分布式ID的特点及其应用

分布式ID主要具有以下特点:

  • 全局唯一性:不能出现有重复的ID标识
  • 递增性:确保生成ID对于用户/业务是递增的
  • 高可用性:确保如何情况下生成的id都正常
  • 高性能性:在高并发的环境下依旧表现良好

而今天我们所要介绍的分布式ID生成方案是业内比较推荐的方法------ Snowflake(雪花算法)

它的优点有以下几个:

  • 生成时不依赖于数据库,完全在内存中生成(高性能高可用)
  • 每秒钟能生成数百万的自增 ID(高吞吐)
  • 存入数据库中,索引效率高

缺点也比较明显:

  • 依赖服务器时间,服务器时间回拨时可能会生成重复 id。

雪花算法的实现机理

一.雪花算法的组成

在雪花算法中会生成64bit的long型数值,它可以分为如下四个部分:

  • 固定值(符号位,0-正 1-负)
  • 时间戳:41bit,存储毫秒级时间戳(41 位的长度可以使用 69 年)
  • 标识位:12bit,用于表示在同一毫秒内生成的多个ID的序号。如果在同一毫秒内生成的ID超过了4096个(2的12次方),则需要等到下一毫秒再生成ID。

拓展: 虽然默认生成的位数是64位,但是这个我们可以手动调节

二.对于雪花算法部分问题的分析

1.生成ID重复问题

场景:一个订单微服务,通过雪花算法生成 ID,共部署三个节点,标识位一致。此时有 200 并发,均匀散布三个节点,三个节点同一毫秒同一序列号下生成 ID,那么就会产生重复 ID

由此我们可以知道该问题出现的前置条件如下:

  • 服务通过集群的方式部署,其中部分机器标识位一致;
  • 业务存在一定的并发量,没有并发量无法触发重复问题;
  • 生成 ID 的时机:同一毫秒下的序列号一致。

解决方案:

  • 预分配:应用上线前,统计当前服务的节点数,人工去申请标识位.(适用于服务节点固定或者项目较少)
  • 动态分配:将标识位存放在 Redis、Zookeeper、MySQL 等中间件,在服务启动的时候去请求标识位,请求后标识位更新为下一个可用的
  • 统一分配:将标识位存放在 Redis、Zookeeper、MySQL 等中间件,在服务启动的时候去请求标识位,请求后标识位更新为下一个可用的

2.标识位的使用方式

标识位一共10 bit,如果全部表示机器,那么可以表示1024台机器,如果拆分,5 bit 表示机房,5bit表示机房里面的机器,那么可以有32个机房,每个机房可以用32台机器。

雪花算法的实现

这里我们选择使用第三方包sonyflake

go 复制代码
 go get github.com/sony/sonyflake
go 复制代码
package snoyflake

import (
	"fmt"
	"github.com/sony/sonyflake"
	"time"
)

var (
	sonyFlake     *sonyflake.Sonyflake
	sonyMachineId uint16
)

func getMachineId() (id uint16, err error) {
	return sonyMachineId, nil
}
func Init(starttime string, machineId uint16) (err error) {
	sonyMachineId = machineId
	t, _ := time.Parse("2006-01-02", starttime) // 设置开始时间
	setting := sonyflake.Settings{
		StartTime: t,
		MachineID: getMachineId,
	}
	sonyFlake = sonyflake.NewSonyflake(setting) //用配置生成sonyflake节点
	return
}

// GetID 返回生成的id
func GetID() (id uint64, err error) {
	if sonyFlake == nil {
		err = fmt.Errorf("sonyflake not init")
		return
	}
	return sonyFlake.NextID()
}

最后我们尝试来生成一下:

也是看到成功的生成了id,再运行一次:

可以看到生成的id并不相同,说明我们的雪花算法已经成功实现了。

结语

在原理方面参考了下面的文章,在此鸣谢大佬的分享:
一文读懂"Snowflake(雪花)"算法

相关推荐
wainyz7 分钟前
Java NIO操作
java·开发语言·nio
喵叔哟16 分钟前
重构代码之用委托替代继承
开发语言·重构
lzb_kkk22 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
SEEONTIME22 分钟前
python-24-一篇文章彻底掌握Python HTTP库Requests
开发语言·python·http·http库requests
起名字真南40 分钟前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
tyler_download1 小时前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
小小小~1 小时前
qt5将程序打包并使用
开发语言·qt
hlsd#1 小时前
go mod 依赖管理
开发语言·后端·golang
小春学渗透1 小时前
Day107:代码审计-PHP模型开发篇&MVC层&RCE执行&文件对比法&1day分析&0day验证
开发语言·安全·web安全·php·mvc
杜杜的man1 小时前
【go从零单排】迭代器(Iterators)
开发语言·算法·golang