应用实践|基于Python手把手教你实现雪花算法

在这里插入图片描述

📫 作者简介:「六月暴雪飞梨花」,专注于研究Java,就职于科技型公司后端工程师
🏆 近期荣誉:华为云云享专家、阿里云专家博主、
🔥 三连支持:欢迎 ❤️关注、👍点赞、👉收藏三连,支持一下博主~

文章目录

  • 概述
  • 什么是雪花ID
  • 代码演示步骤
    • 1 引入依赖库
    • 2 初始化参数
    • 3 定义并实现
    • 4 测试代码
    • 5 异常处理
  • 完整代码示例
  • 运行结果演示
  • 问题分析
    • (1)第一位为什么不使用
    • (2)机器位怎么用
    • (3)时间戳比较
  • 结束语

概述

分布式策略ID的主要应用在互联网网站、搜索引擎、社交媒体、在线购物、金融、大数据处理、日志场景中,这些应用需要支持大量的并发请求和用户访问,分布式ID策略可以通过请求分发到不同的服务器节点来做计算,以提高服务的响应速度和可用性。
常见的分布式ID生成策略:
● UUID(Universally Unique Identifier)
● 雪花算法(Snowflake)
● Redis原子自增
● 基于数据库的自增主键(有些数据库不支持自增主键)
● 取当前毫秒数
本文主要简单介绍下雪花ID算法(Snowflake)的Python语言的计算方法。
在这里插入图片描述

雪花算法(Snowflake)是 Twitter 开源的分布式ID生成算法。雪花ID,或称雪花,是分布式计算中使用的唯一标识符的一种形式。该格式由Twitter创建,用于推文的ID。人们普遍认为,每片雪花都有唯一的结构,因此他们取了“雪花ID”这个名字。在当时Twitter的团队从MySQL转向Cassandra时,需要一种新的方法来生成ID号,而Cassandra中没有顺序ID生成工具,所以,应运而生雪花ID出现了。

雪花算法的相关知识可以参考Github:https://github.com/twitter-archive/snowflake/tree/b3f6a3c6ca8e1b6847baa6ff42bf72201e2c2231

什么是雪花ID

根据官方的介绍,雪花ID是由Twitter团队开发的一种分布式ID生成算法,它的设计目标是在分布式系统中生成唯一ID,具备趋势递增、高性能、可扩展等特点。其实雪花ID生成的唯一ID是由64位二进制数组成,结果是一个long型的ID。可以分解为四个部分:
1 符号位:符号位,也就是最高位,始终是0,没有任何意义,因为要是唯一计算机二进制补码中就是负数,0才是正数。
2 时间戳:占用41位,记录生成ID的时间戳,精确到毫秒级。
3 机器标识:占用10位,用于标识不同的机器。
4 计数序列号:占用12位,用于解决同一毫秒内生成多个ID的冲突。
Snowflake ID的结构可以用二进制格式表示如下:

0 1                                       41     51         64
+-----------------------------------------+------+-----------+
|0|timestamp (milliseconds since epoch)   |worker| sequence  |
+-----------------------------------------+------+-----------+

Snowflake ID的结构可以用图表示如下:
在这里插入图片描述

代码演示步骤

1 引入依赖库

使用Python标准库中的time模块来获取当前时间戳,并使用random模块来生成随机worker_id和data_center_id。

import time  
import random  

2 初始化参数

此处我们定义一个类Snowflake类,提前初始化机器标识ID、数据中心ID、计数序列号、时间戳。

    def __init__(self, worker_id, data_center_id):  ### 机器标识IDself.worker_id = worker_id  ### 数据中心IDself.data_center_id = data_center_id  ### 计数序列号self.sequence = 0  ### 时间戳self.last_timestamp = -1  

3 定义并实现

这是最重要的一个步骤,我们来实现一个生成ID的方法,这个方法根据雪花算法的规则生成唯一ID,具体的实现过程包括获取当前时间戳、判断是否为同一毫秒、更新序列号等。在next_id()方法中,我们首先获取当前时间戳,并检查是否比上一次生成ID的时间戳小。
(1)如果是,则抛出异常,因为这表示时钟回退。
(2)如果时间戳相同,则递增序列号,如果序列号达到最大值4095,则等待下一毫秒。如果时间戳不同,则重置序列号为0。
(3)最后,我们将生成的ID返回。
具体代码如下所示:

    def next_id(self):  timestamp = int(time.time() * 1000)  if timestamp < self.last_timestamp:  raise Exception("Clock moved backwards. Refusing to generate id for %d milliseconds" % abs(timestamp - self.last_timestamp))  if timestamp == self.last_timestamp:  self.sequence = (self.sequence + 1) & 4095  if self.sequence == 0:  timestamp = self.wait_for_next_millis(self.last_timestamp)  else:  self.sequence = 0  self.last_timestamp = timestamp  return ((timestamp - 1288834974657) << 22) | (self.data_center_id << 17) | (self.worker_id << 12) | self.sequence  

注意⚠️:由于时间戳是以毫秒为单位的,所以每毫秒最多可以生成4096个ID。如果ID生成器的负载较高,可能会在同一毫秒内多次调用next_id()方法,导致序列号耗尽。为了避免这种情况,我们在等待下一毫秒时检查时间戳是否小于上一次生成ID的时间戳。如果是,则抛出异常,因为这表示时钟回退。

4 测试代码

在测试代码中,我们使用一个循环来生成10个唯一的ID,并打印出来。如果时钟回退,则会抛出一个异常并打印错误信息。

if __name__ == '__main__':  worker_id = 1  data_center_id = 1  snowflake = Snowflake(worker_id, data_center_id)  for i in range(10):  try:  print(snowflake.next_id())  except Exception as e:  print("Clock moved backwards:", e)

5 异常处理

通过上面几步我们已经实现了雪花ID的核心代码工作,但是为了确保算法的正确性和程序的严谨性,我们需要处理错误和边界情况,比如当同一毫秒内生成的ID超过序列号的最大值时,需要等待下一毫秒再生成。具体代码如下所示:

    def wait_for_next_millis(self, last_timestamp):  timestamp = int(time.time() * 1000)  while timestamp <= last_timestamp:  timestamp = int(time.time() * 1000)  return timestamp  

完整代码示例

接下来就来整合一下上面的分解步骤,这里将展示一个完整的Python语言代码示例,后面会展示运行的最终结果。示例代码将按照上面的步骤来实现雪花算法,并输出生成的唯一ID,下面就是完整的示例代码:

import time  
import random  class Snowflake:  def __init__(self, worker_id, data_center_id):  ### 机器标识IDself.worker_id = worker_id  ### 数据中心IDself.data_center_id = data_center_id  ### 计数序列号self.sequence = 0  ### 时间戳self.last_timestamp = -1  def next_id(self):  timestamp = int(time.time() * 1000)  if timestamp < self.last_timestamp:  raise Exception("Clock moved backwards. Refusing to generate id for %d milliseconds" % abs(timestamp - self.last_timestamp))  if timestamp == self.last_timestamp:  self.sequence = (self.sequence + 1) & 4095  if self.sequence == 0:  timestamp = self.wait_for_next_millis(self.last_timestamp)  else:  self.sequence = 0  self.last_timestamp = timestamp  return ((timestamp - 1288834974657) << 22) | (self.data_center_id << 17) | (self.worker_id << 12) | self.sequence  def wait_for_next_millis(self, last_timestamp):  timestamp = int(time.time() * 1000)  while timestamp <= last_timestamp:  timestamp = int(time.time() * 1000)  return timestamp  ### test
if __name__ == '__main__':  worker_id = 1  data_center_id = 1  snowflake = Snowflake(worker_id, data_center_id)  for i in range(10):  try:  print(snowflake.next_id())  except Exception as e:  print("Clock moved backwards:", e)

运行结果演示

通过上面完整示例代码运行之后,可以得到下面的运行结果,即输出生成的唯一ID。具体的运行结果如下所示:

[Running] python -u "/Users/Aion/WorkSpace/PythonSpace/Snowflow/Snowflow.py"
1742096523036069888
1742096523036069889
1742096523036069890
1742096523036069891
1742096523036069892
1742096523036069893
1742096523036069894
1742096523036069895
1742096523036069896
1742096523036069897[Done] exited with code=0 in 0.057 seconds

问题分析

(1)第一位为什么不使用

在计算机的表示中,第一位是符号位,0表示整数,第一位如果是1则表示负数,我们用的ID默认就是正数,所以默认就是0,那么这一位默认就没有意义。

(2)机器位怎么用

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

(3)时间戳比较

在获取时间戳小于上一次获取的时间戳的时候,不能生成ID,而是继续循环,直到生成可用的ID,这里没有使用拓展位防止时钟回拨。

结束语

其实对于分布式ID的生成策略。无论是我们上述提到的哪一种。无非需要具有以下两种特点:分布式、唯一。通过本文,可以快速了解雪花ID(雪花算法,SnowFlake),SnowFlake的优点是:
(1)单机上整体自增,集群上整体自增,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞;
(2)效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
(3)强依赖性,依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复。
希望本文能帮助您理解雪花算法的实现过程,也希望能够为您在分布式系统开发中提供一些使用帮助。


欢迎关注博主 「六月暴雪飞梨花」 或加入【六月暴雪飞梨花社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/644826.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习300问】17、什么是欠拟合和过拟合?怎么解决欠拟合与过拟合?

一个问题出现了&#xff0c;我们首先要描述这个问题&#xff0c;然后分析问题出现的原因&#xff0c;找到原因后提出解决方案。废话不多说&#xff0c;直接上定义&#xff0c;然后通过回归和分类任务的例子来做解释。 一、什么是欠拟合和过拟合&#xff1f; &#xff08;1&am…

文件上传技术总结

语言可解析的后缀 &#xff08;前提&#xff1a;在Apache httpd.conf 配置文件中有特殊语言的配置 AddHandler application/x-httpd-php .php 搭配大小写、双重、空格来进行 其中&#xff1a; phtml、pht、php3、php4和php5都是Apache和php认可的php程序的文件后缀 常见的…

解决vld内存泄露检测工具只支持到vs2015的问题,visual studio2015以上版本安装vld内存泄漏检测工具[实测vs2022生效]

目录 一.vld工具下载二.vld应用安装三.visual studio2022环境配置四.visual studio2022 vld内存检测测试 一.vld工具下载 Visual Leak Detector github链接: https://kinddragon.github.io/vld/ 下载直达链接: https://github.com/KindDragon/vld/releases/tag/v2.5.1 下拉至…

Spring Boot 学习之——@SpringBootApplication注解(自动注解原理)

SpringBootApplication注解 springboot是基于spring的新型的轻量级框架&#xff0c;最厉害的地方当属**自动配置。**那我们就可以根据启动流程和相关原理来看看&#xff0c;如何实现传奇的自动配置 SpringBootApplication//标注在某个类上&#xff0c;表示这个类是SpringBoot…

初识汇编指令

1. ARM汇编指令 目的 认识汇编, 从而更好的进行C语言编程 RAM指令格式: 了解 4字节宽度 地址4字节对齐 方便寻址 1.1 指令码组成部分 : condition: 高4bit[31:28] 条件码 0-15 &#xff08;16个值 &#xff09; 条件码: 用于指令的 条件执行 , ARM指定绝大部分 都可…

MySql索引事务讲解和(经典面试题)

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;MySql&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 索引 概念 索引的相关操作 索引内部数据结构 事务 为…

Linux启动级别和密码问题文件

1、linux启动级别 如果安装的linux默认带的图形化界面&#xff0c;默认的运行级别为5 graphical.target 因为图形化太耗费资源了&#xff0c;想每次启动的时候&#xff0c;更改它的默认允许级别为命令行&#xff08;文本&#xff09; cat /etc/inittab 修改为命令行 多用户…

洛谷刷题-【入门2】分支结构

目录 1.苹果和虫子 题目描述 输入格式 输出格式 输入输出样例 2.数的性质 题目描述 输入格式 输出格式 输入输出样例 3.闰年判断 题目描述 输入格式 输出格式 输入输出样例 4.apples 题目描述 输入格式 输出格式 输入输出样例 5.洛谷团队系统 题目描述 …

大数据开发之SparkSQL

第 1 章&#xff1a;spark sql概述 1.1 什么是spark sql 1、spark sql是spark用于结构化数据处理的spark模块 1&#xff09;半结构化数据&#xff08;日志数据&#xff09; 2&#xff09;结构化数据&#xff08;数据库数据&#xff09; 1.2 为什么要有sparksql hive on s…

【教学类-综合练习-08】20240105 大3班 综合材料(美术类:骰子、面具、AB手环)

背景需求 年终了&#xff0c;清理库存&#xff0c;各种打印的题型纸都拿出来&#xff0c;当个别化学习材料 教学过程&#xff1a; 时间&#xff1a;2024年1月2日上午 班级&#xff1a;大3班&#xff08;2周才去一次&#xff09; 人数&#xff1a;17人

后端开发_单元测试

后端开发_单元测试 1. 简介2. JUnit 4使用方法2.1 jar包引入2.2 测试用例1. 简介 2. JUnit 4使用方法 2.1 jar包引入 1. 本地依赖引入方式 Junit4.jar包 2. maven方式引入jar <dep

SpringSecurity认证登录成功后获取角色菜单

目录 前言 一、RBAC模型 二、实战应用 1. 建立用户、角色、资源实体类 2. 数据层查询角色资源 3. 业务层实现&#xff0c;调用数据层查询接口 4. SystemController控制器菜单获取方法 5. menu.jsp菜单页面实现 前言 本篇文章接SSM项目集成Spring Security 4.X版本&…

【Java程序员面试专栏 专业技能篇】计算机网络核心面试指引

关于计算机网络部分的核心知识进行一网打尽,包括计算机的网络模型,各个层的一些重点概念,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 分层基本概念 计算机网络模型的分层及具体作用 计算机网络有哪些分层模型 可以按照应用层到物…

2023春秋杯冬季赛 --- Crypto wp

文章目录 前言Cryptonot_wiener 前言 比赛没打&#xff0c;赛后随便做一下题目 Crypto not_wiener task.py: from Crypto.Util.number import * from gmpy2 import * import random, os from hashlib import sha1 from random import randrange flagb x bytes_to_long(f…

量化交易学习1

一、股票数据基本分类 可分为&#xff08;1&#xff09;技术面数据和&#xff08;2&#xff09;基本面数据 &#xff08;1&#xff09;技术面数据 技术面数据是通过股票的历史价格和交易量等市场数据进行计算和分析得出的指标。常用的技术指标包括移动平均线、相对强弱指标、…

在上海做程序员这么多年,退休后我的工资是多少?

大家好&#xff0c;我是拭心。 最近看到一个很可惜的事&#xff1a;有个阿姨在深圳缴纳了 12 年社保&#xff0c;第 13 年家里突然有事不得不回老家&#xff0c;回去后没再缴纳社保&#xff0c;结果退休后无法领退休工资&#xff0c;还得出来打工赚钱。 之所以这样&#xff0…

重温经典struts1之自定义Filter(拦截器)解决中文乱码问题

重温经典struts1之自定义Filter解决中文乱码问题 前言Filter&#xff08;拦截器&#xff09;需求具体实现步骤1. 自定义CharsetEncodingFilter拦截类2 将自定义CharsetEncodingFilter注册到web.xml3 编写测试用的login页面和处理逻辑 总结 前言 页面输入的中文&#xff0c;Act…

UF_UI_select_with_single_dialog()通过单选对话框选择单个对象。对象可以通过光标或输入名称进行选择。对象被突显出来。

int response0;//返回用户操作类型&#xff0c;点了哪一种返回取消或者确定tag_t objtagNULL_TAG;//输出选择对象tag;double cursor[ 3 ];//输出光标位置tag_t view_tagNULL_TAG;//输出视图tag;UF_UI_select_with_single_dialog("请选择一个对象","获取对象类型…

无人机航迹规划(五):七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划(提供MATLAB代码)

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁…

全球先端实验室护肤品牌德妃DERMAFIRM连续6年荣获“韩国第一品牌大奖”

全球先端实验室护肤品牌德妃(DERMAFIRM)在韩国消费者论坛主办的“2024年韩国第一品牌大奖(KOREA FIRST BRAND AWARDS 2024)”中&#xff0c;连续6年获得了由中国消费者评选的实验室护肤品牌部门大奖。 今年迎来第22届的“韩国第一品牌大奖(KOREA FIRST BRAND AWARDS)”是韩国规…