UUID的弊端以及雪花算法

b4f863aab27be0acb3a68ae850849880.jpeg

程序员的成长之路

互联网/程序员/技术/资料共享 

关注

阅读本文大概需要 8 分钟。

来自:blog.csdn.net/m0_62946761/article/details/129033121

一、问题

为什么需要分布式全局唯一ID以及分布式ID的业务需求

在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识

  • 如在美团点评的金融、支付、餐饮、酒店;

  • 猫眼电影等产品的系统中数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息;

  • 特别一点的如订单、骑手、优惠券也都需要有唯一ID做标识。

此时一个能够生成全局唯一ID的系统是非常必要的

ID生成规则部分硬性要求

①全局唯一

即不能出现重复ID号

②趋势递增

在Mysql的InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用的是BTree的数据结构作为存储索引数据,在主键的选择上我们应该尽量使用有序的主键保证写入性能

③单调递增

保证下一个ID一定大于上一个ID,如事务版本号、排序等特殊要求

④信息安全

如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可:如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,需要ID无规则不规则,让竞争对手不好猜

⑤含时间戳

在开发中能快速了解这个分布式id生成的时间

ID号生成系统的可用性要求

①高可用

发一个获取分布式ID的请求,服务器要保证99.999%的情况下能给我返回一个分布式ID

②低延迟

返回的速度要快

③高QPS(抵抗高访问)

比如一秒内10万个,需要服务器能抵抗住并且生成10万个分布式ID

二、一般通用方案

(一)UUID

jdk自带,生成36位字符,形式为8-4-4-4-12,包含4个连号-,对于单体式仅需满足唯一的话可以使用

  • 好处:性能高,本地生成,无网络消耗

  • 坏处:无序且字符串长,存入数据库会增大数据库压力,入数据库性能差。mysql官方推荐主键越短越好

索引, B+树索引的分裂

既然分布式id是主键,然后主键是包含索引的,然后mysql的索引是通过b+树来实现的,每一次新的UUID数据的插入,为了查询的优化,都会对索引底层的b+树进行修改,因为UUID数据是无序的。

所以每一次UUID数据的插入都会对主键地的b+树进行很大的修改,这一点很不好。插入完全无序,不但会导致一些中间节点产生分裂,也会白白创造出很多不饱和的节点,这样大大降低了数据库插入的性能

(二)数据库自增主键

数据库自增主键实现原理:数据库自增id和replace into实现的

REPLACE INTO的含义是插入一条记录,如果表中唯一索引的值遇到冲突,则替换老数据

那数据库自增ID机制适合作分布式ID吗? 答案是不太适合

  1. 系统水平扩展比较困难,比如定义好了步长和机器台数之后,如果要添加机器该怎么做? 假设现在只有一台机器发号是1,2.3.4.5(步长是1),这个时候需要扩容机器一台。可以这样做,把第二台机器的初始值设置得比第一台超过很多,貌似还好,现在想象一下如果我们线上有100台机器,这个时候要扩容该怎么做? 简直是噩梦。所以系统水平扩展方案复杂难以实现。

  2. 数据库压力还是很大,每次获取ID都得读写一次数据库,非常影响性能,不符合分布式ID里面的延迟低和要高QPS的规则(在高并发下,如果都去数据库里面获取id,那是非常影响性能的)

(三)Redis生成全局id策略

因为Redis是单线的天生保证原子性,可以使用原子操作INCR和INCRBY来实现

注意:在Redis集群情况下,同样和MySQL一样需要设置不同的增长步长,同时key一定要设置有效期

可以使用Redis集群来获取更高的吞吐量。

假如一个集群中有5台Redis。可以初始化每台Redis的值分别是1,2,3,4,5,然后步长都是5。

各个Redis生成的ID为:

  • A: 1,6,11,16,21

  • B: 2,7,12,17,22

  • C: 3,8,13,18,23

  • D: 4,9,14,19,24

  • E: 5,10,15,20,25

三、snowflake

(一)概述

官网:

  • https://github.com/twitter-archive/snowflake

Twitter的分布式自增ID算法

特点:

① 能够按照时间有序生成

②Snowflake算法生成id的结果是一个64bit大小的整数,为一个Long型,转化为字符串最多19位

③分布式系统内不会产生ID碰撞(由datacenter和workerld作区分) 并且效率较高。

(二)结构

c0f581559eb10ee39651d72ce04e0327.png

bit表示+-,id一般为正,所以固定为0

时间戳位范围为0-2^41次方,大概是可以使用69年(从1970算起)

工作进程位最多为2^10,即1024个节点,包括5位datacenter和5位workerld作区分

12bit-序列号,序列号,用来记录同毫秒内产生的不同id。12位(bit) 可以表示的最大正整数是2^12-1=4059

(三)代码

/*** Twitter_Snowflake<br>* SnowFlake的结构如下(每部分用-分开):<br>* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>* 加起来刚好64位,为一个Long型。<br>* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。*/
public class SnowflakeIdWorker {// ==============================Fields===========================================/** 开始时间截 (2020-08-28) */private final long twepoch = 1598598185157L;/** 机器id所占的位数 */private final long workerIdBits = 5L;/** 数据标识id所占的位数 */private final long datacenterIdBits = 5L;/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/** 支持的最大数据标识id,结果是31 */private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);/** 序列在id中占的位数 */private final long sequenceBits = 12L;/** 机器ID向左移12位 */private final long workerIdShift = sequenceBits;/** 数据标识id向左移17位(12+5) */private final long datacenterIdShift = sequenceBits + workerIdBits;/** 时间截向左移22位(5+5+12) */private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */private final long sequenceMask = -1L ^ (-1L << sequenceBits);/** 工作机器ID(0~31) */private long workerId;/** 数据中心ID(0~31) */private long datacenterId;/** 毫秒内序列(0~4095) */private long sequence = 0L;/** 上次生成ID的时间截 */private long lastTimestamp = -1L;//==============================Constructors=====================================/*** 构造函数* @param workerId 工作ID (0~31)* @param datacenterId 数据中心ID (0~31) 此方法是判断传入的机房号和机器号是否超过了最大值,即31,或者小于0*/public SnowflakeIdWorker(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}// ==============================Methods==========================================/** 核心方法* 获得下一个ID (该方法是线程安全的)* @return SnowflakeId*/public synchronized long nextId() {//1.获取当前的系统时间long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {// sequence 要增1, 但要预防sequence超过 最大值4095,所以要 与 SEQUENCE_MASK 按位求与 // 即如果此时sequence等于4095,加1后为4096,再和4095按位与后,结果为0sequence = (sequence + 1) & sequenceMask;// 毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截//把当前时间赋值给 lastTime, 以便下一次判断是否处在同一个毫秒内lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDlong id = ((timestamp - twepoch) << timestampLeftShift) // 时间戳减去默认时间 再左移22位 与运算| (datacenterId << datacenterIdShift) // 机房号 左移17位 与运算| (workerId << workerIdShift) // 机器号 左移12位 与运算| sequence; // 序列号无需左移 直接进行与运算return id;}/*** 阻塞到下一个毫秒,直到获得新的时间戳* @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间* @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}//==============================Test=============================================/** 测试 */public static void main(String[] args) {SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);for (int i = 0; i < 1000; i++) {long id = idWorker.nextId();System.out.println(id);}}
}

(四)优缺点

优点:

毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。可以根据自身业务特性分配bit位,非常灵活。

缺点:

依赖机器时钟,如果机器时钟回拨,会导致重复ID生成在单机上是递增的,但是由于设计到分布式环境,每台机器上的时钟不可能完全同步,有时候会出现不是全局递增的情况(此缺点可以认为无所谓,一般分布式ID只要求趋势递增,并不会亚格要求递增,90%的需求都只要求趋势递增)

缺点的解决方案

可以参照以下两个来进行机器时钟的同步

  • 百度开源的分布式唯一ID生成器UidGenerator

  • Leaf--美团点评分布式ID生成系统

<END>

推荐阅读:

IDE + ChatGPT,这款编辑器真的做到可以自动写代码了!

SpringBoot 调用外部接口的三种方式

互联网初中高级大厂面试题(9个G)
内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper......等技术栈!
⬇戳阅读原文领取!                                  朕已阅

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

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

相关文章

【C++笔记总结】面向对象编程——封装 |C++

文章目录 前言一、类的封装1.1、公有&#xff0c;私有&#xff0c;保护1.2、类的定义和类的实现相分离1.3、构造函数&#xff0c;析构函数&#xff0c;拷贝构造函数1.4、静态数据成员和静态成员函数1.5、友元函数&#xff0c;友元类 二、类的实现——对象2.1、对象的静态分配&a…

自然语言处理

1. 自然语言处理任务 1.1. 语言的特点 词汇量大、特征多、类别多&#xff1b;语义信息丰富且隐晦&#xff0c;同义词、近义词、反语等&#xff1b;语言之间有差异性。 1.2. 自然语言处理 vs 语音识别 语音识别是把声学信号和文字进行相互转换&#xff1b;自然语言处理更多…

高性能内存池设计【传统内存管理的弊端以及解决方法】

文章目录 &#x1f34e;常用的内存操作函数&#x1f382;传统内存管理的弊端⭐弊端一⭐弊端二⭐弊端三⭐弊端四 &#x1f680;解决方法⭐内存管理工具选择⭐内存池技术 &#x1f34e;常用的内存操作函数 void *malloc(size_t size);void *calloc(size_t nmemb, size_t size);v…

paper文献和科研小工具

一、好用的网站 Aminer 二、好用的工具 ​1. SciSpace SciSpace官网 【ChatGPT 论文阅读神器】SciSpace 用户注册与实战测试 SciSpace是一款基于 ChatGPT 的论文阅读神器。 2. ReadPaper 强大且超实用的论文阅读工具——ReadPaper ReadPaper官网 ReadPaper下载链接 Rea…

重磅插件 - Bito – GPT-4

文章目录 1.介绍 Bito – GPT-42.Bito AI 功能一览2.1 AI Chat2.2 常用快捷方式2.3 生成的代码可直接插入光标处2.4 优化后的代码与源代码鲜明对比2.5 定制自己的提示模板2.6 Bito 插件2.7 Bito CLI 3.安装 Bito – GPT-44.登陆 Bito – GPT-4 创作不易&#xff0c;如果本文对你…

NBA30只球队2020年各队数据分析

今天周末有点时间&#xff0c;自己也是非常热爱篮球&#xff0c;非常喜欢勒布朗.詹姆斯和德维恩.韦德。我们对2020年NBA各队数据做一个整体分析&#xff0c;之前获取的数据在去掉一些重复数据和问题数据之后&#xff0c;数据量有所降低&#xff0c;所以此次分析纯属娱乐&#x…

哪个NBA球队会夺冠?用深度学习预测最有潜力的球员!

全文共4623字&#xff0c;预计学习时长9分钟 通过利用机器学习和人工智能的最新进展&#xff0c;一批来自加州大学伯克利分校的顶尖专家解答了一个存在已久的问题&#xff1a;如何预测哪位球员能带领球队获得NBA总冠军&#xff1f; 以下是用于做出这些预测的神经网络图&#xf…

Python:NBA球员得分数据排行爬虫

第1步&#xff1a;从网络上获取球员数据排行网页内容&#xff08;数据来源于虎扑&#xff09; 第2步&#xff1a;提取网页内容中信息到合适的数据结构 第3步&#xff1a;输出结果 代码如下&#xff1a; import requests from bs4 import BeautifulSoup import bs4 def getH…

数据分析---2.数据分析----分析NBA中国官方网站2017-2018赛季的球员数据

衣带渐宽终不悔&#xff0c;为伊消得人憔悴 上一篇文章说道了 爬取了NBA中国官方网站2017-2018赛季的球员数据保存了数据库和csv文件,今天要开始处理这些数据了。 先来看看数据内容&#xff0c;如下 """ author: cht time: 2019/9/7 12:33 """#…

巴西队提前出线,预定大力神杯?数据分析告诉你,到底谁才是冠军

2022年卡塔尔世界杯的第一轮小组赛&#xff0c;已经在众多惊诧、悲伤、惊喜的情绪中结束&#xff0c;而仅仅是第一轮的小组比赛&#xff0c;不断爆出的冷门就足以使人大跌眼镜了&#xff1a; 连续34场世界大赛不败的潘帕斯雄鹰——阿根廷&#xff0c;1-2负于世界排名51的沙特&…

用Python分析NBA球员技术

背景 12011年12月27日&#xff0c;尼克斯用一份无保障合同签下林书豪&#xff0c;23场比赛中&#xff0c;他总共只出场55分钟。2豪哥在采访中说到&#xff0c;“你找我来连练习场都不让上&#xff0c;更不用说正式比赛了”3母亲祷告&#xff1a;“God, if this is your will fo…

(更新中)篮球相关英语积累与2020年NBA东部决赛:迈阿密热火与波士顿凯尔特人

写在前面&#xff1a;一方面学习英文&#xff0c;很少刻意去积累篮球相关的英文表达&#xff1b;另一方面&#xff0c;现在记录的时刻&#xff0c;五年后、十年后回头看&#xff0c;终究是美丽的回忆。放在这里&#xff0c;也是一种青春的记录。 毕竟 Lebron James 也将在不久…

武汉光迅科技22校招笔试题(武汉邮科院控股国企上市大厂Python的txt文本处理笔试题)

武汉光迅科技22校招笔试题&#xff08;武汉邮科院控股国企上市大厂Python的txt文本处理笔试题&#xff09; 资源&#xff1a; https://download.csdn.net/download/weixin_53403301/33844279 题目要求&#xff1a; 输入数据&#xff1a; 见附件 <125模块温度查询数据.txt&…

广和通联合中国联通、紫光展锐正式发布LTE Cat.1 bis模组雁飞VN200

2023世界移动通信大会&#xff08;MWC Barcelona 2023&#xff09;于2月27日拉开帷幕&#xff0c;运营商、终端厂商、芯片商、标准组等逾千个单位参加并展示创新科技成果。期间&#xff0c;中国联通、紫光展锐与广和通联合发布LTE Cat 1 bis模组雁飞VN200。联通华盛副总经理陈丰…

未来光通信迈入多通道集成时代,泰克助力上海交大搭建下一代光通信研发平台

泰克科技 基于长期的合作与研究&#xff0c;泰克协助上海交大“区域光纤通信网与新型光通信系统”国家重点实验室&#xff08;以下简称光纤国重&#xff09;搭建起一套业界领先的光通信测试平台&#xff0c;该平台基于泰克4通道同步的59GHz高速实时示波器&#xff0c;最高支持…

致态TiPlus5000固件+升级软件(联芸MAP1202主控)

致态TiPlus5000固件进行了优化&#xff0c;从SVN7151、ZTA08322更新到ZTA09139&#xff0c;版本更新的说明如下&#xff1a;本次为硬盘兼容性更新&#xff0c;修复平台兼容性问题&#xff0c;提高固态硬盘运行稳定性。修复S.M.A.R.T信息中传感器数量及数值显示异常问题。 1、安…

2021年中国集成电路重点企业对比(光迅科技VS大唐电信VS士兰微VS中芯国际)[图]

一、现状 据国家统计局数据显示&#xff0c;2020年中国集成电路累计产量达到了2614.2亿块&#xff0c;同比增长29.53%。2021年中国集成电路累计产量达到了3594亿块&#xff0c;同比增长37.48%。 2011-2021年中国集成电路产量及增速 资料来源&#xff1a;国家统计局、智研咨询整…

光通信的最新技术趋势

大家好&#xff0c;我是小枣君。 上周&#xff0c;我参加了“2021中国光通信高质量发展论坛”&#xff0c;有一些收获与思考。特此撰文&#xff0c;与大家分享。 ▉ 光通信的发展现状 1966年&#xff0c;华裔科学家高锟博士发表了那篇划时代的经典论文——《光频率介质纤维表面…

省时科技ChatGPT服务正式发布,接入OpenAI、微软官方商用服务权限,等你来体验(文末有福利!)...

省时查报告-专业、及时、全面的报告库 省时查方案-专业、及时、全面的方案库 废话不说&#xff0c;先上ChatGPT的使用链接&#xff0c;点开就可以直接使用&#xff1a; https://chatgpt.zntjxt.com ChatGPT可以做的事情&#xff0c;相信使用过的用户有所体会&#xff0c;没用过…

可见光通信!触摸6G科技,玩转光联万物

VLC功能护眼台灯 在健康护眼台灯基础上&#xff0c;多了可见光通信功能 有什么用&#xff1f; 可见光通信&#xff1a;把信息编辑到灯光里&#xff0c;以光线的形式发散出来&#xff0c;人眼捕捉不到&#xff0c;但手机能完全接收。 有了VLC功能&#xff0c;台灯不仅是灯&…