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(雪花)"算法

相关推荐
傻啦嘿哟10 分钟前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel
大数据编程之光14 分钟前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink
初九之潜龙勿用14 分钟前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
风尚云网31 分钟前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
Dola_Pan31 分钟前
C语言:数组转换指针的时机
c语言·开发语言·算法
ExiFengs31 分钟前
实际项目Java1.8流处理, Optional常见用法
java·开发语言·spring
paj12345678933 分钟前
JDK1.8新增特性
java·开发语言
IT古董40 分钟前
【人工智能】Python在机器学习与人工智能中的应用
开发语言·人工智能·python·机器学习
繁依Fanyi44 分钟前
简易安卓句分器实现
java·服务器·开发语言·算法·eclipse
湫ccc1 小时前
《Python基础》之pip换国内镜像源
开发语言·python·pip