系统唯一ID是设计一个系统的时候常常会遇到的问题,也常常为这个问题而纠结。

生成ID的方法有很多,适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会有多个ID生成的策略。

0. 分布式ID要求

(1)全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求;

(2)粗略有序:如果在分布式环境中做到完全有序,需要用到锁等,考虑到性能,采用粗略有序,具体分为秒级有序和毫秒级有序;

(3)可反解:即生成ID服务提供反解方法,这样在存储时就能以十进制存储,省下传统timestamp类字段的占用空间了;

(4)可伸缩:中心发布模式时可以进行集群部署,这样在生成ID里就必须包含机器ID;

(5)趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-Tree的数据结构来存储索引数据,在主键的选择上我们应该尽量使用有序的主键保证写入性能;

(6)单调递增:保证下一个ID一定大于上一个ID,例如事务版本号,IM增量信息,排序特殊需求;

(7)信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易,直接按照顺序下载指定URL即可,如果是订单号就更危险了

1. 数据库自增长列或字段

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

优点:

(1)最常见的方式,利用数据库,全数据库唯一,简单,代码方便,性能可以接受

(2)数字ID天然排序,对分页或需要排序的结果很有帮助

缺点:

(1)不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理

(2)在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成,有单点故障的风险

(3)在性能达不到要求的情况下,比较难于扩展

(4)如果遇到多个系统需要合并或者涉及到数据迁移会相当痛苦

(5)分表分库的时候有麻烦

2. UUID

优点:

(1)常见的方式,可以利用数据库也可以利用程序生成,一般来说全球唯一,简单,代码方便

(2)生成ID性能非常好,基本不会有性能问题

(3)全球唯一,在遇见数据迁移,系统数据合并或数据库变更等情况下,可以从容应对

缺点:

(1)没有排序,无法保证趋势递增

(2)UUID往往是使用字符串存储,查询的效率比较低

(3)存储空间比较大,如果是海量数据库,就需要存储量的问题

(4)传输数据量大

(5)不可读

3. Redis生成ID

当使用数据库来生成ID性能不够要求的时候,可以尝试用Redis来生成ID。

4. Twitter的snowflake算法

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:

使用41bit作为毫秒数

10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID)

12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID)

最后还有一个符号位,永远是0。

https://github.com/twitter/snowflake

优点:

(1)不依赖数据库,灵活方便,且性能优于数据库

(2)ID按照时间在单机上是递增的

缺点:

(1)在单机上是递增的,但是由于涉及到分布式环境中,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

5. 利用zookeeper生成唯一ID

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

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

6. MongoDB的ObjectId

MongoDB的ObjectId和snowflake算法类似。它涉及成轻量级,不同的机器都能用全局唯一的同种方法方便地生成它。MongoDB从一开始就设计用来作为分布式数据库,处理多个节点是一个核心要求,使其在分布式环境中要容易生成得多。

前4个字节是从标准纪元开始的时间戳,单位是秒。时间戳和随后的5个字节组合起来,提供了秒级别的唯一性。由于时间戳在前,意味着ObjectId大致会按照插入的顺序排列。这对于某些方面很有用,如将其作为索引提高效率。这4个字节也隐含了文档创建时间。绝大多数客户端类库都会公开一个方法从ObjectId获取这个信息。

接下来3字节是所在主机的唯一标识符。通常是主机名的散列值,这样就可以确保不同主机生成不同的ObjectId,不产生冲突。

接下来2个字节来自产生ObjectId的进程标识符(PID),为了确保在同一台机器上并发的多个进程产生的ObjectId是唯一的。

前9字节保证了同一秒中不同机器不同进程产生的ObjectId是唯一的,后3字节就是一个自动增加的计数器,确保相同进程同一秒产生的ObjectId也是不一样的。同一秒钟最多允许每个进程拥有16777216个不同的ObjectId。

7. 单点批量ID生成服务

分布式系统之所以难,很重要的原因之一是“没有一个全局时钟,难以保证绝对的时序”,要想保证绝对的时序,还是只能使用单点服务,用本地时钟保证“绝对时序”。数据库写压力大,是因为每次生成ID都访问了数据库,可以使用批量的方式降低数据库写压力。

数据库中只存储当前ID的最大值,例如0。ID生成服务假设每次批量拉取6个ID,服务访问数据库,将当前ID的最大值修改为5,这样应用访问ID生成服务索要ID,ID生成服务不需要每次访问数据库,就能依次派发0,1,2,3,4,5这些ID了,当ID发完后,再将ID的最大值修改为11,就能再次派发6,7,8,9,10,11这些ID了,于是数据库的压力就降低到原来的1/6了。

优点

(1)保证了ID生成的绝对递增有序

(2)大大的降低了数据库的压力,ID生成可以做到每秒生成几万几十万个

缺点

(1)服务仍然是单点

(2)如果服务挂了,服务重启起来之后,继续生成ID可能会不连续,中间出现空洞(服务内存是保存着0,1,2,3,4,5,数据库中max-id是5,分配到3时,服务重启了,下次会从6开始分配,4和5就成了空洞,不过这个问题也不大)

(3)虽然每秒可以生成几万几十万个ID,但毕竟还是有性能上限,无法进行水平扩展

改进方法

单点服务的常用高可用优化方案是“备用服务”,也叫“影子服务”,所以我们能用以下方法优化上述缺点(1)。对外提供的服务是主服务,有一个影子服务时刻处于备用状态,当主服务挂了的时候影子服务顶上。这个切换的过程对调用方是透明的,可以自动完成,常用的技术是vip+keepalived,具体就不在这里展开。

参考资料:

https://www.cnblogs.com/haoxinyue/p/5208136.html

https://tech.meituan.com/MT_Leaf.html

分布式ID生成方案的更多相关文章

  1. 一种基于Orleans的分布式Id生成方案

    基于Orleans的分布式Id生成方案,因Orleans的单实例.单线程模型,让这种实现变的简单,贴出一种实现,欢迎大家提出意见 public interface ISequenceNoGenerat ...

  2. 搞懂分布式技术12:分布式ID生成方案

    搞懂分布式技术12:分布式ID生成方案 ## 转自: 58沈剑 架构师之路 2017-06-25 一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-i ...

  3. 分布式id生成方案总结

    本文已经收录自 JavaGuide (60k+ Star[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.) 本文授权转载自:https://juejin.im/post/ ...

  4. 分布式ID生成方案总结整理

    目录 1.为什么需要分布式ID? 2.业务系统对分布式ID有什么要求? 3.分布式ID生成方案 3.1 UUID 3.2.数据库自增 3.3.号段模式 3.4. Redis实现 3.4. 雪花算法(S ...

  5. 分布式ID生成方案汇总

    1.目标 1.1.全局唯一 不能出现重复的ID,全局唯一是最基本的要求. 1.2.趋势有序 业务上分页查询需求,排序需求,如果ID直接有序,则不必建立更多的索引,增加查询条件. 而且Mysql Inn ...

  6. 分布式ID详解(5种分布式ID生成方案)

    分布式架构会涉及到分布式全局唯一ID的生成,今天我就来详解分布式全局唯一ID,以及分布式全局唯一ID的实现方案@mikechen 什么是分布式系统唯一ID 在复杂分布式系统中,往往需要对大量的数据和消 ...

  7. 分布式唯一ID生成方案是什么样的?(转)

    一.前言 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表.因为数据量巨大一张表无法承接,就会对其进行分库分表. 但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题, ...

  8. 一线大厂的分布式唯一ID生成方案是什么样的?

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  9. 分库分表的 9种分布式主键ID 生成方案,挺全乎的

    <sharding-jdbc 分库分表的 4种分片策略> 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为 ...

随机推荐

  1. There is no Action mapped for namespace [/] and action name [Login] associated with context path [/e

    近期学习web开发时,就遇到这个令人头疼的问题. 百度谷歌了N遍,最终在博客http://blog.csdn.net/liu578182160/article/details/17266879中找到了 ...

  2. 怎样用git提交多次改动

    在提交完代码后,我们发现所改动的文件还有须要完好的地方,可是我们已经upload过了可是还未合入到库上,此时要提交新的改动有两种做法: 一是等上次的改动合入到库上后,再次upload提交一次,这明显是 ...

  3. 使用openURL实现程序间带参数跳转详解

    使用openURL实现程序间带参数跳转详解 实现的效果:有两款应用A与B,A打开B A --> B 1. 新建工程B,bundle ID为com.YouXianMing.B 建立一个URL 这么 ...

  4. Android RadioButton设置选中时文字和背景颜色同时改变

    主要应用在购物车,像淘宝的那样,点击以后弹出一个选择种类颜色这样的popuwindow以后,然后这个选择种类的地方要用到类似这个玩意儿. 搜了一下,效果和这个文章一致.转了. 原文地址:http:// ...

  5. easyui datagrid 表格适应屏幕

    1.项目后台系统使用easyui,datagrid 的数据设置为自动适应屏幕,那么对于笔记本的话,就会显得有的小,可以通过设置datagrid属性,进行固定长度的设置 $('#gridTable'). ...

  6. SpringMVC之HandlerMethodArgumentResolver和<mvc:argument-resolvers>

    SpringMVC提供了一个HandlerMethodArgumentResolver接口可以让我们处理方法的参数,和注解结合提来,能有很强大的功能,例如SpringMVC提供的@ModelAttri ...

  7. Orchard运用 - 理解App_Data目录结构

    了解一个系统,应该基本上要了解目录结构及其组织形式.这样对于开发人员更是必备的知识,比如开发模块最终安装到哪,主题Themes是如何配置启用. 今天跟大家分享其实是个笔记记录,就是看到有一篇文章介绍A ...

  8. JavaScript 你不知道的事 -- 关于函数

    接上篇Javascript 你不知道的事,直接条列了: 每个函数创建时默认带有一个prototype属性,其中包含一个constructor属性,和一个指向Object对象的隐藏属性__proto__ ...

  9. C#的四个基本技巧

    1.如果可能尽量使用接口来编程 .NET框架包括类和接口,在编写程序的时候,你可能知道正在用.NET的哪个类.然而,在这种情况下如果你用.NET支持的接口而不是它的类来编程时,代码会变得更加稳定.可用 ...

  10. MySQL服务器安装完之后如何调节性能

    原文作者: Peter Zaitsev原文来源: http://www.mysqlperformanceblog.com/2006/09/29/what-to-tune-in-mysql-server ...