一个简单的短ID生成器

2,212

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

为什么需要ID生成器?

在大多数开发过程中,实体类的属性中都有一个唯一标识用来获取这条数据的详情信息,无规律且唯一。在满足获取详情的基础上,需要可以保证这个ID在现在互联网中的分布式开发可以保证唯一,所以需要一个特有的ID生成器。

有哪些ID生成器?

UUID

通用唯一识别码(Universally Unique Identifier,简称UUID)是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。 UUID的目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。 UUID是由一组32位数的16进制数字所构成,是故UUID理论上的总数为1632=2128,约等于3.4 x 1038。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字元。示例:

0b0324d0-1fc1-4331-8142-756598cf16f4

数据库自增ID

基于数据库的auto_increment自增ID完全可以充当分布式ID,具体实现:需要一个单独的MySQL实例用来生成ID,实现简单,ID单调自增。通过集群模式设置不同步长可解决DB单点问题。

雪花算法

雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,开源后广受国内大厂的好评,在该算法影响下各大公司相继开发出各具特色的分布式生成器。

Snowflake生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。 Snowflake ID组成结构:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 数据中心(占5比特)+ 自增值(占12比特),总共64比特组成的一个Long类型。

  • 第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
  • 时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
  • 工作机器id(10bit):也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以。
  • 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID

自定义实现短ID

以上三种已有的实现不适用,需要生成一个短ID。下面是使用随机数和引用hashIds生成短ID的步骤

引入依赖

maven引入hashids依赖

<dependency>
   <groupId>org.hashids</groupId>
   <artifactId>hashids</artifactId>
   <version>1.0.3</version>
</dependency>

工具类实现

通过随机数加盐,并且使用Hashids生成短ID

@Slf4j
public class CnCodeUtil {

   private static Random rand =new Random();
   private CnCodeUtil(){}

   /**
    * 获取唯一码
    * @return
    */
   public static String code() {
      final String SALT =  this.getRandomString(8);
      final int MIN_HASH_LENGTH = 11;
      Hashids hashids = new Hashids(SALT, MIN_HASH_LENGTH);
      return hashids.encode(347L);
   }



   /**
    * 生成随机字符串
    *
    * @param length 表示生成字符串的长度
    * @return
    */
   public static String getRandomString(int length) {
      String base = "0123456789";
      StringBuilder sb = new StringBuilder();
      for (int i = 0; i < length; i++) {
         int number = rand.nextInt(base.length());
         sb.append(base.charAt(number));
      }
      return sb.toString();
   }

小结

以上列举了几种已经有的,常用分布式ID实现方案,但我需要的是短ID,方便数据库存储,且保证唯一值。使用了hashIds结合随机数的方式实现,在实现过程中可以替换加盐为任一方式,保证随机性即可。