Apache Seata基于改良版雪花算法的分布式UUID生成器分析1


title: Seata基于改良版雪花算法的分布式UUID生成器分析
author: selfishlover
keywords: [Seata, snowflake, UUID]
date: 2021/05/08

本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。

Seata基于改良版雪花算法的分布式UUID生成器分析

Seata内置了一个分布式UUID生成器,用于辅助生成全局事务ID和分支事务ID。我们希望该生成器具有如下特点:

  • 高性能
  • 全局唯一
  • 趋势递增

高性能不必多言。全局唯一很重要,否则不同的全局事务/分支事务会混淆在一起。
此外,趋势递增对于使用数据库作为TC集群的存储工具的用户而言,能降低数据页分裂的频率,从而减少数据库的IO压力
(branch_table表以分支事务ID作为主键)。

在老版Seata(1.4以前),该生成器的实现基于标准版的雪花算法。标准版雪花算法网上已经有很多解读文章了,此处就不再赘述了。
尚未了解的同学可以先看看网上的相关资料,再来看此文章。
此处我们谈谈标准版雪花算法的几个缺点:

  1. 时钟敏感。因为ID生成总是和当前操作系统的时间戳绑定的(利用了时间的单调递增性),因此若操作系统的时钟出现回拨,
    生成的ID就会重复(一般而言不会人为地去回拨时钟,但服务器会有偶发的"时钟漂移"现象)。
    对于此问题,Seata的解决策略是记录上一次的时间戳,若发现当前时间戳小于记录值(意味着出现了时钟回拨),则拒绝服务,
    等待时间戳追上记录值。 但这也意味着这段时间内该TC将处于不可用状态。
  2. 突发性能有上限。标准版雪花算法宣称的QPS很大,约400w/s,但严格来说这算耍了个文字游戏~
    因为算法的时间戳单位是毫秒,而分配给序列号的位长度为12,即每毫秒4096个序列空间。
    所以更准确的描述应该是4096/ms。400w/s与4096/ms的区别在于前者不要求每一毫秒的并发都必须低于4096
    (也许有些毫秒会高于4096,有些则低于)。Seata亦遵循此限制,若当前时间戳的序列空间已耗尽,会自旋等待下一个时间戳。

在较新的版本上(1.4之后),该生成器针对原算法进行了一定的优化改良,很好地解决了上述的2个问题。
改进的核心思想是解除与操作系统时间戳的时刻绑定,生成器只在初始化时获取了系统当前的时间戳,作为初始时间戳,
但之后就不再与系统时间戳保持同步了。它之后的递增,只由序列号的递增来驱动。比如序列号当前值是4095,下一个请求进来,
序列号+1溢出12位空间,序列号重新归零,而溢出的进位则加到时间戳上,从而让时间戳+1。
至此,时间戳和序列号实际可视为一个整体了。实际上我们也是这样做的,为了方便这种溢出进位,我们调整了64位ID的位分配策略,
由原版的:
在这里插入图片描述

改成(即时间戳和节点ID换个位置):
在这里插入图片描述

这样时间戳和序列号在内存上是连在一块的,在实现上就很容易用一个AtomicLong来同时保存它俩:

/*** timestamp and sequence mix in one Long* highest 11 bit: not used* middle  41 bit: timestamp* lowest  12 bit: sequence*/
private AtomicLong timestampAndSequence;

最高11位可以在初始化时就确定好,之后不再变化:

/*** business meaning: machine ID (0 ~ 1023)* actual layout in memory:* highest 1 bit: 0* middle 10 bit: workerId* lowest 53 bit: all 0*/
private long workerId;

那么在生产ID时就很简单了:

public long nextId() {// 获得递增后的时间戳和序列号long next = timestampAndSequence.incrementAndGet();// 截取低53位long timestampWithSequence = next & timestampAndSequenceMask;// 跟先前保存好的高11位进行一个或的位运算return workerId | timestampWithSequence;
}

至此,我们可以发现:

  1. 生成器不再有4096/ms的突发性能限制了。倘若某个时间戳的序列号空间耗尽,它会直接推进到下一个时间戳,
    "借用"下一个时间戳的序列号空间(不必担心这种"超前消费"会造成严重后果,下面会阐述理由)。
  2. 生成器弱依赖于操作系统时钟。在运行期间,生成器不受时钟回拨的影响(无论是人为回拨还是机器的时钟漂移),
    因为生成器仅在启动时获取了一遍系统时钟,之后两者不再保持同步。
    唯一可能产生重复ID的只有在重启时的大幅度时钟回拨(人为刻意回拨或者修改操作系统时区,如北京时间改为伦敦时间~
    机器时钟漂移基本是毫秒级的,不会有这么大的幅度)。
  3. 持续不断的"超前消费"会不会使得生成器内的时间戳大大超前于系统的时间戳, 从而在重启时造成ID重复?
    理论上如此,但实际几乎不可能。要达到这种效果,意味该生成器接收的QPS得持续稳定在400w/s之上~
    说实话,TC也扛不住这么高的流量,所以说呢,天塌下来有个子高的先扛着,瓶颈一定不在生成器这里。

此外,我们还调整了下节点ID的生成策略。原版在用户未手动指定节点ID时,会截取本地IPv4地址的低10位作为节点ID。
在实践生产中,发现有零散的节点ID重复的现象(多为采用k8s部署的用户)。例如这样的IP就会重复:

  • 192.168.4.10
  • 192.168.8.10

即只要IP的第4个字节和第3个字节的低2位一样就会重复。
新版的策略改为优先从本机网卡的MAC地址截取低10位,若本机未配置有效的网卡,则在[0, 1023]中随机挑一个作为节点ID。
这样调整后似乎没有新版的用户再报同样的问题了(当然,有待时间的检验,不管怎样,不会比IP截取策略更糟糕)。

以上就是对Seata的分布式UUID生成器的简析,如果您喜欢这个生成器,也可以直接在您的项目里使用它,
它的类声明是public的,完整类名为:
io.seata.common.util.IdWorker

当然,如果您有更好的点子,也欢迎跟Seata社区讨论。

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

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

相关文章

电子式汽车机油压力传感器的接线方法及特点

电子式机油压力传感器由厚膜压力传感器芯片、信号处理电路、外壳、固定电路板装置和两根引线(信号线和报警线)组成。信号处理电路由电源电路、传感器补偿电路、调零电路、电压放大电路、电流放大电路、滤波电路和报警电路组成。 厚膜压力传感器是20世纪…

数据结构 - C/C++

快速跳转 数组链表栈队列串树 目录 数据结构 逻辑结构 物理结构 数据结构 数据 数据不仅仅包括整型、实型等数值类型,还包括字符及声音、图像、视频等非数值类型。 计算机可以理解并按照指定格式处理。 结构 元素相互之间存在一种或多种特定关系的数据集合。 …

tuxera ntfs for mac是什么 tuxera ntfs for mac怎么用 tuxera激活码

Tuxera NTFS for Mac是一款完全的mac读写软件,可辅助mac电脑读写ntfs格式,进行磁盘的管理。本文会详细讲解tuxera ntfs for mac的操作方法。 Tuxera NTFS for Mac 2023安装包免费下载:https://souurl.cn/IE35lO 一、Tuxera NTFS for Mac是什…

goget配置多个golang 运行环境

一台主机安装多个golang 运行环境 本环境 windows10 为 基础 mac linux也可以按照此方法操作 背景 开发不同的运维工具会用到不同版本的golang,但是开发者不能一直进行重装来处理 ,因此 需要一个工具进行golang版本的管理 go管理工具介绍 gvm (Go V…

微服务保护和分布式事务(Sentinel、Seata)笔记

一、雪崩问题的解决的服务保护技术了解 二、Sentinel 2.1Sentinel入门 1.Sentinel的安装 (1)下载Sentinel的tar安装包先 (2)将jar包放在任意非中文、不包含特殊字符的目录下,重命名为 sentinel-dashboard.jar &…

【C++报错】error C2143:语法错误:缺少“ : ”(在“<” 的前面)[ 相互引用问题 ]

这里写自定义目录标题 情景再现问题分析是经典的互相引用问题 解决方案 情景再现 问题分析 template<class K,class T,class KeyofT,class Hash>class __HTIterator{typedef HashNode<T> Node;typedef HashTable<K, T, KeyofT, Hash> HT; 【★】typedef …

基于springboot+vue+Mysql的网上商城购物系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Java创建并遍历N叉树(前序遍历)

力扣 title589&#xff1a;N叉树的前序遍历 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表示&#xff0c;每组子节点由空值 null 分隔&#xff08;请参见示例&#xff09;。 思路&#xff1a; 1.初始化时…

[ACTF2020 新生赛]BackupFile 1 [极客大挑战 2019]BuyFlag 1 [护网杯 2018]easy_tornado 1

目录 [ACTF2020 新生赛]BackupFile 1 1.打开页面&#xff0c;叫我们去找源文件 2.想到用disearch扫描&#xff0c;发现源文件index.php.bak 3.访问这个文件&#xff0c;下载一个文件&#xff0c;用记事本打开 4.翻译php代码 5.构造payload url/?key123&#xff0c;得到fl…

如何配置和使用Apollo的component里的plugin

关于如何使用Apollo的Component里的plugin&#xff0c;在Apollo的文档里只有如果和开发的说明却没有找到一个清楚完整说明怎么把plugin跑起来的说明&#xff0c;例如我想把lidar_detection_filter按我们的需求对目标过滤算法作修改然后编译完后&#xff0c;执行 cyber_launch …

【七十三】【算法分析与设计】516. 最长回文子序列,二叉树,329. 矩阵中的最长递增路径,记忆化递归填表

516. 最长回文子序列 给你一个字符串 s &#xff0c;找出其中最长的回文子序列&#xff0c;并返回该序列的长度。 子序列定义为&#xff1a;不改变剩余字符顺序的情况下&#xff0c;删除某些字符或者不删除任何字符形成的一个序列。 示例 1&#xff1a; 输入&#xff1a;s &qu…

《QT实用小工具·五十一》带动画的 CheckBox

1、概述 源码放在文章末尾 该项目实现了带动画效果的多选框&#xff0c;鼠标放在上面或者选中都会呈现炫酷的动画效果&#xff0c;demo演示如下&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef LINEARCHECKBOX_H #define LINEARCHECKBOX_H#include <QCheckBox> …

OPPO手机支持深度测试+免深度测试解锁BL+ROOT权限机型整理-2024年3月更新

绿厂OPPO手机线上线下卖的都很不错&#xff0c;目前市场份额十分巨大&#xff0c;用户自然也非常多&#xff0c;而近期ROM乐园后台受到很多关于OPPO手机的私信&#xff0c;咨询哪些机型支持解锁BL&#xff0c;ROOT刷机&#xff0c;今天ROM乐园正式盘点当前市场上可以解BL刷root…

Mysql-黑马

Mysql-黑马 编写规范&#xff1a;## 一级1. 二级三级 1.Mysql概述 数据库概念mysql数据仓库 cmd启动和停止 net start mysql180 net stop mysql180备注&#xff1a;其中的mysql180是服务名 客户端连接 远程连接数据仓库 -h 主机号 -P端口号 mysql [-h 127.0.0.1] [-P 33…

1083 是否存在相等的差

solution 输出的是重复的差值&#xff0c;而非全部差值 #include<iostream> #include<algorithm> using namespace std; const int maxn 1e4 10; int flag[maxn] {0}; int main(){int n, x;scanf("%d", &n);for(int i 1; i < n; i){scanf(&…

一文全面了解 wxWidgets 布局器(Sizers)

目录 Sizers背后的理念 共同特征 最小大小 边框 对齐方式 伸缩因子 使用 Sizer 隐藏控件 wxBoxSizer wxStaticBoxSizer wxGridSizer wxFlexGridSizer 布局器&#xff08;Sizers&#xff09;&#xff0c;由wxWidgets类层次结构中的wxSizer类及其派生类表示&#xff0…

个人博客建设必备:精选域名和主机的终极攻略

本文目录 &#x1f30f;引言&#x1f30f;域名的选择&#x1f315;域名的重要性品牌识别营销和宣传可访问性和易记性信任和权威感搜索引擎优化&#xff08;SEO&#xff09;未来的灵活性和扩展性保护品牌 &#x1f315;如何选择域名&#x1f315;工具与资源分享国内的主流域名注…

springboot 自动配置源码解读

什么是自动装配 当我们程序依赖第三方功能组件时&#xff0c;不需要手动将这些组件类加载到IOC容器中。例如 当程序需要用到redis时&#xff0c;在pom.xml文件中引入依赖&#xff0c;然后使用依赖注入的方式直接从IOC容器中拿到相应RedisTemplate实例。 SpringBootApplication …

LeetCode 98.验证二叉搜索树

题目描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff…

如何在低代码平台中引用 JavaScript ?

引言 在当今快速发展的数字化时代&#xff0c;企业对业务应用的需求日益复杂且多元。低代码开发平台作为一个创新的解决方案&#xff0c;以直观易用的设计理念&#xff0c;打破了传统的编程壁垒&#xff0c;让非技术人员也能轻松构建功能完备的Web应用程序&#xff0c;无需深入…