- 全局唯一
- 高可用
UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。
java.util.UUID
四种生成策略
-
Time-based,基于时间计算
UUID uuid = Generators.timeBasedGenerator().generate();
- 通过UUID的高低位来进行构造
public UUID generate() { long rawTimestamp = this._timer.getTimestamp(); int clockHi = (int)(rawTimestamp >>> 32); int clockLo = (int)rawTimestamp; int midhi = clockHi << 16 | clockHi >>> 16; midhi &= -61441; midhi |= 4096; long midhiL = (long)midhi; midhiL = midhiL << 32 >>> 32; long l1 = (long)clockLo << 32 | midhiL; return new UUID(l1, this._uuidL2); }
-
DCE security,DCE安全的uuid
-
Name-based,基于名字的uuid
UUID uuid = UUID.nameUUIDFromBytes("huifer".getBytes()); System.out.println(uuid);
-
Randomly generated,随机数uuid生成策略
UUID uuid = UUID.randomUUID(); System.out.println(uuid.toString());
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.2.0</version>
</dependency>
- 本地生产没有I/O
- 无序性
- 128位二进制过长存储只能用string
- 不能体现有序
假设有3个数据库a、b、c,步长位数据库数量,a的起始id=0,b的起始id=1,c的起始id=2\
a,0,3,6...
b,1,4,7...
c,2,5,8...
id = 起始id+步长x操作次数
public static void main(String[] args) {
String[] strings = {"A", "B", "C"};
int step = strings.length;
int operationNumber = 10;
for (int i = 0; i < strings.length; i++) {
for (int j = 0; j < operationNumber; j++) {
String dbName = strings[i];
int id = i + step * j;
System.out.println(String.format("数据库%s,第%s次操作,id=%s", dbName, j, id));
}
}
}
通过数据库的自增长id来得到id
CREATE TABLE `tb_seqno` (
`a` varchar(1) NOT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_a` (`a`)
) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=utf8
实际使用中需要连续执行两条sql,一条插入语句一条获取最后id语句
REPLACE INTO tb_seqno(`a`) VALUE('a');
select last_insert_id();
Long collectionId = redis.opsForValue().increment(RedisKey.OTC_COLLECTION_ID_KEY, 1L);
Jedis jedis = new Jedis();
Long incr = jedis.incrBy("test-inrc", 1L);
Long incr1 = jedis.incr("test-inrc");
Long incr2 = jedis.incrBy("test-inrc", 1L);
Long incr3 = jedis.incr("test-inrc");
String s = jedis.get("test-inrc");
System.out.println(s);
41bit可以存储 (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
年(毫秒级别)
package com.huifer.idgen;
/**
* @author: wang
* @description:
*/
public class SnowFlake {
/**
* 开始时间戳
*/
public static final long START_STMP = 1546272000000L;
/**
* 序列号位数
*/
public static final long SEQUENCE_BIT = 12L;
/**
* 机器表示位数
*/
public static final long MACHINE_BIT = 5L;
/**
* 数据中心位数
*/
public static final long DATACENTER_BIT = 5L;
/**
* 序列号最大值
*/
public static final long MAX_SEQUENCE_NUM = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 机器最大值
*/
public static final long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
/**
* 数据中心最大值
*/
public static final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
/**
* 机器左移
*/
public static final long MAXCHINE_LEFT = SEQUENCE_BIT;
/**
* 数据中心左移
*/
public static final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
/**
* 时间戳左移
*/
public final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
/**
* 数据中心id
*/
private long dataCenterId;
/**
* 机器id
*/
private long machineId;
/**
* 序列号
*/
private long sequence = 0L;
/**
* 最后一次时间戳
*/
private long lastStmp = -1L;
public SnowFlake(long dataCenterId, long machineId) {
if (dataCenterId > MAX_DATACENTER_NUM || dataCenterId < 0) {
throw new IllegalArgumentException();
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException();
}
this.dataCenterId = dataCenterId;
this.machineId = machineId;
}
public synchronized long genId() {
long curStmp = System.currentTimeMillis();
if (curStmp < lastStmp) {
throw new RuntimeException();
}
if (curStmp == lastStmp) {
// 时间戳相同时序列号+1
sequence = (sequence + 1) & MAX_SEQUENCE_NUM;
if (sequence == 0L) {
curStmp = getNextMill();
}
} else {
sequence = 0L;
}
lastStmp = curStmp;
return (curStmp - START_STMP)
<< TIMESTMP_LEFT//时间戳
| dataCenterId << DATACENTER_LEFT//数据中心
| machineId << MAXCHINE_LEFT // 机器标识
| sequence//序列号
;
}
private long getNextMill() {
long mill = System.currentTimeMillis();
while (mill <= lastStmp) {
mill = System.currentTimeMillis();
}
return mill;
}
public static void main(String[] args) {
long dataCenterId = 2L;
long machineId = 3L;
long sequence = 4L;
SnowFlake snowFlake = new SnowFlake(dataCenterId, machineId);
for (int i = 0; i < (1<<30); i++) {
long genId = snowFlake.genId();
System.out.println(genId);
}
}
}
根据Snowflake
算法剔除的思路进行修改。拓展一个实体类,对每个特征值进行位移一定的位数转换成二进制进行返回得到一个id
/**
* 机器id
*/
private long machine;
/**
* 步长
*/
private long seq;
/**
* 时间戳
*/
private long time;
/**
* 生产方法
*/
private long genMethod;
/**
* 类型
*/
private long type;
/**
* 版本号
*/
private long version;