当前位置: 首页 > 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;经过一番尝…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

MySQL中【正则表达式】用法

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

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...