Tinyid原理介绍

2023-11-10

Id生成系统要点

 在简单系统中,我们常常使用db的id自增方式来标识和保存数据,随着系统的复杂,数据的增多,分库分表成为了常见的方案,db自增已无法满足要求。这时候全局唯一的id生成系统就派上了用场。当然这只是id生成其中的一种应用场景。那么id生成系统有哪些要求呢?

  1. 全局唯一的id:无论怎样都不能重复,这是最基本的要求了
  2. 高性能:基础服务尽可能耗时少,如果能够本地生成最好
  3. 高可用:虽说很难实现100%的可用性,但是也要无限接近于100%的可用性
  4. 简单易用: 能够拿来即用,接入方便,同时在系统设计和实现上要尽可能的简单

Tinyid的实现原理

 我们先来看一下最常见的id生成方式,db的auto_increment,相信大家都非常熟悉,我也见过一些同学在实战中使用这种方案来获取一个id,这个方案的优点是简单,缺点是每次只能向db获取一个id,性能比较差,对db访问比较频繁,db的压力会比较大。那么是不是可以对这种方案优化一下呢,可否一次向db获取一批id呢?答案当然是可以的。
 一批id,我们可以看成是一个id范围,例如(1000,2000],这个1000到2000也可以称为一个"号段",我们一次向db申请一个号段,加载到内存中,然后采用自增的方式来生成id,这个号段用完后,再次向db申请一个新的号段,这样对db的压力就减轻了很多,同时内存中直接生成id,性能则提高了很多。那么保存db号段的表该怎设计呢?

DB号段算法描述

id start_id end_id
1 1000 2000

 如上表,我们很容易想到的是db直接存储一个范围(start_id,end_id],当这批id使用完毕后,我们做一次update操作,update start_id=2000(end_id), end_id=3000(end_id+1000),update成功了,则说明获取到了下一个id范围。仔细想想,实际上start_id并没有起什么作用,新的号段总是(end_id,end_id+1000]。所以这里我们更改一下,db设计应该是这样的

id biz_type max_id step version
1 1000 2000 1000 0
  • 这里我们增加了biz_type,这个代表业务类型,不同的业务的id隔离
  • max_id则是上面的end_id了,代表当前最大的可用id
  • step代表号段的长度,可以根据每个业务的qps来设置一个合理的长度
  • version是一个乐观锁,每次更新都加上version,能够保证并发更新的正确性

 那么我们可以通过如下几个步骤来获取一个可用的号段,

  • A.查询当前的max_id信息:select id, biz_type, max_id, step, version from tiny_id_info where biz_type='test';
  • B.计算新的max_id: new_max_id = max_id + step
  • C.更新DB中的max_id:update tiny_id_info set max_id=#{new_max_id} , verison=version+1 where id=#{id} and max_id=#{max_id} and version=#{version}
  • D.如果更新成功,则可用号段获取成功,新的可用号段为(max_id, new_max_id]
  • E.如果更新失败,则号段可能被其他线程获取,回到步骤A,进行重试

号段生成方案的简单架构

 如上我们已经完成了号段生成逻辑,那么我们的id生成服务架构可能是这样的

简单架构

 id生成系统向外提供http服务,请求经过我们的负载均衡router,到达其中一台tinyid-server,从事先加载好的号段中获取一个id,如果号段还没有加载,或者已经用完,则向db再申请一个新的可用号段,多台server之间因为号段生成算法的原子性,而保证每台server上的可用号段不重,从而使id生成不重。
 可以看到如果tinyid-server如果重启了,那么号段就作废了,会浪费一部分id;同时id也不会连续;每次请求可能会打到不同的机器上,id也不是单调递增的,而是趋势递增的,不过这对于大部分业务都是可接受的。

简单架构的问题

 到此一个简单的id生成系统就完成了,那么是否还存在问题呢?回想一下我们最开始的id生成系统要求,高性能、高可用、简单易用,在上面这套架构里,至少还存在以下问题:

  • 当id用完时需要访问db加载新的号段,db更新也可能存在version冲突,此时id生成耗时明显增加
  • db是一个单点,虽然db可以建设主从等高可用架构,但始终是一个单点
  • 使用http方式获取一个id,存在网络开销,性能和可用性都不太好

优化办法

双号段缓存

 对于号段用完需要访问db,我们很容易想到在号段用到一定程度的时候,就去异步加载下一个号段,保证内存中始终有可用号段,则可避免性能波动。

增加多db支持

 db只有一个master时,如果db不可用(down掉或者主从延迟比较大),则获取号段不可用。实际上我们可以支持多个db,比如2个db,A和B,我们获取号段可以随机从其中一台上获取。那么如果A,B都获取到了同一号段,我们怎么保证生成的id不重呢?tinyid是这么做的,让A只生成偶数id,B只生产奇数id,对应的db设计增加了两个字段,如下所示

id biz_type max_id step delta remainder version
1 1000 2000 1000 2 0 0

 delta代表id每次的增量,remainder代表余数,例如可以将A,B都delta都设置2,remainder分别设置为0,1则,A的号段只生成偶数号段,B是奇数号段。 通过delta和remainder两个字段我们可以根据使用方的需求灵活设计db个数,同时也可以为使用方提供只生产类似奇数的id序列。

增加tinyid-client

 使用http获取一个id,存在网络开销,是否可以本地生成id?为此我们提供了tinyid-client,我们可以向tinyid-server发送请求来获取可用号段,之后在本地构建双号段、id生成,如此id生成则变成纯本地操作,性能大大提升,因为本地有双号段缓存,则可以容忍tinyid-server一段时间的down掉,可用性也有了比较大的提升。

tinyid最终架构

 最终我们的架构可能是这样的

最终架构

  • tinyid提供http和tinyid-client两种方式接入
  • tinyid-server内部缓存两个号段
  • 号段基于db生成,具有原子性
  • db支持多个
  • tinyid-server内置easy-router选择db

总结

 好了,到此为止,tinyid的整体架构已经介绍完了。如果你感兴趣,可以看一下tinyid的相关代码,所有code都非常简单。Have fun!

其他说明

关于号段算法实现,tinyid参考了美团leaf,并对其做了扩展,增加了多db支持和tinyid-client,从而获得了更好的性能和可用性。

 

 

 

 

https://github.com/didi/tinyid/wiki/tinyid%E5%8E%9F%E7%90%86%E4%BB%8B%E7%BB%8D

https://github.com/didi/tinyid

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Tinyid原理介绍 的相关文章

  • Sharding-Proxy自定义分片策略(按年分库,按月、日、小时、分表)

    目录 版本说明 一 官方的说明 1 使用自定义分片算法 2 数据分片配置说明 二 正确的姿势 1 下载官方源码 2 配置启动sharding proxy 3 代码 配置文件 xff0c 结构说明 版本说明 组件版本备注Sharding Pr
  • Sharding-JDBC简介

    一般 xff0c 线上系统的业务量不是很大 xff0c 比如说单库的数据量在百万级别以下 xff0c 那么MySQL的单库即可完成任何增 删 改 查的业务操作 随着业务的发展 xff0c 单个DB中保存的数据量 xff08 用户 订单 计费
  • 【C/C++】获取计算机CPUID序列号

    1 GetGPUId h文件 pragma once include
  • oracle生成uuid函数

    sys guid
  • 客户问我,为啥把数据库清空后,添加数据id不是从1开始增长!

    在清空MySQL数据库中的表的记录后 重新插入数据时自增id不是从1开始 要想在插入数据时id从1开始 不能使用可视化工具手动删除 而要用的truncate语句 该语句的作用是清空表中的数据 包括自增主键id 代码如下 truncate t
  • SpringBoot集成ShardingJDBC系列【2】—— 基于yaml基本配置

    文章只负责讲解sharding的相关配置 springboot其他的配置自己解决 文章内容将分开发布 便于平时查阅 基于yaml基本配置 在application yml配置文件中对mybatis plus做简单的配置 这里不对Mybati
  • SpringBoot集成ShardingJDBC系列【1】—— 添加依赖

    文章只负责讲解sharding的相关配置 springboot其他的配置自己解决 文章内容将分开发布 便于平时查阅 添加依赖
  • Sharding-jdbc实现读写分离、分库分表

    一 简介 Sharding jdbc官网 http shardingsphere apache org 1 概述 a Sharding jdbc是一个开源的分布式的关系型数据库中间件 b Sharding jdbc是客户端代理模式 c 定位
  • discard long time none received connection. , jdbcUrl.......

    在druid中 日志输出报错discard long time none received connection jdbcUrl 的信息 但是对我们使用不会有影响 只是会影响性能 但作为强迫症的我 怎么会忍心看着这个ERROR呢 解决办法
  • Android中 @id 与 @+id 区别

    Android 中的组件需要用一个int 类型的值来表示 这个值也就是组件标签中的id 属性值 id 属性只能接受资源类型的值 也就是必须以 开头的值 例如 id abc id xyz等 如果在 后面使用 表示当修改完某个布局文件并保存后
  • 数据库分片和分区资源

    我正在使用遇到可扩展性问题的数据库模式 该架构中的一个表已增长到大约 1000 万行 我正在探索分片和分区选项 以允许该架构扩展到更大的数据集 例如 10 亿到 1000 亿行 我们的应用程序还必须可部署到多种数据库产品上 包括但不限于 O
  • Kubernetes 中的有状态服务的分片负载均衡

    我目前正在从 Service Fabric 切换到 Kubernetes 想知道如何进行自定义和更复杂的负载平衡 到目前为止 我已经了解到 Kubernetes 提供的 服务 可以为隐藏在其后面的 Pod 进行负载平衡 但这只能以更简单的方
  • MongoDB分片,添加新节点时如何重新平衡?

    我正在尝试了解 MongoDB 和分片的概念 如果我们从 2 个节点开始 并根据姓氏对客户数据进行分区 其中 A 到 M 数据存储在节点 1 上 N 到 Z 数据存储在节点 2 上 当我们想要横向扩展并添加更多节点时会发生什么 我只是不明白
  • cassandra 分片和复制

    我是 Cassandra 的新手 不过本文解释分片和复制 我陷入了一个困境 我的本地计算机上配置了一个包含 6 个 Cassandra 节点的集群 我创建一个新的键空间 TestKeySpace 复制因子为 6 并在键空间 employee
  • CPU 100%时如何提高Redis性能?分片?最快的.Net 客户端?

    由于我们网站上的负载大量增加 redis 现在正在努力应对峰值负载 因为 redis 服务器实例的 CPU 使用率达到 100 在八个核心之一上 导致超时 我们已将客户端软件更新至 ServiceStack V3 来自 BookSleeve
  • MySQL 分片方法?

    对 MySQL 表进行分片的最佳方法是什么 我能想到的方法是 应用程序级别分片 MySQL代理层的分片 用于分片的中央查找服务器 您知道该领域有哪些有趣的项目或工具吗 对 MySQL 表进行分片的最佳方法是不要这样做 除非完全不可避免 当您
  • 数据库分片的 MySQL 代理替代方案

    MySQL Proxy 有其他替代方案吗 我不想使用它 因为它仍处于 alpha 阶段 我将拥有 10 台 MySQL 服务器 其中 table 1 table 2 table 3 table 4 table 10 分布在这 10 台服务器
  • Solrcloud 多核配置

    我有一个独立的Solr具有 4 个不同内核的实例使用嵌入式 Jetty 服务器运行良好 我为 v4 10 3 配置了核心 但自从我迁移到 v5 1 后 一切似乎都工作正常 无需任何更改 在投入生产之前 我需要将其设置为Solrcloud 安
  • Spark Mongo 连接器,MongoShardedPartitioner 不起作用

    出于测试目的 我配置了一个 4 节点集群 每个节点都有一个 Spark Worker 和一个 MongoDB Shard 这些是详细信息 四台 Debian 9 服务器 名为 Visa0 Visa 1 Visa 2 Visa 4 个节点上的
  • 我如何了解有关网站分片用户数据的更多信息?

    我有兴趣在多个服务器上分割我的网站用户数据 例如 用户将从同一位置登录 但登录脚本需要弄清楚用户数据驻留在哪个服务器上 因此 登录脚本将在主注册表中查询该用户名 并且可能会返回该用户名位于服务器 B 上 然后 登录脚本将连接到服务器 B 并

随机推荐

  • 基于LSTM的负荷和可再生能源出力预测(核心部分复现)

    目录 1 主要内容 长短期记忆网络介绍 2 程序结果 3 下载链接 1 主要内容 该程序复现文章 基于改进鲸鱼优化算法的微网系统能量优化管理 负荷和可再生能源预测部分 根据长短期记忆网络 Long Short Term Memory LST
  • NASM与link、golink和alink具体例子使用对比

    一 OMF文件格式链接 使用import伪指令 import伪指令可以直接使用函数名 而不用给函数名加上 前缀和 number 后缀 但import伪指令仅适合于OMF borland obj 格式输出 OMF格式是MS在16位下操作系统的
  • vue awesome swiper 轮播图 循环不了 无法自动播放 loop无效 autoplay无效 蓝圈 解决办法

    vue awesome swiper 轮播图循环不了 无法自动播放 loop无效 autoplay无效 解决办法 出现问题 1 轮播图无法自动播放 2 swiper opagination为蓝色圈如何变成白色圈 3 loop无效 对应的解决
  • QT 一信号对应多个槽函数

    网络上搜索 大部分都废话连篇 直接上码测试此功能 结果显示OK 分别创建三个类 A B C 信号和槽绑定关系如下 一个信号绑定两个槽函数 A A QObject parent QObject parent B b new B C c new
  • CentOS7安装详细教程

    VM安装CentOS 7详细教程 通过VM安装CentOS7虚拟机的全部过程 并自动配置IP地址和DNS服务器 可以进行联网 1 软件准备 VM12 软件 安装包下载地址 云盘链接 VM12软件安装包下载地址 提取码 5lgm CentOS
  • 微信企业号,回调模式开通.net

    企业号每个应用有普通模式和回调模式两种 普通模式直接打开网页 回调模式可设置应用底部菜单项 可增加交互开发 可把客户端的操作事件传给企业服务器 企业服务器做响应开发 开通回调模式 首先需要通过url的回调验证 那么进入应用后台设置项 设置好
  • Vue2学习计划二:mustache与methods和computed等Vue实例参数

    上一节写了Vue实例的生命周期 我们心里有了个Vue里的数据绑定至DOM 那么具体怎么实现的呢 要实现只需要在Vue绑定的DOM元素中使用mustache语法即可 简单例子如下 div h2 message h2 hr h3 全名 full
  • (一)ideal 创建springboot工程和实现简单配置

    新建 IDEA project 选择Spring Initializr Choose Initializr Service URL 选择 Default Https start spring io 点击 next 进入下一步 提示 1 sp
  • Keil for arm 关于enit0 快速中断(FIQ)的响应

    本文原创 版权所有 如需转载 请注明出处 接着上篇讲arm7对于普通中断的响应 今天讲一下 关于快速中断的响应 步骤1 基础环境 arm7 LPC2106 Realview 4 2 编译环境默认 步骤2 starup s文件 启动代码 和i
  • adb shell 报错error: device unauthorized

    2022 7 29 oppo r11s 安卓8 亲测成功 windows电脑在链接安卓设备后 想要进行终端命令行进入到该设备 出现报错 报错内容 C Users gt adb shell error device unauthorized
  • 机械手使用者坐标系和工具坐标系_发那科机器人应用-坐标系介绍(2)

    工具坐标系 由工具中心点 的位置 和工具的姿势 构成 工具中心点 的位置 通过相对机械接口坐标系的工具中心点的坐标值 来定义 工具的姿势 通过机械接口坐标系的 轴 轴 轴周围的回转角 来定义 工具中心点用来对位置数据的位置进行示教 在进行工
  • Linux系列:Linux中如何安装.rpm、.tar、.tar.gz和tar.bz2文件

    我以下面三个包为例 三个包都在 etc opt下 A example 1 2 3 1 rpm B example 1 2 3 1 tar C example 1 2 3 1 tar gz 1 安装rpm包 说起RPM REDHAT Pack
  • 关于QtCreator 4.8 创建工程时,选中创建界面(.ui)无法创建工程问题

    在主界面一次选中 help gt about plugins 然后在弹出界面中找到QtCreator 在Designer右侧的的方框中打钩 然后close界面 重启软件 搞定 很简单的设置 浪费了好长时间
  • 芯片设计制造全过程

    芯片设计制造全过程 将一颗芯片从0到1 可以分为芯片设计和芯片制造两部分 芯片设计对应市场上一些fabless公司 这类公司只做芯片设计 而芯片制造对应的是foundary 比如国内的smic TSMC 国外的Samsung GlobalF
  • 分布式系统的接口幂等性设计

    什么是幂等性 就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的 不会因为多次点击而产生了其他的结果 在 CRUD 这 4 个操作中 查询操作是天然幂等的 删除操作也只会删除一次 多次的删除操作结果还是一样 影响幂等的只有在添加和
  • 服务器考试系统推荐,如何挑选并发性好的在线考试系统?

    原标题 如何挑选并发性好的在线考试系统 并发性是在线考试系统的核心指标之一 不同的在线考试系统之间可能功能上看似差异不大 但并发性不同就会使得使用体验悬若霄壤 如何挑选并发性好的在线考试系统呢 最好的办法还是通过具体的使用来进行判断 什么是
  • ConstraintLayout 使用详解

    在安卓开发过程中除了一些列的逻辑代码程序以外 UI界面也是很重要的组成部分 对于用户来说界面更是一个应用的所有 所有的操作和交互都是在UI发生 对于开发者来说UI的加载和绘制对应用的计算和内存的影响更是不容忽视 下面看一组界面 这个布局很简
  • 线程池使用和自定义线程池

    目录 1 线程基础概述 1 1 线程池的作用 1 2 为什么要用线程池 1 3 比较重要的几个类 1 4 new Thread的弊端 2 四种线程池 2 1 源码分析 2 2 RejectedExecutionHandler 线程池四种拒绝
  • subversion强制写log的windows 和linux hooks脚本

    windows code echo offsetlocalset REPOS 1set TXN 2rem check that logmessage contains at least 10 characterssvnlook log RE
  • Tinyid原理介绍

    Id生成系统要点 在简单系统中 我们常常使用db的id自增方式来标识和保存数据 随着系统的复杂 数据的增多 分库分表成为了常见的方案 db自增已无法满足要求 这时候全局唯一的id生成系统就派上了用场 当然这只是id生成其中的一种应用场景 那