龙空技术网

担心Mysql面试官问你如何生成分布式全局唯一ID?四种够不够?

IT大派 862

前言:

今天我们对“mysql全局id”大约比较关注,姐妹们都想要分析一些“mysql全局id”的相关资讯。那么小编在网上收集了一些有关“mysql全局id””的相关资讯,希望你们能喜欢,各位老铁们一起来了解一下吧!

前言

在复杂的分布式系统中,需要对大量的数据和消息进行唯一的标识。比如美团的金融、支付、餐饮、酒店、猫眼电影等产品的系统中,数据日益增长,将数据库分表后需要一个唯一的ID来标示一条数据或消息,数据库的自增ID显然不能满足需求,特别是订单、骑手、优惠券也需要有唯一的ID才能识别。

此时,一个能够生成全局唯一ID的系统是非常必要的。

一、为什么要用分布式ID?

在讨论分布式ID的具体实现之前,我们先简要分析一下为什么要使用分布式ID?分布式ID应该满足哪些特征?

1、分布式ID是什么?

当我们的业务数据量不大时,一个数据库和一张表就可以完全支持现有的业务。对较多的数据采用MySQL主从同步读写分离的方案也可以应付。

但是,随着数据的不断增长,主从同步无法实现,因此有必要将数据库分库分表。但是分库分表后,需要一个唯一的ID来标识一条数据,而数据库的自增ID显然不能满足需求。特别点的例如订单和优惠券需要有一个唯一的ID来标示。此时,一个能够生成全局唯一ID的系统是非常必要的那么这个全局唯一ID就叫分布式ID。

2、分布式ID有什么要求?

2、 分布式ID的生成方法有哪些?

先看最简单的六种模式。

我们在这章节内容会讨论它们的实现方式,以及各种优缺点。

1、基于UUID

UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同),时钟序列,全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得),UUID的唯一缺陷在于生成的结果串会比较长。

看一下java版本如何获取UUID。

我们可以使用java下的UUID类,用它可以产生一个号称全球唯一的ID。

public static void main(String[] args) {

String uuid = UUID.randomUUID().toString().replaceAll("-","");

System.out.println(uuid);

}

输出结果 c2b8c2b9e46c47e3b30dca3b0d447718。

虽然使用简单但是缺点也很明显,比较占地方,和INT类型相比,影响插入速度, 并且造成硬盘使用率低存储一个UUID要花费更多的空间,使用UUID后,URL显得冗长,不够友好,像用作订单号UUID这样的字符串没有丝毫的意义,看不出和订单相关的有用信息。所以不推荐用做主键。

好处是出现数据拆分、合并存储的时候,能达到全局的唯一性,生成也比较简单。

2、基于数据库自增ID

基于数据库的auto_increment自动增量ID可以作为分布式ID,具体实现:需要单独的MySQL实例生成ID,表结构如下:

当我们需要一个ID时,在表中插入一条记录以返回主键ID。但是暴露出的缺点还是蛮大的,当访问量急剧增加时,MySQL容易达到系统的瓶颈。不建议使用它来实现分布式服务!

优点:

数据库自动编号,速度快,而且是增量增长,按顺序存放,对于检索非常有利;。

缺点:

DB单点存在宕机风险。很难处理分布式存储的数据表,数据量特别大时,会导致查询数据库操作变慢。

3、基于数据库集群模式

前边说了单点数据库方式不可取,那对上边的方式做一些高可用优化,换成主从模式集群。害怕一个主节点挂掉没法用,那就做双主模式集群,也就是两个Mysql实例都能单独的生产自增ID。

那这样还会有个问题,两个MySQL实例的自增ID都从1开始,会生成重复的ID怎么办?

解决方案:设置起始值和自增步长

两个MySQL实例的生成的自增ID为:

1、3、5、7、9

2、4、6、8、10

如果使用集群后的性能仍然顶不住高并发怎么办?那么就要对Mysql的节点进行扩容。

从上图可以看出,水平扩展的数据库集群有利于解决数据库单点压力问题。同时,对于ID生成特性,根据机器数量设置自动递增步长。

要增加第三台MySQL实例时,需要手动修改第一台和第二台的MySQL实例的起始值和步长,把第三台机器的ID起始生成位置设定在比现有最大自增ID的位置远一些,但必须在一、二两台MySQL实例ID还没有增长到第三台MySQL实例的起始ID值的时候,否则自增ID就要出现重复了,必要时可能还需要停机修改。

4,雪花模式

基于twitter snowflake算法。

雪花模式基本思路是,将一个64位的long分割为3部分,使用时间递增的特性,来生成唯一的值。

其中timestamp为当前毫秒数减去某个固定值(当前固定值取2018.01.01时的毫秒值),workerId占10位,因此可以容纳最多1024个服务实例同时使用。 sequence占12位,是单毫秒内递增值,也就是1毫秒内可以最多4096个值,意味着可以容纳的单个服务实例的最大QPS为4096000。

雪花模式非常依赖服务器时间,所以它的最大问题/隐患在于时间回拨,如果服务器时间回拨,那么就会生成重复的ID。 比如闰秒时的时间回拨,或者由于某些原因管理员修改了系统时间。 虽然在程序中判断了时间需要递增,但是如果在服务重启过程中发生了时间回拨,则无法避免产生相同的ID。

如果服务对错误容忍度高且希望简单方便,推荐使用雪花模式。

其实除了这4种外还有其它好用的中间件能为我们生成分布式全局ID。感兴趣的可以在网上了解下其它的方法。

标签: #mysql全局id