原文转载自「刘悦的技术博客」https://v3u.cn/a_id_155

但凡说起分布式系统,我们肯定会对一些海量级的业务进行分拆,比如:用户表,订单表。因为数据量巨大一张表完全无法支撑,就会对其进行分库分表。但是一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题,当我们使用mysql的自增长主键(auto_increment)时,充分感受到了它的好处:整个系统ID唯一,ID是数字类型,而且是趋势递增的,ID简短,查询效率快,在分布式系统中显然由于单点问题无法使用mysql自增长了,此时需要别的解决方案来支撑分布式业务。

首先映入脑海的一定是uuid

>>> import uuid
>>> print(uuid.uuid1())
d13a0096-abca-11ea-8997-acbc32785ec1

客观地说,如果一定要用uuid生成订单号这类东西也能凑合用,但是它有着罄竹难书的“罪行”:肉眼可见,它是无序的;长度是64位数字字母随机组合的字符串,占用空间巨大;完全不具备业务属性,也就是说使用uuid你完全无法推算出它到底是干嘛的;因为无序,所以趋势递增就更不用指望了;所以用uuid生成订单号就是自杀行为,适合它的是类似生成token令牌的场景。

那么我们就要说起业界鼎鼎有名的SnowFlake(雪花算法)发号器了。 Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序,让twitter可以通过一定的索引来进行检索,而在Twitter庞大的分布式系统中不同机器产生的id必须又必须不同。

它的好处显而易见,不仅全局唯一,并且有序按时间递增,同时占用空间少,生成的id仅仅是19位的整形数字,正好契合mysql的bigint数据类型,简直完美。

为啥它叫做Snowflake(雪花)算法?因为每个人都知道没有两片一样的雪花,这一事实源于晶体在天空中形成的方式。雪是一团冰晶,在大气中形成,并在它们下落时保持其形状。雪花形成于大气冷到能阻止它们融化变成雨或雨夹雪的时候。尽管云中的温度和湿度是不均匀的,但是在雪花大小的范围内,这些变量大约都是常数,这就是雪花的生长通常是对称的原因。另一方面,塔夫茨大学(Tufts University)化学家玛丽·简·舒尔茨(Mary Jane Shultz)指出:每片雪花都受到风,日光和其他变量变化的影响。她解释说,由于每个雪晶都到云层紊乱的影响,它们的形式都略有不同。

而Snowflake的逻辑也非常简单,雪花算法生成64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:

1位标识符:始终是0

41位时间戳:41位时间戳不是存储当前时间的时间戳,而是存储时间截的差值(当前时间截 - 开始时间截 )得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的
10位机器标识码:可以部署在1024个节点,如果机器分机房(IDC)部署,这10位可以由 5位机房ID + 5位机器ID 组成
12位序列:毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

看到时间戳,就可以联想到它的缺陷,也就是它依赖机器的时钟,如果服务器时钟回拨,可能会导致重复ID生成。

这里我们用Python3.0来生成SnowFlake生成的唯一id

首先安装库

pip3 install pysnowflake

安装完成后,就可以在本地命令行启动snowflake服务

snowflake_start_server --worker=1

这里的worker就是当前节点的标识,此时编写代码就可以打印出当前客户端使用的snowflake的服务信息

import snowflake.client
print(snowflake.client.get_stats()) {'dc': 0, 'worker': 1, 'timestamp': 1591871273195, 'last_timestamp': 550281600000, 'sequence': 0, 'sequence_overload': 0, 'errors': 0}

当然了,如果一台服务器上起了很多节点服务,也可以指定相关的节点进行装载

host = '127.0.0.1'
port = 30001
snowflake.client.setup(host, port)

随后我们究竟可以根据该服务生成唯一id了

import snowflake.client  

print(snowflake.client.get_guid())

4368750411956359169

可以看到这些id很明显带有递增的连续性,有的人会问了,假设我搭建了上千个节点的分布式系统,此时接口接到参数id,我怎么判断该id的订单信息存储在那个节点中呢?

其实很容易就可以判断,从SnowFlake的算法结构入手,本身就是二进制转换十进制的整形,现在我们反着进行解析即可,这里以这个19位的id为例子:4368750411956359169

首先将其转换为二进制

print(bin(4368750411956359169))
0b11110010100000111010110101101001100001000000000001000000000001

可以看到上文所述的第一位是标识符,此后是41位的时间戳,紧接着10位的节点标识码,最后12位的递增序列,从后面数12位是:000000000001,再数5位是:00001 这5位就是某个节点的存储标识,但是它目前是二进制,我们再将它转换为十进制

print(int('00001',2))
1

可以看到,转换结果显示该id存储在节点1的数据库中,如此就具备了相当强的业务属性,通过反推逻辑我们可以快速准确的定位到数据的具体存储位置从而进行查询。

结语:

其实关于分布式唯一id的解决方案,也不仅仅只有uuid或者snowflake,像redis的incr原子性操作自增,亦或者Mongodb极具特色的_objectid的生成方式,专为分布式而设计的ID生成方案。都是可以参考的解决方案,但是方案总归是方案,总有其自身的特点和缺陷,这就需要根据实际应用场景而具体问题进行具体分析了。

原文转载自「刘悦的技术博客」 https://v3u.cn/a_id_155

说起分布式自增ID只知道UUID?SnowFlake(雪花)算法了解一下(Python3.0实现)的更多相关文章

  1. Twitter分布式自增ID算法snowflake原理解析(Long类型)

    Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个Long类型的6 ...

  2. 详解Twitter开源分布式自增ID算法snowflake(附演算验证过程)

    详解Twitter开源分布式自增ID算法snowflake,附演算验证过程 2017年01月22日 14:44:40 url: http://blog.csdn.net/li396864285/art ...

  3. Twitter分布式自增ID算法snowflake原理解析

    以JAVA为例 Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个 ...

  4. 【Java】分布式自增ID算法---雪花算法 (snowflake,Java版)

    一般情况,实现全局唯一ID,有三种方案,分别是通过中间件方式.UUID.雪花算法. 方案一,通过中间件方式,可以是把数据库或者redis缓存作为媒介,从中间件获取ID.这种呢,优点是可以体现全局的递增 ...

  5. 分布式自增ID算法-Snowflake详解

    1.Snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并 ...

  6. 分布式ID生成系统 UUID与雪花(snowflake)算法

    Leaf——美团点评分布式ID生成系统 -https://tech.meituan.com/MT_Leaf.html 网游服务器中的GUID(唯一标识码)实现-基于snowflake算法-云栖社区-阿 ...

  7. 分布式ID生成器 snowflake(雪花)算法

    在springboot的启动类中引入 @Bean public IdWorker idWorkker(){ return new IdWorker(1, 1); } 在代码中调用 @Autowired ...

  8. 分布式Snowflake雪花算法

    前言 项目中主键ID生成方式比较多,但是哪种方式更能提高的我们的工作效率.项目质量.代码实用性以及健壮性呢,下面作了一下比较,目前雪花算法的优点还是很明显的. 优缺点比较 UUID(缺点:太长.没法排 ...

  9. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

随机推荐

  1. 好客租房53-context的使用

    app组件要传递给child组件 该如何处理 更好的姿势 跨组件传递数据 Provider 用来提供数据 Consumer用来消费数据 1调用React.createContext() 创建provi ...

  2. unity---脚本创建按钮

    脚本创建按钮 新建文件夹 Resources 方便引用图片 在文件Resources中新建Images,并且下载一个图片 没有图片,按钮内容无法显示 图片需要处理一下 Textrue Type 改为 ...

  3. Mac下iTerm2安装rzsz后上传下载失败解决

    背景描述 mac环境,安装了iTerm2,需要使用ssh登陆linux服务器.服务器登陆需要经过以下步骤 输入token 输入登陆选项 输入IP 因此写了expect脚本来完成自动输入 但是在上传下载 ...

  4. docker安装nginx,配置SSL

    nginx安装 下载镜像并测试 1.docker pull nginx 2.docker images nginx 查看我们拉取到本地的nginx镜像IMAGE ID 3.首先测试下nginx镜像是否 ...

  5. React简单教程-4.1-hook

    前言 虽然我们简单感受了一下 useState 的用法,但我想你还是对 React 里的 hook 迷迷糊糊的.本文我们将明确下 React 的概念. HOOK 前生今世 在我示例中,写的 React ...

  6. 代码模板整理(0):先水一波a+b

    梦开始的地方 "Hello World!" #include<bits/stdc++.h> using namespace std; int main(){ cout& ...

  7. 高度灵活可定制的PC布局:头部按钮、左边栏、右边栏、状态栏

    什么是自适应布局 CabloyJS提供了一套布局管理器,实现自适应布局 关于自适应布局的概念,强烈建议先阅读以下两篇文章: 自适应布局:pc = mobile + pad 自适应布局:视图尺寸 什么是 ...

  8. 对TCP粘包拆包的理解

    TCP的粘包与拆包 TCP是一种字节流(byte-stream)协议,所谓流,就是没有界限的一串数据. 一个完整的包会被TCP拆为多个包进行发送,也有可能把多个小包封装成一个大的数据包发送,这就是所谓 ...

  9. 介绍python和库文件管理

    一.Python 特点 1.易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单. 2.易于阅读:Python代码定义的更清晰. 3.易于维护:Python的成功 ...

  10. 第三章、DNS域名解析服务

    DNS 1DNS简介 域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务.它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网.DNS ...