分布式的惟一ID是一个系统设计过程当中常常遇到的问题。生成分布式ID的策略有多种,应用场景也不尽相同。下面简单介绍一下一些经常使用的分布式ID生成策略:java
经过定义数据库的自增ID进行实现。git
优势:github
缺点:redis
优化:算法
经过本地代码或者数据库生成。数据库
优势:架构
缺点:less
优化:dom
private static BigInteger getBigIntegerFromUuid() { UUID id = UUID.randomUUID(); ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(id.getMostSignificantBits()); bb.putLong(id.getLeastSignificantBits()); return new BigInteger(1, bb.array()); }
经过redis的incrby或者lua方式进行生成ID。分布式
Snowflake算法源自Twitter,主要应用于解决分布式的场景下的惟一ID。
优势:
缺点:
改进:
下面简单介绍一下Snowflake的基本原理。
snowflake使用64bit的存储空间,正好是一个长整数的存储空间。
时间前缀(42) + 机房标识(5) + 主机标识(5) + 顺序号(12)
时间前缀:
使用秒数记录。须要设置一个起始时间点(如:2016-01-01 00:00:00)。那么生成ID时,将当前的秒数-起始时间的秒数。年T = (1L << 42) / (1000L * 60 * 60 * 24 * 365) = 139.46,表示这个算法能够支持使用139.46年。
机房标识:
能够用于表示机房编号之类。按(1L << 5) = 32计算,能够支持32个机房。
主机标识:
能够用于表示主机编号之类。按(1L << 5) = 32计算,能够支持32个主机。综合机房标识和主机标识,一共能够支持使用1024台主机。
顺序号:
按(1L << 12) = 4096计算,每秒能够生成4096个ID。再综合机房、主机、顺序号,1024*4096约400万,即每秒能够生成400万个不重复的ID。
参考:https://github.com/twitter/snowflake
注意:能够根据实际状况对snowflake的不一样组成位数进行调整。
参考中的代码使用scala编写,下面是java版本的翻译:
package com.zheng.coderepo.snowflake; /** * Created by zhangchaozheng on 17-2-24. */ public class IdGen { private final long twepoch = 1288834974657L; private final long workerIdBits = 5L; private final long datacenterIdBits = 5L; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final long sequenceBits = 12L; private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); private long workerId; private long datacenterId; private long sequence = 0L; private long lastTimestamp = -1L; public IdGen(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen() { return System.currentTimeMillis(); } }