分布式ID生成方式
1.前言
分布式id的来源主要是针对分布式的整体应用,在每秒可成品千万级别的数据。对于这样的大批量数据,必定需要分库分表,那么针对常用的自增主键(MySQL)或序列增长(Oracle),同一业务的分表,会存在相同的主键,对于数据处理来说,会显得异常麻烦。那就必须采用更有效的方式来解决这种问题,实现ID的全局唯一。
2.主键策略分类
2.1雪花算法(SnowFlake)
这种主键生成方式是目前各大中小型系统用的最多的。具体用法见雪花算法生成主键。生成的结果是19位long类型,有个缺点就是它目前只能使用70年。
2.2UUID算法
对于UUID应该都不陌生,生成方式如下:
String uuid = UUID.randomUUID().toString().replace("-", "");
其生成的是一个全局唯一的32位的字符串(通常会把其中的"-"替换掉)。例如 a902dd6953a04d7daeebff4f4ff70648 。
也就因为其长度达32位,生成的值也没有一定的规则,对于数据库来说,不仅会占用一定的空间,而且索引是随机分布的,会导致查询性能降低。故目前一般不使用这种方式,但UUID可用在请求作为每次请求的ID.
2.3基于Redis的原子性自增方式
Redis是单线程的,其内部命令都是原子性执行的,其有一种方式是可以快速实现元素的自增而又是全局唯一的,那就是incr命令。
incr seq_id
默认从0开始,每执行一次,元素都会增加1,同时会返回增加后的值。即使是redis集群,也会支持数据的共享。
但对于每秒千万级别的数据,每次都从reids中获取主键,对redis的压力非常大,很可能造成服务经常宕机。其实可以采用分段模式,也就是说,每次不从redis取一个,而是取多个(例如100个),其命令如下:
incrby user_id 100
默认从0开始,每执行一次,元素都会增加100(以最后指定的步长为准),同时会返回增加后的值。那么在应用中将返回的值加载到内存中使用,使用完后再去获取。那么应用在这里获取到的是最大值,如何指定最小值呢?也就是说从哪个值开始使用呢,实际和步长有关,最小值=当前值-步长。
2.4号段模式
号段模式和redis模式思想类似,在数据库专门设计一个表来存储各个业务主键的号段信息,每次从数据库批量获取自增ID(也就是号段范围)加载到内存中使用,这段id使用完后再去数据库获取。其表如下:
CREATE TABLE s_id_generator (
id int(10) NOT NULL auto_increment,
max_id bigint(20) NOT NULL COMMENT '当前最大id',
step int(20) NOT NULL COMMENT '号段步长',
biz_type int(20) NOT NULL COMMENT '业务类型',
version int(20) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
)
其中字段version 是一个乐观锁,每次都更新version,保证并发时数据的正确性。例如业务是1001的号段信息如下:

每次获取是都先更新号段信息,然后再查询:
update s_id_generator set max_id = max_id + step, version = version + 1 where version = # {version} and biz_type = # {bizType };
select max_id,version from s_id_generator order by version desc limit 1;
获取的号段范围是( max_id, max_id + step ]。
那么如何把号段信息存入内存呢?在java中常用的是ConcurrentHashMap,它是线程安全的,且可设置过期时间。
2.5百度(UidGenerator)
UidGenerator是百度技术团队开发的, 基于Snowflake算法的唯一ID生成器。github地址https://github.com/baidu/uid-generator。
接入SpringBoot的步骤如下:(默认已导入mybatis、mysql等依赖并配置数据库连接信息)
1)下载源码
2)使用命令行打jar包
mvn install -Dmaven.test.skip=true
指定打包时跳过测试,使用默认的版本号信息。
3)在项目中引入jar
把生产的jar包放到资源目录的lib目录中

在pom.xml引入
<dependency>
<groupId>com.baidu.fsg</groupId>
<artifactId>uid-generator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>system</scope>
<type>jar</type>
<systemPath>${project.basedir}/src/main/resources/lib/uid-generator-1.0.0-SNAPSHOT.jar</systemPath>
</dependency>
4)创建表
将源码中script/WORKER_NODE.sql拿到数据库执行。
5)将需要的文件复制到项目中
将源码中dao目录下的WorkerNodeDAO复制到项目的dao目录中,修改类上的注解为@Mapper(也可命名为WorkerNodeMapper)

将源码中resources/META-INF/mybatis/mapper目录下的WORKER_NODE.xml复制到项目的resource/mapper目录中,改名WorkerNodeMapper.xml并修改namespace

将源码中DisposableWorkerIdAssigner类复制到项目的config目录中,并把里面依赖的WorkerNodeDAO改为本地新增的WorkerNodeDAO,把此类注入Spring中

6)配置生成构造器
package com.zxh.bootdemo0705.config; import com.baidu.fsg.uid.impl.CachedUidGenerator;
import com.baidu.fsg.uid.impl.DefaultUidGenerator;
import com.baidu.fsg.uid.worker.WorkerIdAssigner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /***
* 构造器配置类
*/
@Configuration
public class CachedUidGeneratorConfig { /**
* 注入默认构造器
*
* @param disposableWorkerIdAssigner
* @return
*/
@Bean
public DefaultUidGenerator defaultUidGenerator(WorkerIdAssigner disposableWorkerIdAssigner) {
DefaultUidGenerator defaultUidGenerator = new DefaultUidGenerator();
defaultUidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);
/**
* 对于并发数要求不高、期望长期使用的应用, 可增加timeBits位数, 减少seqBits位数
* 如{"workerBits":23,"timeBits":31,"seqBits":9}, 可支持28个节点以整体并发量14400 UID/s的速度持续运行68年
*
* 对于节点重启频率频繁、期望长期使用的应用, 可增加workerBits和timeBits位数, 减少seqBits位数
* 如{"workerBits":27,"timeBits":30,"seqBits":6}, 可支持37个节点以整体并发量2400 UID/s的速度持续运行34年
*/
//timeBits,相对于时间基点"2016-05-20"的增量值,单位:秒,可使用的时间为2^timeBis 秒
defaultUidGenerator.setTimeBits(32);
// 机器id,最多可支持2^22约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃
defaultUidGenerator.setWorkerBits(22);
// 每秒下的并发序列,9 bits可支持每台服务器每秒512个并发。
defaultUidGenerator.setSeqBits(9);
//设置时间基点
defaultUidGenerator.setEpochStr("2020-01-01");
return defaultUidGenerator;
} /**
* 注入缓存构造器
*
* @param disposableWorkerIdAssigner
* @return
*/
@Bean
public CachedUidGenerator cachedUidGenerator(WorkerIdAssigner disposableWorkerIdAssigner) {
CachedUidGenerator cachedUidGenerator = new CachedUidGenerator();
cachedUidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);
cachedUidGenerator.setTimeBits(32);
cachedUidGenerator.setWorkerBits(22);
cachedUidGenerator.setSeqBits(9);
cachedUidGenerator.setEpochStr("2020-01-01");
//RingBuffer size扩容参数, 可提高UID生成的吞吐量
cachedUidGenerator.setBoostPower(3);
cachedUidGenerator.setScheduleInterval(60L);
return cachedUidGenerator;
}
}
通过上述可配置项来配置id的生成策略。
7)测试生成id
@Autowired
private UidGenerator cachedUidGenerator; @Test
public void test() {
Long uid = cachedUidGenerator.getUID();
System.out.println(uid);
}
生成的结果例如"35967776271113728",长度是17位。没生成一次,都会记录到上述表中

2.6美团(Leaf)
由美团公司研发,github地址https://github.com/Meituan-Dianping/Leaf。
支持号段模式和snowflake模式,通过配置进行开启或关闭。若使用号段模式,则需创建表记录号段信息;若使用snowflake模式,则开启即可使用。其官方文档提供了详细的接入说明,在此不再赘述。
2.7滴滴(Tinyid)
由滴滴开发,github地址https://github.com/didi/tinyid。
采用号段模式,和Leaf的实现原理类似。个人认为其官方文档写的过于粗略,在此略。
分布式ID生成方式的更多相关文章
- 一口气说出 9种 分布式ID生成方式,面试官有点懵了
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 本文作者:程序员内点事 原文链接:https://mp.weix ...
- 一口气说出9种分布式ID生成方式,面试官有点懵
一.为什么要用分布式ID? 在说分布式ID的具体实现之前,我们来简单分析一下为什么用分布式ID?分布式ID应该满足哪些特征? 1.1.什么是分布式ID? 拿MySQL数据库举个栗子:在我们业务数据量不 ...
- 分布式 ID 的 9 种生成方式
为什么要用分布式ID? 在说分布式ID的具体实现之前,我们来简单分析一下为什么用分布式ID?分布式ID应该满足哪些特征? 什么是分布式ID? 拿MySQL数据库举个栗子: 在我们业务数据量不大的时候, ...
- 9种分布式ID生成之 美团(Leaf)实战
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 更多优选 一口气说出 9种 分布式ID生成方式,面试官有点懵了 ...
- 面试总被问分布式ID怎么办? 滴滴(Tinyid)甩给他
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问 ...
- 不能错过的分布式ID生成器(Leaf ),好用的一批!
本文收录在个人博客:www.chengxy-nds.top,技术资料共享,同进步 不了解分布式ID的同学,先行去看<一口气说出 9种 分布式ID生成方式,面试官有点懵了>温习一下基础知识, ...
- 分布式系列-分布式ID
一.数据库自增(单实例) 1.方案描述 基于数据库自增ID(auto_increment)利用其来充当分布式ID.实现方式就是用一张表来充当ID生成器,当我们需要ID时,向表中插入一条记录返回主键ID ...
- 分布式ID方案有哪些以及各自的优劣势,我们当如何选择
作者介绍 段同海,就职于达达基础架构团队,主要参与达达分布式ID生成系统,日志采集系统等中间件研发工作. 背景 在分布式系统中,经常需要对大量的数据.消息.http请求等进行唯一标识,例如:在分布式系 ...
- 分布式Id教程
转自:https://baijiahao.baidu.com/s?id=1584913615817222458&wfr=spider&for=pc 一,题记 所有的业务系统,都有生成I ...
- Leaf:美团分布式ID生成服务开源
Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家.数学家莱布尼茨的一句话:“There are no two identical leaves in the world.”L ...
随机推荐
- java 省略 System 写法
简介 RT code import static java.lang.System.*; 可以直接使用 out.println("ddd");
- 使用AssemblyScript在360浏览器下报错 SyntaxError: Unexpected reserved word
背景 最近在一个项目中使用了AssemblyScript,它能将类似于TypeScript的代码编译为WebAssembly,在其他浏览器都能正常使用,然而在360浏览器上却会报错:SyntaxErr ...
- "firmwarepasswd": MacOS Firmware Password Management: CHECK and DELETE Macbook Pro Firmware Password from the command line.
Abaels-MacBook-Pro:~ abaelhe$ su Password: bash-3.2# firmwarepasswd -check Password Enabled: Yes bas ...
- SciTech-BigDataAIML-TensorFlow-Model模型的 建立与训练 与 Layer层的inputs/outputs参数可自适应训练建模(投入产出)
TensorFlow 模型建立与训练 TensorFlow 模型建立与训练 本章介绍如何使用 TensorFlow 快速搭建动态模型. 模型的构建: tf.keras.Model 和 tf.keras ...
- 进阶篇:3.1.1.5)DFM塑胶-注射模具和设备
本章目的:了解塑胶件的注射模具典型结构. 1.前言 本章只是了解章节,介绍了塑胶件注射模具典型的机构.浇口.顶出,目的是为了设计出更好的塑胶零件. 但并非是需要结构设计工程师一定能设计出模具,人的精力 ...
- Archlinux Gnome桌面下Codeblocks无法运行的解决方案之一
如题,之一是因为造成编译运行报错的原因之一 首先我们通过pacman或者yay装codeblocks是不会带xterm的(像debian系的就会),xterm是什么呢?就是Setting->En ...
- 登录当前用户存储token
1.假设后端API的地址是http://localhost:3000/api,可以创建一个js文件(api.js) import axios from 'axios'; const instance ...
- 牛客周赛 round105 (A-F)题解
牛客链接 A 思路 掌握 x^0=x 这个性质就可以秒了 题解 #include <bits/stdc++.h> using namespace std; const int N=1e5+ ...
- 【摸鱼办公神器】七仔的桌面工具超进化 -> 灵卡面板 v1.1.9
[摸鱼办公神器]七仔的桌面工具超进化 -> 灵卡面板 v1.1.9 前言 本工具的前身是[七仔的桌面工具] 上次推出时评价还不错,大家给出了很多意见,经过一年多的打磨,本工具完成了超进化,包含前 ...
- echarts折线图阴影发光效果
1.先上效果图 2.具体的option代码 option = { backgroundColor: '#035254', // color:'#ff8000', title: { text: '折线图 ...