雪花算法

在分布式的环境下,数据的唯一id生成策略一般采用雪花算法。


为什么要使用🧐

我们平成生成数据的id一般的策略是数据库自增,但是在分布式的环境下(甚至只是做了分库分表),就会出现id字段重复的情况,此时我们就要抛弃自增的id生成形式

这时,我们一般有两种方案:

  • UUID
  • 雪花算法

为什么不使用UUID

为什么不使用UUID呢?🤔

我们来看UUID的特点:

  • 32位字符+4位“-”组成
  • 不递增

在我们数据库存储过程中,第一点会造成存储占用问题(太长),第二点会造成插入时索引树分裂问题。这些特点导致不推荐使用UUID。

雪花算法是什么

SnowFlake算法使用Twitter开源的分布式ID生成算法。它的核心思想:使用一个64位long型数字作为全局的唯一id,这对比UUID来说短了很多。

原理

SnowFlake算法的64位long型数字长这样:

摘自阿星不是程序员

解释

  • 第一位无意义,固定位0
  • 第二部分的41位是当前的时间戳,精确到毫秒级,代表这一毫秒生成的id
  • 第二部分的10位代表生成这个id的工作机器,这部分一般也会被拆成两个部分,前5位为datacenterID(机房ID),后5位为workerID(机器ID)
  • 第四部分的12位是同一时间戳下的递增的序列号(segment)。

这些可以保证雪花算法在时间尺度上,随着时间单调递增。

当然,这四部分并不是固定这样的,你可以根据你自己的业务需求进行调整。

比如美团的Leaf算法就调整了segment,并从zookeeper获取workerID、百度UidGenerator改动了时间戳(只精确到秒)并且workerID采用用后即弃(一次重启就递增一次并入库)

问题

采用雪花算法还是有可能出现一些问题,我们遇到的主要问题就是出现了重复id

机器时间回拨

其中时间戳常理来说是递增的,但是这个其实并不是可靠的,因为机器有可能会出现时间回拨的情况,为此我们需要对时间戳进行校验,及与上次生成id的时间戳比较,出现回拨问题时可以直接抛出异常,或者严谨一点可以等待几毫秒后重新比对,若还有问题再抛出异常。

但是如果出现大范围的回拨,我们其实可以采用简单粗暴的方式:人工调整😊

当然也可以采用调整算法的方式:时钟序列

我们可以取时间戳后的几位作为时钟序列(要入库存储),每次出现回拨的情况就递增。以三位为例子,我们就可以抵挡8次的时间回拨。

机器码不可靠

在k8s的场景下,如果我们使用了MybatisPlus的雪花生成算法就有可能出现id重复的问题,因此在k8s的环境下最好使用其他形式的雪花算法,MybatisPlus的雪花算法可以作为备选项。

MybatisPlus的机器码同样分为两个部分,datacenterID和wokerID,其中datacenterID取机器的mac地址,wokerID取进程的PID