分布式唯一id生成策略

最近发现公司用的公共jar包里生成唯一主键的方法竟然用的是当前时间戳,这种方式有明显弊端,参考了网上各种生成唯一id的方式之后,做下总结。

一、数据库自增ID

使用mysql数据库的自增id,数据库的自增id的优点是非常明显的:第一是速度快,而且是按序自增,检索非常有利。第二是自增的id都是数字型,占用空间小,易于程序中排序。 第三是新增记录时可以不用指定id,不用担心主键重复问题。

当然,使用数据库自增ID也有缺点:第一是手动插入指定ID的记录时会比较麻烦。第二是当新老系统合并做数据迁移时,新旧系统都是数字型,会导致多个主键id冲突,或新旧系统主键不同是数字型就会导致修改主键数据类型。第三如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。

二、UUID

UUID保证对在同一时空中的所有机器都是唯一的。UUID由以下几部分的组合:
(1)当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
(2)时钟序列。
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。

java中提供了两个对应的方法:

randomUUID() ,适用于生成唯一订单号。 

nameUUIDFromBytes(byte[] n)会根据n产生唯一的uuid。只要有用户的唯一性信息。就能保证此用户的uuid的唯一性。

优点:性能非常高:本地生成,没有网络消耗。

缺点:第一是不易存储,UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。第二是信息不安全,基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

下面是UUID及生成8位UUID的java实例:

public class UUIDTest {

    public static String[] chars = new String[]
            {
                    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
                    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
            };

    public static void main(String[] args) {
        System.out.println(UUID.randomUUID().toString().replace("-", ""));
        System.out.println(getShortUuid());
    }

    public static String getShortUuid() {
        StringBuffer stringBuffer = new StringBuffer();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int strInteger = Integer.parseInt(str, 16);
            stringBuffer.append(chars[strInteger % 0x3E]);
        }

        return stringBuffer.toString();
    }
}复制代码

其中生成8位UUID的思路是:将32位UUID按每四位截取,将截取的每组字符串转换成10进制的数字,然后对0x3E(十进制为62)取余,根据取余得到的结果找到ASCII字符数组中(总共62个字符)的位置,将每组的结果拼接就得到一个8位的UUID字符串。

三、SnowFlake雪花算法

雪花ID生成的是一个64位的二进制正整数,然后转换成10进制的数。64位二进制数由如下部分组成:

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

 

1位标识部分,在java中由于long的最高位是符号位,正数是0,负数是1,一般生成的ID为正数,所以为0;

41位时间戳部分,这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间-固定的开始时间),这样可以使产生的ID从更小值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年;

10位节点部分,Twitter实现中使用前5位作为数据中心标识,后5位作为机器标识,可以部署1024个节点;

12位序列号部分,支持同一毫秒内同一个节点可以生成4096个ID;

优点

  • 简单高效,生成速度快。
  • 时间戳在高位,自增序列在低位,整个ID是趋势递增的,按照时间有序递增。
  • 灵活度高,可以根据业务需求,调整bit位的划分,满足不同的需求。

缺点

  • 依赖机器的时钟,如果服务器时钟回拨,会导致重复ID生成。
  • 在分布式环境上,每个服务器的时钟不可能完全同步,有时会出现不是全局递增的情况。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值