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

简介: 本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。

前言

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

分布式ID的特点及其应用

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

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

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

它的优点有以下几个:

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

缺点也比较明显:

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

雪花算法的实现机理

一.雪花算法的组成

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

  • 固定值(符号位,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 get github.com/sony/sonyflake
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()
}

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

image.png

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

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

结语

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

相关文章
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
3天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
11 2
|
3天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
14 2
|
7天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
84 58
|
4天前
|
算法 关系型数据库 MySQL
分布式唯一ID生成:深入理解Snowflake算法在Go中的实现
在分布式系统中,确保每个节点生成的 ID 唯一且高效至关重要。Snowflake 算法由 Twitter 开发,通过 64 位 long 型数字生成全局唯一 ID,包括 1 位标识位、41 位时间戳、10 位机器 ID 和 12 位序列号。该算法具备全局唯一性、递增性、高可用性和高性能,适用于高并发场景,如电商促销时的大量订单生成。本文介绍了使用 Go 语言的 `bwmarrin/snowflake` 和 `sony/sonyflake` 库实现 Snowflake 算法的方法。
16 1
分布式唯一ID生成:深入理解Snowflake算法在Go中的实现
|
6天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
3天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
10 4
|
3天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
14 1
|
7天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
7天前
|
Go 数据处理 调度
探索Go语言的并发模型:Goroutines与Channels的协同工作
在现代编程语言中,Go语言以其独特的并发模型脱颖而出。本文将深入探讨Go语言中的Goroutines和Channels,这两种机制如何协同工作以实现高效的并发处理。我们将通过实际代码示例,展示如何在Go程序中创建和管理Goroutines,以及如何使用Channels进行Goroutines之间的通信。此外,本文还将讨论在使用这些并发工具时可能遇到的常见问题及其解决方案,旨在为Go语言开发者提供一个全面的并发编程指南。