Leaf分布式ID

文章目录

  • 系统对Id号的要求
  • UUID
  • snowflake
  • Leaf
    • Leaf-snowflake
    • Leaf-segment
      • MySQL自增主键
      • segment
      • 双buffer

系统对Id号的要求

1、业务

1)全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求

2)趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能

3)单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求

  • 数据自增id

4)信息安全:如果ID是连续的, 竞对在两天中午12点分别下单,通过订单id号相减就能大致计算出公司一天的订单量 。所以在一些应用场景下,会需要ID无规则

  • UUID或雪花算法

2、可靠性

  • 平均延迟和TP999延迟都要尽可能低
  • 可用性5个9
  • 高QPS

UUID

1、定义

36个字符,示例:550e8400-e29b-41d4-a716-446655440000

public class IdUtil {/** 返回使用ThreadLocalRandom的UUID,比默认的UUID性能更优*/public static UUID fastUUID() {ThreadLocalRandom random = ThreadLocalRandom.current();return new UUID(random.nextLong(), random.nextLong());}
}

2、缺点

  • 太长,不易于存储
  • 无序性,如果作为数据库主键,可能会引起数据页位置频繁变动,严重影响性能
  • 信息不安全,基于MAC地址生成UUID的算法可能会造成MAC地址泄露

snowflake

1、结构

Long型64位的整数,如图1所示
在这里插入图片描述

  • 41-bit的时间戳

    可以表示(1L<<41)/(1000L360024*365)=69年的时间

  • 10-bit 数据中心ID和机器

    5bit数据中心id,5bit机器id,一般使用ZK分配

  • 12个自增序列号

    在同一毫秒内生成2^12个唯一的ID

理论上snowflake方案的QPS约为409.6w/s,可以保证在任何一个IDC、任何一台机器、在任意毫秒内、生成的ID都是不同的

2、问题

41-bit时间戳部分,强依赖机器时钟,如果机器上时钟回拨,会导致发号重复

Leaf

使用参考:Leaf生成单据号

Leaf-snowflake

1、适合场景:生成的ID需要无规则

2、解决机器时钟回拨问题

1)要求当前时间戳,必须 > 机器创建时间

2)同时

  • 对比其余Leaf节点的系统时间,来判断自身系统时间是否准确
  • RPC请求得到所有节点的系统时间,计算sum(time)/nodeSize < 阈值,则认为正确

阈值 = 5ms

因为理论上5ms内无法,完全使用完成后12个自增序列号,所以不会重复

否则直接报错自动摘除本身节点并报警

Leaf-segment

1、适合场景:生成的ID单调递增

2、实现:基于MySQL的自增主键

MySQL自增主键

1、获取ID方式

使用下列SQL读写MySQL得到ID号

begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
commit;
  • Tickets64:表
  • stub:列

实现方式类似:

useGeneratedKeys=“true“ keyProperty=“id“
  • int insert(XxxDO xxxDo)时,先将DO内容写入db
  • insert成功后,再将JDBC自增主键值AUTO_INCREMENT,回写到DO的id属性字段
  • 后续可能会从DO中获取此id值进行查询数据、编辑数据

2、存在问题

因为每次都是都需要写,读MySQL才能获取ID值,单台MySQL的读写性能是瓶颈

3、解决-集群

  • 在分布式系统中多部署几台机器

  • 每台机器设置不同的初始值,且步长和机器数相等

    比如有两台机器。设置步长step为2,TicketServer1的初始值为1(1,3,5,7,9,11…)

    TicketServer2的初始值为2(2,4,6,8,10…)

  • 假设部署N台机器,步长需设置为N,每台的初始值依次为0,1,2…N-1 ,则整个Leaf架构如图2
    在这里插入图片描述

4、存在问题

  • 数据库压力还是很大,每次获取ID都得读写一次数据库,只能靠堆机器来提高性能
  • 系统水平扩展比较困难,定义好了步长和机器台数之后,如果要添加机器不好做

5、解决- 批量分段(segment)获取


segment

1、实现,如图3所示

1)db表设计

+-------------+--------------+------+-----+-------------------+-------------------------
| Field       | Type         | Null | Key | Default           | Extra                     
+-------------+--------------+------+-----+-------------------+--------------------------
| biz_tag     | varchar(128) | NO   | PRI |                   |                           
| max_id      | bigint(20)   | NO   |     | 1                 |                           
| step        | int(11)      | NO   |     | NULL              |                           
| desc        | varchar(256) | YES  |     | NULL              |                           
| update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update 
  • biz_tag用来区分业务(外卖、支付)
  • max_id表示该biz_tag目前所被分配的ID号段的最大值
UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx
  • step表示每次分配的号段长度

2)系统架构
在这里插入图片描述

3) ID值趋势递增

eg:test_tag业务

  • Leaf Server 1:从DB加载号段[1,1000]。
  • Leaf Server 2:从DB加载号段[1001,2000]。
  • Leaf Server 3:从DB加载号段[2001,3000]。

用户通过Round-robin的方式调用Leaf Server的各个服务,通过CAS获取ID,所以某一个Client获取到的ID序列

可能是:1,1001,2001,2,1002,2002……

也可能是:1,2,1001,2001,2002,2003,3,4…

当某个Leaf Server号段用完之后,下一次请求就会从DB中加载新的号段,这样保证了每次加载的号段是递增

2、解决-读写性能

  • 原来获取一个ID值,都需要读写一次数据库

  • 现在只需要把step设置得足够大,比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次

    读写数据库的频率从1减小到了1/step

  • test_tag业务,在第一台Leaf机器上是1~1000的号段,当这个号段用完时

  • 会去加载另一个长度为step=1000的号段

  • 假设另外两台号段都没有更新,这个时候第一台机器新加载的号段就应该是3001~4000

  • 同时数据库对应的biz_tag = test_tag 这条数据的max_id会从3000被更新成4000

3、 解决-扩容操作

只需要对biz_tag分库分表

4、问题

  • 在号段消耗完的时候进行取号段时,还是会夯在更新数据库的I/O上

  • 假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢

5、解决-双buffer


双buffer

1、解决-双buffer

  • 当其中一个Buffer中的号段消费到某个点(90%)时,就启异步线程的把下一个号段加载到内存中的另一个Buffer

2、 容灾

分库分表

4、问题

  • 在号段消耗完的时候进行取号段时,还是会夯在更新数据库的I/O上

  • 假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢

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

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

相关文章

贪心系列专题篇四

​​​​​​​ 目录 整数替换 解法一 解法二 俄罗斯套娃信封问题 堆箱子 可被三整除的最大和 距离相等的条形码 重构字符串 声明&#xff1a;接下来主要使用贪心法来解决问题&#xff01;&#xff01;&#xff01; 整数替换 题目 思路 下面将使用两种方法来解决这…

【数据结构】算法的时间复杂度与空间复杂度

计算机考研408-数据结构笔记本之——第一章 绪论 1.2 算法和算法评价 1.2.2 算法效率的度量 算法效率的度量是通过时间复杂度和空间复杂度来描述的。 1.时间复杂度 时间复杂度T(n)是事前预估算法时间开销T(n)与问题规模 n 的关系&#xff08;T 表示 “time”&#xff09;&a…

眼在手外-机器人坐标系与相机坐标系标定方法

1 眼在手外坐标系概述 实现机械臂和相机的手眼标定&#xff0c;就是要通过双目相机坐标系、机械臂坐标系和机械臂 末端执行器三者的坐标系转换&#xff0c;求出手眼转换矩阵。设双目相机坐标系为 Oc&#xff0c;标定板坐标 系为 Ow&#xff0c;末端执行器坐标系为 Oe&#xff0…

【C++】二维数组定义方式

二维数组有四种定义方式 1、数据类型 数组名[行数 ][ 列数 ]; 2、数据类型 数组名[ 行数 ][ 列数 ]{{数据1&#xff0c;数据2}&#xff0c;{数据3&#xff0c;数据4 }}; 3、数据类型 数组名[ 行数 ][ 列数 ]{数据1&#xff0c;数据2&#xff0c;数据3&#xff…

Java | Leetcode Java题解之第321题拼接最大数

题目&#xff1a; 题解&#xff1a; class Solution {public int[] maxNumber(int[] nums1, int[] nums2, int k) {int m nums1.length, n nums2.length;int[] maxSubsequence new int[k];int start Math.max(0, k - n), end Math.min(k, m);for (int i start; i < e…

04 表的操作

目录 创建查看修改删除 1. 创建 语法&#xff1a; CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储殷勤; 说明&#xff1a; field&#xff0c;表示列名 datatype&#xff0c;表示列的类型…

C++ 继承 派生类的拷贝构造

继承 派生类的拷贝构造构造顺序拷贝构造 引例1: 当子类,不自实现拷贝构造时,默认调用父类的拷贝构造引例2: 子类自实现拷贝构造,不做特殊处理时,只会调用父类的构造器.引例3: 显示的调用父类的拷贝构造器。案例: 内嵌函数的拷贝构造 引例1 :当内嵌子对象,子类不自实现拷贝构造时…

【java】单行注释(//)与多选注释(/* */)

文章目录 单行注释多行注释注意事项 在Java中&#xff0c;注释是用来给代码添加说明的&#xff0c;它不会被编译器执行。Java提供了两种主要的注释方式&#xff1a;单行注释和多行注释&#xff08;有时也称为块注释或块级注释&#xff09;。 单行注释 单行注释以两个正斜杠&…

数模评价决策类—熵权法

目录 文章目录 前言 一、熵权法是什么&#xff1f; 二、基本步骤 计算样本权重及熵权 总结 前言 前面我们学习了层次分析法和Topsis法&#xff0c;这两个方法都是偏主观的方法&#xff0c;这篇我们将运用较为客观的方法——熵权法&#xff0c;做出决策 一、熵权法是什么…

JavaWeb-HTML

一、HTML&CSS&JavaSript的作用: 1.HTML主要用于网页为主体结构的搭建&#xff1b; 2.CSS主要用于页面元素的美化 3.JavaScript主要用于页面元素的动态处理; 二、HTML HTML是Hyper Text Markup Language的缩写。意思是超文本标记语言。它的作用是搭建网页结构&…

2024死磕小红书,一定能赚到钱!

​2024死磕小红书&#xff0c;一定能赚到钱&#xff01;在文末领取小红书运营完全指南电子书 从2023年起&#xff0c;小红书这股热乎劲儿就像开了挂&#xff0c;突然间就成了人人想蹭的“显学”。大伙儿都想趁着平台红利期&#xff0c;分一杯羹。说来惭愧&#xff0c;我从2020年…

牛顿插值法代替泰勒公式

引入 例题 近似函数&#xff1a; 通过这个近似函数可以看出&#xff0c;若要证的函数超过二阶可导&#xff0c;那么就不适合用牛顿插值法代替泰勒公式 因为&#xff0c;后面的操作非常复杂&#xff0c;不划算了… 总结 我们可以通过牛顿插值法生成一个逼近曲线的直线&#xf…

使用Python库开发Markdown编辑器并将内容导出为图片

简介 在本文中&#xff0c;我们将探索如何使用Python的wxPython库开发一个Markdown编辑器应用程序。这个应用程序不仅能浏览和编辑Markdown文件&#xff0c;还可以将编辑的内容导出为PNG图片。 C:\pythoncode\new\markdowneditor.py 完整代码 import wx import markdown2 im…

【传知代码】实体关系抽取(论文复现)

当谈论信息提取领域的最前沿时&#xff0c;实体关系抽取无疑是其中一颗耀眼的明星。从大数据时代的信息海洋中提炼出有意义的关系&#xff0c;不仅是科技进步的体现&#xff0c;更是人类对知识管理和智能决策迫切需求的响应。本文将探索实体关系抽取的核心技术、应用场景及其在…

太阳光模拟器在光纤中的应用

概述 太阳光模拟器是一种重要的实验室设备&#xff0c;它能模拟太阳光的光谱、强度和角度分布&#xff0c;广泛应用于光纤通信、光电器件测试、太阳能研究等多个领域。通过模拟太阳光的光照条件&#xff0c;研究人员可以在实验室环境中对光电材料和器件进行性能测试和研究。 太…

二维码生成原理及解码原理

☝☝☝二维码配图 二维码 二维码&#xff08;Quick Response Code&#xff0c;简称QR码&#xff09;是一种广泛使用的二维条形码技术&#xff0c;由日本公司Denso Wave在1994年开发。二维码能有效地存储和传递信息&#xff0c;广泛应用于商品追溯、支付、广告等多个领域。二维…

c++入门基础(下篇)————引用、inline、nullptr

引用 引用的概念和定义 引⽤不是新定义⼀个变量&#xff0c;⽽是给已存在变量取了⼀个别名&#xff0c;编译器不会为引⽤变量开辟内存空间&#xff0c; 它和它引⽤的变量共⽤同⼀块内存空间。 类型& 引用别名 引用对象; 就像孙悟空也叫齐天大圣 猪八戒也叫天蓬元帅。…

Meinberg Lantime服务器监控指标解读

监控易是一款功能强大的IT基础设施监控软件&#xff0c;它能够实时监控各种IT设备的状态&#xff0c;提供全面的性能分析和告警通知服务。对于Meinberg Lantime服务器&#xff0c;监控易通过一系列监控指标&#xff0c;确保服务器的稳定运行和服务的可用性。 一、监控对象概述…

策略模式的一次应用

项目的需求是将一组图像按照相似度分类。 采用了模板匹配计算相似度的实现方式。 #include <opencv2/core.hpp> #include <openev2/core/utility.hpp> #include <opencv2/highqui.hpp> #include <openav2/imgproc.hpp> cv::Mat image matched; double …

Linux系统编程 --- 动静态库

一、回顾&#xff0c;制作一个库 libXXX.a --- 静态链接 libYYY.so --- 动态链接 设计一个库&#xff1a; 把我们提供的方法&#xff0c;给别人用&#xff1a; 1、把源文件直接给他 2、把我们的源代码打包成库 库 头文件。 原理&#xff1a;把所有的.o文件打包成.a文件也…