当前位置: 首页 > news >正文

全局ID生成方式

全局ID生成方式

目录

  • 1. 全局唯一id介绍
    • 1.1 特点
  • 2. 常见的全局唯一id生成策略
    • 2.1 利用数据库自增字段生成id
    • 2.2 UUID
    • 2.3 Redis生成id
    • 2.4 zookeeper生成ID
    • 2.5 Twitter的snowflake算法
  • 3. 面试题目:实现一个全局的ID生成器,注意线程安全
    • 3.1 单例模式分类
    • 3.2 普通懒汉模式单例(线程不安全)
    • 3.3 线程安全懒汉模式单例
    • 3.4 饿汉模式单例

本文章中前面两个章节转载自:

https://cloud.tencent.com/developer/article/1884037

1. 全局唯一id介绍

在复杂的分布式系统中,需要对大量的数据和消息进行唯一标识,在设计的初期就要考虑到日后的数据量的级别,如果需要对数据库进行分库分表,就需要有一个全局唯一id来标识一条数据或记录。

1.1 特点

全局唯一id主要有以下几个特点

  • 全局唯一性
  • 趋势递增:MySQL InnoDB默认使用的是聚簇索引,底层使用B+ tree的数据结构来存储索引数据,在主键选择上尽量使用有序的主键保证写入性能
  • 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、**IM**增量消息、排序等特殊需求
  • 信息安全:如果ID是连续的,恶意用户的爬取工作就非常容易做了,在一些应用场景下,需要ID无规则、不规则。
  • 高可用性:同时除了对ID号码自身的要求,业务还对ID号生成系统的可用性要求极高,如果ID系统瘫痪,会带来一场灾难,所以不能有单点故障。
  • 分片支持
  • 长度适中

2. 常见的全局唯一id生成策略

2.1 利用数据库自增字段生成id

优点

  • 简单:成本小,代码简单,性能可以接收
  • ID号单调递增,可以实现一些对ID有特殊要求的业务,比如分页或排序等

缺点

  • 强依赖DB。数据库迁移、多数据库版本支持、或分库分表时需要处理,比较麻烦
  • 单点故障。单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成,有单点故障的风险
  • 数据一致性问题。配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。
  • 难于扩展。在新能达不到要求的情况下,难以扩展

部分优化方案

对于主库单点,如果有多个Master库,则每个Master库设置的起始数字不一样,步长一样,可以是Master的个数。例如:Master 1生成的是1,4,7,10;Master 2生成的是2,5,8,11;Master 3生成的是3,6,9,12。这样就可以有效生成集群的唯一ID,也可以大大降低ID生成数据库操作的负载

2.2 UUID

常见的id生成方式,利用程序生成

UUID的目的是让分布式系统中的所有元素,都能有唯一的辨识咨询,而不需要透过中央控制端来做辨识资讯的指定。

UUID的标准形式包含32个16进制数字,以‘-’分为5段,示例:550e8400-e29b-41d4-a716-446655440000
在这里插入图片描述

C++中使用boost生成uuid的示例

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>int main() {boost::uuids::uuid a_uuid = boost:uuids::random_generator();string uuid_string = boost::uuids::to_string(a_uuid);return 0;
}

优点

  • 非常简单,本地生成,可以调用API
  • 性能很高,没有网络消耗,基本不会有性能问题
  • 全球唯一,在数据迁移、系统数据合并等情况下可以从容应对

缺点

  • 存储成本高。16字节128位,通常用36长度的字符串表示,海量数据场景下,需要考虑存储量的问题
  • 信息不安全。基于MAC地址生成UUID的算法可能造成MAC地址泄漏
  • 不适用作为主键
  • UUID是无序的
  • 传输数据量大
  • 不可读

部分优化方案

  • 为了解决UUID不可读,可以使用UUID to Int64方法
  • 为了解决UUID无序的问题, NHibernate在其主键生成方式中提供了Comb算法(combined guid/timestamp)。保留GUID的10个字节,用另6个字节表示GUID生成的时间(DateTime)。

2.3 Redis生成id

当使用数据库生成ID性能不够要求的时候,可以尝试使用Redis来生成ID,这主要依赖于Redis是单线程的,所以也可以用于生成全局唯一的ID。可以使用Redis的原子操作**INCR和INCRBY**来实现。

可以使用Redis集群来获取更高的吞吐量。假如一个集群中有5台Redis。可以初始化每台Redis的值分别是1,2,3,4,5,然后步长都是5。

优点

  • 不依赖数据库,灵活方便,且性能优于数据库
  • 数字ID天然排序,对分页或需要排序的结果很有帮助

缺点

  • 如果系统中没有Redis,还需要引入新的组件,增加系统的复杂度
  • 需要编码和配置的工作量比较大
  • Redis单点故障,影响序列服务的可用性

2.4 zookeeper生成ID

zookeeper主要通过其中znode数据版本来生成序列号,可以生成32位和64位的数据版本号,客户端可以使用这个版本号来作为唯一的序列号。

很少会使用zookeeper来生成唯一ID。主要是由于需要依赖zookeeper,并且是多步调用API,如果在竞争较大的情况下,需要考虑分布式锁。因此,性能在高并发的分布式环境下,也不是很理想。

2.5 Twitter的snowflake算法

snowflake(雪花算法)是Twitter开源的分布式ID生成算法,结果是一个long型的ID。这种方案把64-bit分别划分成多段,分开来标识机器、时间等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sI2XDQmC-1692772460124)(image/image_OUDNHYGCkP.png)]

其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看github

snowflake算法可以根据自身项目的需要进行一定的修改。比如估算未来的数据中心个数,每个数据中心的机器数以及统一毫秒可以能的并发数来调整在算法中所需要的bit数。

优点

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

缺点

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
  • ID可能不是全局递增。在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

3. 面试题目:实现一个全局的ID生成器,注意线程安全

这是我在最近面试过程中遇到的一道代码题目,个人思路是考察设计模式中的单例模式,考虑线程安全的话需要使用饿汉模式,或者双重检查锁的懒汉模式。

3.1 单例模式分类

单例模式可以分为懒汉和饿汉,两者之间的区别在于创建实例的时间不同

  • 懒汉:系统运行时,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例(这种方式需要考虑线程安全
  • 饿汉指系统一运行,就初始化创建实例,当需要时,直接调用即可(本身就线程安全,没有多线程的问题

3.2 普通懒汉模式单例(线程不安全)

#include <iostream>
#include <mutex>
#include <pthread.h>using namespace std;class IdGenerator {
public:// 获取单例对象 static IdGenerator* getInstance();// 释放单例,进程退出时调用static void deleteInstance();// 获取唯一IDint getID();
private:IdGenerator();~IdGenerator();// 将拷贝构造函数设为私有,禁止外部拷贝IdGenerator(const IdGenerator& rhs);const IdGenerator& operator=(const IdGenerator& rhs);// 唯一单例对象指针static IdGenerator* m_IdGenerator;// 全局唯一IDint id;
};// 初始化静态成员变量
IdGenerator* IdGenerator::m_IdGenerator = NULL;int IdGenerator::getID() {return id ++;
}IdGenerator* IdGenerator::getInstance() {if (m_IdGenerator == nullptr) {m_IdGenerator = new IdGenerator();}return m_IdGenerator;
}void IdGenerator::deleteInstance() {if (m_IdGenerator) {delete m_IdGenerator;m_IdGenerator = nullptr;}
}IdGenerator::IdGenerator() : id(0) {}
IdGenerator::~IdGenerator() {}

3.3 线程安全懒汉模式单例

双重检查锁

#include <iostream>
#include <mutex>
#include <pthread.h>using namespace std;// 锁对象
std::mutex g_lock;class IdGenerator {
public:// 获取单例对象 static IdGenerator* getInstance();// 释放单例,进程退出时调用static void deleteInstance();// 获取唯一IDint getID();
private:IdGenerator();~IdGenerator();// 将拷贝构造函数设为私有,禁止外部拷贝IdGenerator(const IdGenerator& rhs);const IdGenerator& operator=(const IdGenerator& rhs);// 唯一单例对象指针static IdGenerator* m_IdGenerator;// 全局唯一IDint id;
};// 初始化静态成员变量
IdGenerator* IdGenerator::m_IdGenerator = NULL;int IdGenerator::getID() {return id ++;
}IdGenerator* IdGenerator::getInstance() {if (m_IdGenerator == nullptr) { // 第一重检查std::lock_guard<std::mutex> lock(g_lock);if (m_IdGenerator == nullptr) { // 第二重检查m_IdGenerator = new IdGenerator(); }}return m_IdGenerator;
}void IdGenerator::deleteInstance() {if (m_IdGenerator) {delete m_IdGenerator;m_IdGenerator = nullptr;}
}IdGenerator::IdGenerator() : id(0) {}
IdGenerator::~IdGenerator() {}

3.4 饿汉模式单例

#include <iostream>
#include <pthread.h>using namespace std;class IdGenerator {
public:// 获取单例对象 static IdGenerator* getInstance();// 释放单例,进程退出时调用static void deleteInstance();// 获取唯一IDint getID();
private:IdGenerator();~IdGenerator();// 将拷贝构造函数设为私有,禁止外部拷贝IdGenerator(const IdGenerator& rhs);const IdGenerator& operator=(const IdGenerator& rhs);// 唯一单例对象指针static IdGenerator* m_IdGenerator;// 全局唯一IDint id;
};// 初始化静态成员变量
IdGenerator* IdGenerator::m_IdGenerator = new IdGenerator();int IdGenerator::getID() {return id ++;
}IdGenerator* IdGenerator::getInstance() {return m_IdGenerator;
}void IdGenerator::deleteInstance() {if (m_IdGenerator) {delete m_IdGenerator;m_IdGenerator = nullptr;}
}IdGenerator::IdGenerator() : id(0) {}
IdGenerator::~IdGenerator() {}

参考:

https://blog.csdn.net/fly910905/article/details/79286680

相关文章:

全局ID生成方式

全局ID生成方式 目录 1. 全局唯一id介绍 1.1 特点 2. 常见的全局唯一id生成策略 2.1 利用数据库自增字段生成id2.2 UUID2.3 Redis生成id2.4 zookeeper生成ID2.5 Twitter的snowflake算法 3. 面试题目&#xff1a;实现一个全局的ID生成器&#xff0c;注意线程安全 3.1 单例模式…...

c++之指针

总结性质 我们如何在一个函数中获取数组的长度&#xff1a; 我们都知道&#xff0c;在main函数中我们获得数组的长度只需要使用sizeof&#xff08;a&#xff09;/sizeof&#xff08;a【0】&#xff09;即可获得&#xff0c;但当我们把一个数组传入到方法时&#xff0c;c默认把…...

JVM 访问对象的两种方式

Java 程序会通过栈上的 reference 数据来操作堆上的具体对象。由于 reference 类型在《Java 虚拟机规范》里面只规定了它是一个指向对象的引用&#xff0c;并没有定义这个引用应该通过什么方式去定位、访问到堆中对象的具体位置&#xff0c;所以对象访问方式也是由虚拟机实现而…...

yo!这里是Linux基础开发工具介绍

目录 前言 基础开发工具 yum vim 1.基本介绍 2.基本操作 3.正常模式常用命令 4.底行模式常用命令 gcc/g gdb 1.基本介绍 2.常用操作 make/Makefile 1.背景 2.介绍 3.使用 git 1.介绍 2.操作 进度条程序简单实现 后记 前言 在学完初步的基础指令及权限控…...

本地组策略编辑器找不到怎么解决?| 解决windows home 版本隐藏本地组策略编辑器的问题 | 简单的介绍本地组策略编辑器

一般的 Windows 非家庭系统中&#xff0c;本地组策略编辑器不会被隐藏&#xff0c;但在某些特定情况下&#xff0c;可能会受到限制或不可用。如果你无法访问本地组策略编辑器&#xff0c;并且认为应该可以访问&#xff0c;请确保你拥有管理员权限&#xff0c;并检查是否有任何系…...

将Spring boot 项目部署到tomcat服务艰难

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z X Y Z...

第十二章 ObjectScript - 命令

文章目录 第十二章 ObjectScript - 命令命令熟悉的命令用于多维数组的命令 第十二章 ObjectScript - 命令 命令 本节概述了在 ObjectScript 常用命令。其中包括与其他语言中的命令相似的命令&#xff0c;以及其他语言中没有等效项的其他命令。 命令名称不区分大小写&#xf…...

在 CentOS 7 / RHEL 7 上安装 OpenSSL 1.1.x

OpenSSL 是一个开源软件库&#xff0c;由用于实现传输层安全 (TLS) 和安全套接字层 (SSL) 协议以及其他加密功能&#xff08;例如签名、加密、解密和验证&#xff09;的工具和库组成。操作系统和许多应用程序使用 OpenSSL 通过互联网提供安全通信。 CentOS 7 / RHEL 7 操作系统…...

论文阅读_模型结构_LoRA

name_en: LoRA: Low-Rank Adaptation of Large Language Models name_ch: LORA&#xff1a;大语言模型的低阶自适应 paper_addr: http://arxiv.org/abs/2106.09685 date_read: 2023-08-17 date_publish: 2021-10-16 tags: [‘深度学习’,‘大模型’] author: Edward J. Hu cita…...

uniapp获取 pdf文件流 并展示

1、流数据 uni.request({ url: this.$config.apiUrl“/api/report/content/fill?codebv.mf.refund.pay.voucher&busiNo00201323051500148949”, header: { ‘content-type’: ‘application/json;charsetutf-8’, ‘X-App-Code’: ‘weixin’, ‘X-Source’: ‘program’,…...

Linux(进程间通信)

目录 一、通信概念 二、进程间通信机制 1、管道 1.1 匿名管道&#xff08;Anonymous Pipe&#xff09; 1.2 命名管道&#xff08;Named Pipe&#xff09; 2、信号量 2.1 概念 2.2 API详解 2.3 使用示例 3、消息队列 3.1 概念 3.2 API函数 3.3 应用代码 4、共享内…...

Go的Gorm数据库操作错误WHERE conditions required

这是我在写这个代码处出现的问题 result : db.Save(&emergency) 这个错误是由于在提交保存数据时&#xff0c;GORM 需要指定 WHERE 条件&#xff0c;确保能够正确执行数据库操作。要解决这个问题&#xff0c;可以尝试使用 Create 方法替换 Save 方法&#xff0c;同时将创…...

基于java swing和mysql实现的仓库商品管理系统(源码+数据库+运行指导视频)

一、项目简介 本项目是一套基于java swing和mysql实现的仓库商品管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经…...

6、css学习6(表格)

1、指定CSS表格边框&#xff0c;使用border属性。 2、表格双边框是因为th/td有各自独立的边框。 3、boder-collapse设置表格边框是否被折叠成一个单一的边框。 4、width和height属性定义表格的宽度和高度。 5、text-align属性设置水平对齐方式。 6、vertic-align属性设置垂…...

Ceph源码解析:PG peering

集群中的设备异常(异常OSD的添加删除操作)&#xff0c;会导致PG的各个副本间出现数据的不一致现象&#xff0c;这时就需要进行数据的恢复&#xff0c;让所有的副本都达到一致的状态。 一、OSD的故障和处理办法&#xff1a; 1. OSD的故障种类&#xff1a; 故障A&#xff1a;一…...

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题

之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、...

对建造者模式理解

当对象成员变量太多时&#xff0c;使用建造方法给变量赋值往往变得很臃肿&#xff0c;所以可以这样做 public class Something {private String a;private String b;private String c;private String d;private String e;public Something(Builder builder) {this.a builder.…...

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介…...

静态链接库和动态链接库的区别

C静态链接库&#xff08;Static Linking&#xff09;和动态链接库&#xff08;Dynamic Linking&#xff09;的主要区别在于代码的组织和加载方式。 静态链接库 在编译时将库的代码和应用程序的代码合并在一起&#xff0c;生成一个单独的可执行文件。执行文件独立包含所需的库…...

使用 python 源码搭建 conda 环境

今天需要使用 python 2.6.8 的环境&#xff0c;发现 conda 设置成清华源后&#xff0c;没有旧版本了。所以打算从官网上下载一份 python 进行安装&#xff0c; 结果发现&#xff0c;conda 不能直接安装离线包&#xff08;也可能我没找到方法&#xff09;&#xff0c;经过一番尝…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...