全局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. 面试题目:实现一个全局的ID生成器,注意线程安全 3.1 单例模式…...

c++之指针
总结性质 我们如何在一个函数中获取数组的长度: 我们都知道,在main函数中我们获得数组的长度只需要使用sizeof(a)/sizeof(a【0】)即可获得,但当我们把一个数组传入到方法时,c默认把…...

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

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

本地组策略编辑器找不到怎么解决?| 解决windows home 版本隐藏本地组策略编辑器的问题 | 简单的介绍本地组策略编辑器
一般的 Windows 非家庭系统中,本地组策略编辑器不会被隐藏,但在某些特定情况下,可能会受到限制或不可用。如果你无法访问本地组策略编辑器,并且认为应该可以访问,请确保你拥有管理员权限,并检查是否有任何系…...

将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 常用命令。其中包括与其他语言中的命令相似的命令,以及其他语言中没有等效项的其他命令。 命令名称不区分大小写…...
在 CentOS 7 / RHEL 7 上安装 OpenSSL 1.1.x
OpenSSL 是一个开源软件库,由用于实现传输层安全 (TLS) 和安全套接字层 (SSL) 协议以及其他加密功能(例如签名、加密、解密和验证)的工具和库组成。操作系统和许多应用程序使用 OpenSSL 通过互联网提供安全通信。 CentOS 7 / RHEL 7 操作系统…...

论文阅读_模型结构_LoRA
name_en: LoRA: Low-Rank Adaptation of Large Language Models name_ch: LORA:大语言模型的低阶自适应 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 匿名管道(Anonymous Pipe) 1.2 命名管道(Named Pipe) 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) 这个错误是由于在提交保存数据时,GORM 需要指定 WHERE 条件,确保能够正确执行数据库操作。要解决这个问题,可以尝试使用 Create 方法替换 Save 方法,同时将创…...

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

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

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

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题
之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、...
对建造者模式理解
当对象成员变量太多时,使用建造方法给变量赋值往往变得很臃肿,所以可以这样做 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布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图)效果一览基本介…...
静态链接库和动态链接库的区别
C静态链接库(Static Linking)和动态链接库(Dynamic Linking)的主要区别在于代码的组织和加载方式。 静态链接库 在编译时将库的代码和应用程序的代码合并在一起,生成一个单独的可执行文件。执行文件独立包含所需的库…...
使用 python 源码搭建 conda 环境
今天需要使用 python 2.6.8 的环境,发现 conda 设置成清华源后,没有旧版本了。所以打算从官网上下载一份 python 进行安装, 结果发现,conda 不能直接安装离线包(也可能我没找到方法),经过一番尝…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...