【分布式】分布式ID

目录

  • 前言
  • 一、雪花算法snowflake
    • 1. 组成
    • 2. 优缺点
    • 3. 时钟回拨怎么解决
      • a. 时钟回拨
      • b. 解决方案
    • 4. 项目中如何使用
  • 二、基于Redis
  • 三、基于Zookeeper
  • 四、号段模式
  • 五、指定步长的自增ID
  • 六、UUID
    • 参考
  • 六、扩展
  • 总结

前言

分布式场景下,一张表可能分散到多个数据结点上。因此需要一些分布式ID的解决方案。

分布式ID需要有几个特点:

  • 全局唯一(必要) :在多个库的主键放在一起也不会重复
  • 有序(必要) :避免频繁触发索引重建
  • 信息安全:ID连续,可以根据订单编号计算一天的单量,造成信息泄露
  • 包含时间戳:能够快速根据ID得知生成时间

下面几种方案按推荐顺序排序,越推荐使用越靠前。

一、雪花算法snowflake

64 位的 long 类型的唯一 id
在这里插入图片描述

1. 组成

1)1位不用

带符号整数第1位是符号位,正数是0,ID一般为正数,此位不用。

2)41位毫秒级时间戳

41位存储当前时间截 – 开始时间截得到的差值,可以表示 2 41 2^{41} 241个毫秒的值,转化成单位年则是: 2 41 1000 ∗ 60 ∗ 60 ∗ 24 ∗ 365 = 69 年 \frac{2^{41}}{1000∗60∗60∗24∗365}=69年 1000606024365241=69

注:开始时间截由程序指定,一般是id生成器开始使用的时间,设置好后避免更改。依赖服务器时间,服务器时钟回拨时可能会生成重复 id。

3)10位机器ID

生成ID的服务可以部署在1024台机器上

4)12位序列号
能够表示4096个序列号。

因此,某一毫秒,同一台机器,最多能生成4096个序号。理论上单机QPS最大为4096*1000=409.6w/s

2. 优缺点

优点:

  • ID不重复:用时间戳+机器+序号生成不重复ID
  • 性能高:在内存中生成
  • 有序

缺点:

依赖服务器时间,存在时钟回拨的问题。

3. 时钟回拨怎么解决

时钟回拨可能产生重复ID进而影响关联系统。

a. 时钟回拨

什么是时钟回拨: 服务器上的时间倒退回之前的时间

哪些情况造成时钟回拨:

  • 人为修改服务器时间
  • 时钟同步后,由于机器之间时间不同,可能产生时钟回拨

b. 解决方案

算法中会记录当前服务上次生成ID的最后时间,只需要保证我下次生成ID的时间大于上次最后时间即可。根据回拨后时间距离上次生成最后时间大小,可以有不同的解决方案。

  • 相差0~100ms :等待直至当前时间超过上次最后生活时间
  • 相差100ms~1s:采用等待方式可能导致接口超时。可以记录已生成ID的最大ID,在这个基础上++。(预留扩展位,在扩展位上增加。回拨后又回拨可能有问题)
  • 相差1s~5s:采用最大ID增加的方式,时间过长可能导致范围溢出。可以生成ID服务响应异常,由调用方例如基于Ribbon调用其他生成ID服务。
  • 相差超过5s:采用Ribbon循环调用的方式,下次访问到时钟回拨的服务可能还没达到上次生成最后时间,浪费时间。可以让超过5s的服务主动下线,并通知运维,人工介入,等待时钟正常后再重启。

4. 项目中如何使用

在这里插入图片描述

时钟回拨的处理逻辑在nextId()里的if (timestamp < lastTimestamp) 逻辑下。这里直接抛出异常。

public class SnowflakeIdWorker {// ==============================Fields===========================================/** 开始时间截 (2020-01-01) */private final long twepoch = 1577808000000L;/** 机器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)*/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() {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 = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (datacenterId << datacenterIdShift) //| (workerId << workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳* @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 < 100; i++) {long id = idWorker.nextId();System.out.println(id);}}
}

二、基于Redis

三、基于Zookeeper

四、号段模式

数据库中保存号段表,每次从数据库获取一个号段范围,由服务在内存中自增生成ID,直到达到号段范围再去获取。

id业务类型最大可用ID号段长度版本号
1xxx200010000
updateset 最大可用ID = 3000, version = version + 1 where version = 0 and biz_type = xxx

采用乐观锁的方式避免长时间锁表。

优点:

  • ID有序递增
  • 对数据库压力比较小

缺点:

  • 存在安全问题,用ID可以判断数据量
  • 存在单点问题,集群实现困难

五、指定步长的自增ID

多主集群模式下,表主键设置自增ID,多节点之间会有重复ID。需要采用指定初始值的自增步长
举例:两台数据库A,B。A初始值和步长为(1,2),B初始值和步长为(2,2)。两张表生成的主键分别为
A:1,3,5,7…
B:2,4,6,8…

设置方法:

set @@auto_increment_offset = 1;     -- 起始值
set @@auto_increment_increment = 2;  -- 步长

优点:

  • 简单、解决单点问题

缺点:

  • 扩容困难

六、UUID

128位长的字符标识串,由32个16进制数字组成,用-连接,共36个字符。如:03425604-5462-11ee-80ad-80fa5b8732b1

生成算法与重复性:

  • 基于随机数:不重复
  • 基于MAC地址:不重复
  • 基于时间戳:可能重复

作为分布式ID的优缺点:

  • 优点:本地生成,性能高,无网络损耗
  • 缺点:
    • 无序:造成索引重建,入库性能差
    • 字符串长:需要36字符

参考

  • UUID会重复吗?
  • 雪花算法视频
  • 9种分布式ID生成方式

六、扩展

  • 百度:UidGenerator
  • 美团:Leaf

总结

优点缺点
uuid实现简单连续性差,作为主键每次新增数据都会触发索引重建。
分布式环境中可能重复
雪花算法性能好,有序依赖服务器时间,时钟回拨可能生成重复ID
号段模式
redis/zookeeperRedis基于INCR 命令生成 分布式全局唯一id
zookeeper一种通过节点,一种通过节点的版本号

基因算法

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

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

相关文章

uniapp——实现在线选座功能——技能提升

首先声明一点&#xff1a;下面的内容是从一个uniapp的程序中摘录的&#xff0c;并非本人所写&#xff0c;先做记录&#xff0c;以免后续遇到相似需求抓耳挠腮。 这里写目录标题 效果图代码——html部分cu-custom组件anil-seat组件 代码——jscss部分 效果图 代码——html部分 …

gin-基础笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、get和post方法二、重定向总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0…

uniapp掉完接口后刷新当前页面方法

uniapp掉完接口后刷新当前页面方法 掉完接口&#xff0c;里面加下面这个方法uni.redirectTo({}) setTimeout(() > {uni.redirectTo({// 当前页面路由url: /pages/property/mutualrotation/mutualrotation);}, 500)实例 mutualRotationSubmit() {let self this;uni.showMod…

Python Opencv实践 - 视频文件操作

参考资料&#xff1a; 视频处理VideoCapture类---OpenCV-Python开发指南&#xff08;38&#xff09;_python opencv videocapture_李元静的博客-CSDN博客 OpenCV VideoCapture.get()参数详解 - 简书FOURCC四字符码对照表_4fvcc_Kellybook的博客-CSDN博客 import cv2 as cv im…

网络基础--1.网络纵横

网络的发展历程 计算机由原来的只能单一处理信息&#xff08;单用户批处理&#xff09;逐步发展为多用户批处理&#xff0c;可以实现一台计算机连接多个终端同时使用一台计算机&#xff08;分时系统&#xff09;&#xff0c;但是多个终端之间不能相互通信&#xff0c;再发展成为…

API接口文档管理系统平台搭建(更新,附系统源码及教程)

简介 这是一款简洁大方的API接口文档管理系统&#xff0c;附系统源码及教程方法。可以轻松管理和使用API接口。 安装步骤 打开config/database.php配置数据库信息导入数据库data.sql设置运行目录为/public伪静态设置think PHP后台地址/admin/login.html 账号&#xff1a;adm…

git 远程名称 远程分支 介绍

原文&#xff1a; 开发者社区> 越前君> 细读 Git | 让你弄懂 origin、HEAD、FETCH_HEAD 相关内容 读书笔记&#xff1a;担心大佬文章搬家&#xff0c;故整理此学习笔记 远程名称&#xff08;Remote Name&#xff09; Origin 1、 origin 只是远程仓库的一个名称&#xff…

【Kafka】Kafka再平衡机制及相关参数

背景 Kafka作为一款基于发布订阅模式的消息队列&#xff0c;生产者将消息发送到Kafka集群&#xff08;Brokers&#xff09;中&#xff0c;消费者&#xff08;Consumer Group &#xff09;拉取消息进行消费&#xff0c;实现了异步机制。Kafka中&#xff0c;消费者通常以消费者组…

解决方案| anyRTC远程检修应用场景

背景 在这个科技飞速发展的时代&#xff0c;各行各业都要求高效运转。然而&#xff0c;当出现问题时&#xff0c;我们却常常因为无法及时解决而感到困扰&#xff0c;传统解决问题的方式是邀请技术人员现场解决问题&#xff0c;如果技术人员解决不了&#xff0c;还要邀请专家从…

编程参考 - std::exchange和std::swap的区别

这两个功能是C standard library中的Standard template library中的一部分。容易混淆&#xff0c;我们来看下它们的区别。 exchange&#xff1a; 这个函数是一个返回原先值的set函数。 std::exchange is a setter returning the old value. int z std::exchange(x, y); Af…

C语言指针详解(3)———指针题目,你确定你学会指针了?进来看看吧!(几十个指针小题+超详解)

你确定你学会指针了&#xff1f; 你确定你明白数组名了&#xff1f; 如果你觉得你学的还不错&#xff0c;就进来看看吧&#xff0c;相信你看完之后一定能收获更多。 数组名的理解一定要弄清楚 数组名是数组首元素的地址 但是有2个例外&#xff1a; sizeof(数组名)&#xff0c;这…

【MySQL多表查询以及事务、索引】

1. 多表查询 1.1 概述 1.1.1 数据准备 #建议&#xff1a;创建新的数据库 create database db04; use db04;-- 部门表 create table tb_dept (id int unsigned primary key auto_increment comment 主键ID,name varchar(10) not null unique comment 部门名称…

Jmeter 实现 mqtt 协议压力测试

1. 下载jmeter&#xff0c;解压 https://jmeter.apache.org/download_jmeter.cgi 以 5.4.3 为例&#xff0c;下载地址&#xff1a; https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.4.3.zip linux下解压&#xff1a; unzip apache-jmeter-5.4.3.zip 2. 下载m…

proxy相对于object.defineproperty有哪些优点?

Proxy相对于Object.defineProperty的优点有1&#xff1a; 功能更强大&#xff1a;Proxy提供了更灵活的方式来操作对象&#xff0c;可以捕获和定制对象的各种操作&#xff0c;包括获取属性、设置属性、删除属性等。捕获多种操作&#xff1a;Proxy可以捕获多种不同的操作&#x…

在线海报图片设计器、图片编辑器源码/仿照稿定设计源码

在线海报设计系统素材设计源码是一个漂亮且功能强大的在线海报图片设计器&#xff0c;仿照稿定设计而成。该系统适用于多种场景&#xff0c;包括海报图片生成、电商分享图、文章长图、视频/公众号封面等。用户无需下载软件&#xff0c;即可轻松实现创意&#xff0c;迅速完成排版…

使用 React Native 针对 Android 进行开发

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 概述 通过安装所需工具开始使用 React Native 创建新的 React Native 项目 本指南将有助于开始使用 Windows 上的…

性能测试工具有哪些?原理是什么?怎么选择适合的工具?

前言 本篇文章主要简单总结下性能测试工具的原理以及如何选型。性能测试和功能测试不同&#xff0c;性能测试的执行是基本功能的重复和并发&#xff0c;需要模拟多用户&#xff0c;在性能测试执行时需要监控指标参数&#xff0c;同时性能测试的结果不是那么显而易见&#xff0…

43.228.64.X游戏盾在业务高峰时间段,出现用户大量掉线问题,可能是什么原因导致

服务器负载过高&#xff1a;业务高峰期间&#xff0c;服务器可能无法处理大量的用户请求&#xff0c;导致性能下降&#xff0c;甚至引起服务器崩溃&#xff0c;从而导致用户掉线。 网络带宽不足&#xff1a;当大量用户同时访问游戏盾时&#xff0c;网络带宽可能不足以支持所有…

Smart Community(1)之设计规范

通过前面大数据开发相关知识的学习&#xff0c;准备做一个项目进行练习---我给他起了一个响亮的名字&#xff1a;基于HadoopHA的智慧社区服务平台 设计规范&#xff1a; 做一个项目之前肯定要先规定一些开发过程中的设计规范 &#xff08;一&#xff09;数据埋点规范&#xf…

linux jenkins2.414.1-1.1版本安装

文章目录 前言一、rpm文件下载二、安装jenkins2.1.升级jdk1.82.2安装jenkins2.3 启动服务2.4 使用密码登录2.5 修改插件源2.6 汉化插件安装演示 总结 前言 之前也安装过jenkins&#xff0c;但是那个版本是2.1的&#xff0c;太老了很多插件都不支持&#xff0c;现在安装目前为止…