美文网首页
唯一ID的设计

唯一ID的设计

作者: 菜鸡前端 | 来源:发表于2023-01-08 23:01 被阅读0次

一、背景概述

在应用程序中,很多时候会用到全局唯一的id,比如用户ID,订单ID等等,因此在架构设计中,全局唯一的id生成服务作为公共基础服务是整个项目的基石之一。然而,设计一个满足不重复、高性能、高可用的分布式id服务并不容易:

  • 不重复:需要保证在整个服务的生命周期内不会出现重复。
  • 高性能:在满足自身业务需要的情况下,如果可以,尽量提供更高的性能,以利于业务的长期发展。
  • 高可用:因为此服务是整个业务的基础服务,或者以模块的形式作为基础能力提供给其他服务,因此其高可用性直接影响到整个业务的高可用性,因此在必须保证服务的高可用。

二、设计方案

2.1 UUID 方案

UUID是通用唯一识别码(Universally Unique Identifier)的缩写,基于当前时间、计数器(counter)和硬件标识(通常为网卡的MAC地址)等数据计算生成的,因此可以做到全局唯一。Java本身提供UUID类,因此使用UUID可以满足不重复、高性能、高可用三方面的要求。

  • 优点
    • 1)使用简单方便;
    • 2)基于jdk的能力,且生成过程不存在阻塞,天然满足了高性能和高可用的要求;
  • 缺点
    • 1)无序,如果业务中对id有顺序要求,如趋势递增,则无法满足;
    • 2)id为数字和英文字母组合,对于只需要纯数字的场景,则无法满足;
    • 3)长度为128bit,对长度有要求的场合也不适用;

因此,此方案非常适合对id无顺序、无长度和无纯数字要求的场景,比如在分布式应用场景中,为了排查问题方便,多个服务之间会使用一个唯一id串起来整个请求的链路,比如对于TCP长连接协议的服务端,需要一个连接id标识每个连接。

2.2 数据库自增

利用数据库的自增特性,以 MySQL 举例,利用给字段设置 auto_increment_increment 和auto_increment_offset 来保证ID自增,且全局唯一。

  • 优点
    • 1)使用简单;
    • 2)严格递增;
    • 3)纯数字;
    • 4)几乎任何业务都会使用数据库,因此等于未依赖第三方组件;
  • 缺点
    • 1)依赖数据库,生成过程存在阻塞操作,且数据库本身性能不高,因此性能较差;
    • 2)依赖数据库,因此高可用性需要数据库一起保证,需要做主从备份,异地容灾等;

因此,此方案仅适用对性能要求不高,且必须为纯数字递增的场景,如用户id。

2.3 类snowflake方案

snowflake是Twitter以划分命名空间来生成ID的一种算法,在实际应用中我们可以借鉴其算法模型生成满足自己要求的全局唯一id。
snowflake算法把时间戳,工作机器id,序列号组合在一起。总共64bit,设计如下:


image.pngimage.png
  • 63位:预留
  • 62-22位:时间戳。使用了41bit。
  • 21-12位:机器id。使用10位标识机器信息,如机房信息,节点id信息,进程号,端口号信息等。
  • 11-0位:自增数。使用12bit作为自增数,范围为0~4098

snowflake 只是提供了一种算法思路,因此呢,我们可以根据业务的实际情况来调整各段所占的bit数,比如机器id不需要使用 10 位,可以预留更多的位数给自增数使用。因此应该灵活变通。

  • 优点
    • 1)可以为纯数字,也可以使用16进制变为0~F组合;
    • 2)由于生成过程不存在任何阻塞,因此性能极高;
    • 3)不依赖第三方,与UUID一样,很好的满足了高可用性;
    • 4)时间戳在高位,因此整体处于趋势递增的;
  • 缺点
    • 1)需要业务自己开发。
    • 2)由于机器时间可以回拨,因此可能存在重复的风险 (特别是某些国家夏令时和冬令时切换)

因此,此方案适用于在UUID不能满足的场景下,可以优先考虑的方案,如订单号,聊天消息id等等。

2.4 redis自增操作方案

利用 redis 的原子自增操作(incr命令)生成一个自增的序列,由于redis本身为单线程工作模式(6.0版本支持多线程,但是目前还属于beta版本,预计2020年5月才正式发布),因此可以天然的保证不会存在重复,而且redis本身的高性能,可以满足性能要求。

  • 优点
    • 1)纯数字
    • 2)使用简单
    • 3)高性能
    • 4)严格递增
  • 缺点
    • 1)依赖redis,因此必须通过redis的主从保证服务的高可用性。
    • 2)因为redis是内存数据库,因此需要通过主从,持久化来保证服务宕机后,当前计数值不会丢失从而出现id重复。
    • 3)redis是单线程,在与其他业务共用的情况下,会由于其他业务执行高时间复杂度命令导致线程阻塞,从而影响此服务性能。
    • 4)即使使用持久化,当redis服务器的硬盘损坏,或者机房所在地出现自然灾害等,会导致数据丢失。

因此,此方案适用于生成QQ号这样纯数字,且递增,并对性能有较高要求的场景,但是依赖redis,需要考虑与其他业务共用redis存在的问题,比如持久化可能会影响redis性能。

2.5 类美团 Leaf方案

美团的 Leaf 方案使用本地缓存+MySQL实现高性能、高可用且全局唯一的纯数字id,在业务实现过程中可以借鉴其设计思路。
在第2种方案中,我们知道基于MySQL的方案最大问题是性能问题,因此美团Leaf进行了优化,应用服务到数据库取id的时候,每次不再是只取一个id,而是取一个范围的id,称之为步长,假如为1000,那么只有当这1000个值使用完后才会再去访问数据库,因此提升了性能。
数据库设计一张id_table的表,如下:
[图片上传失败...(image-6dafe0-1673276567734)]

  • biz_tag:业务标识
  • max_id:当前最大的id值
  • step:步长(每次应用程序取值范围,根据业务实际情况而定)
  • desc:描述信息
  • create_time:创建时间
  • update_time:更新时间

应用程序通过以下两条sql语句完成取值:

Begin
UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx
SELECT max_id, step FROM table WHERE biz_tag=xxx
Commit

由于应用程序是拿到一个步长范围内的id值,因此在应用程序中还需要进行id的分配,因此,应用程序需要通过锁/CAS的方式保证id分配时不因为并发而出现重复。所有对开发人员有较强的要求。为了避免每个开发人员/模块重复造轮子,因此可以将此服务独立出来。架构设计演变为如下:


image.pngimage.png

Leaf服务可以通过集群提升服务的性能和增加可用性。
此外美团还提供了双buffer思想,进一步提升服务性能,即在Leaf服务内,当第一个步长值快要使用完毕之前,异步去数据库取下一个步长的值,这样不会存在当Leaf的id值分配完了,再去数据库取值出现性能问题。
目前,美团的Leaf已经开源,在我的业务使用时,此方案并没有开源,因此我个人参照此思路实现了我们业务的Leaf服务,主要需要考虑的是并发问题导致的id分配重复,另外双buffer思想实现起来非常繁琐,如果对性能要求不是非常极致,可以不考虑,而是通过3节点的Leaf服务即可满足。

  • 优点:

1)纯数字
2)趋势递增
3)只依赖数据库,因为几乎所有服务都需要数据库,相当于未增加第三方依赖。
4)满足高性能和高可用的要求。

  • 缺点:
    • 1)应用服务需要自己实现id的分配,保证不会因为并发等问题出现重复。
    • 2)因为应用程序每次取的是一个范围值到应用程序中,当服务发布时,未使用完的数据会丢失。所以,不是严格连续的。

因此,适用的场景是对性能要求很高,且为趋势递增的纯数字场景,而且不能因为任何原因而丢失数据导致ID重复的场景,如用户的id号。

三、总结

以上提供了5种最常见和实用的唯一id生成方案,可以根据自己的业务实际情况进行选择,总体原则是选实现最简单的,只要能满足业务需要即可,越复杂的方案越容易出错。

相关文章

  • 全局唯一ID设计

    在分布式系统中,经常需要使用全局唯一ID查找对应的数据。产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对...

  • 全局唯一ID设计

    一、前言 这周网上的各种瓜真的是吃到肚子里都是水啊。按照惯例周末聊点轻松的,这次我们讲几种全局唯一ID。 平常一些...

  • 唯一单号

    1.调研 2. 设计原则 1.唯一性,一般冗余用户id或商户id,时间。保证唯一性2.安全性,避免直接使用用户id...

  • 【分布式】一种简单高效的分布式唯一ID生成方法

    1. 设计目标 ID唯一不重复 高性能 2. 架构图 Server要生成唯一ID,先从Mysql一次性申请若干个独...

  • html的属性id和name

    属性id id 属性规定 HTML 元素的唯一的 id。id 在 HTML 文档中必须是唯一的。 根据提供的唯一i...

  • MySQL之主键

    引用 数据表的主键选择 设计套路:Mysql主键的选择 数据库的唯一标示符(ID)的选择 MySQL 使用自增ID...

  • 3.【每日分享】谈谈常用的分布式ID的设计

    在分布式开发中,唯一主键的设计非常重要,我们今天来看看分布式ID的常用设计 首先我们先明确一下分布式ID的定义1....

  • MySQL分布式环境下生成全局自增有序ID(雪花算法Snowfl

    1 MySQL全局ID 1.1 前言 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠...

  • 全局唯一ID

    生成全局唯一ID 通过MySQL的自增主键,作为唯一id; 通过内存中变量AtomicLong的自增来得到唯一id...

  • 防止mq重复消费

    1. 利用全局唯一id 消息里放入全局唯一id,做完业务后查询id是否存在表里,不存在则将唯一id插入数据库单独...

网友评论

      本文标题:唯一ID的设计

      本文链接:https://www.haomeiwen.com/subject/gdkpcdtx.html