分佈式集羣中,如何生成唯一的id
1,使用uuid
uuid:通用唯一識別碼(Universally Unique Identifier),指在一臺機器上生成的數字,它保證對在同一時空中的所有機器都是唯一的。
UUID.randomUUID().toString();
優點: 1,隱藏規律
缺點: 1,string類型字符串做主鍵,浪費存儲和影響索引性能
2,
2,利用數據庫的主鍵自增
3,snowflake算法
package com.daojia.study.id;
/**
* @author xiachao
* @date 2019/8/30 17:46
*/
/**
* 技術基礎:機器數、真值、源碼、反碼、補碼、模運算、運算符
* [+1] = [00000001]原 = [00000001]反 = [00000001]補
* [-1] = [10000001]原 = [11111110]反 = [11111111]補
* 1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [1000 0010]原 = -2
* 1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
* 1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]補 + [1111 1111]補 = [0000 0000]補 = [0000 0000]原 = 0
* (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]補 + [1000 0001]補 = [1000 0000]補 = -128
* 每個id用一個64bit的正整數標示
* 第1個bit:0代表正整數
* 後41個bit:代表當前時間減twepoch
* 後10個bit:代表服務器標示
* 後12個bit:代表同一毫米內的不同id
*/
public class IdWorker {
// 集羣唯一表示
private final long clusterId;
// 服務器唯一標示,服務獲取
private final long workerId;
// 時間從2016-01-01開始
private static final long twepoch = 1451577600000L;
// 數據中心表示佔5位,可以支持32個數據中心
private static final int clusterIdBits = 5;
// 服務器標示佔5位,id生成器可以有32個機器
private static final int workerIdBits = 5;
// 每毫米內的id佔12位
private static final int sequenceBits = 12;
// 服務器標示左移位數
private static final int workerIdShift = sequenceBits;
// 數據中心左移位數
private static final int clusterIdShift = sequenceBits + workerIdBits;
// 時間戳左移位數
private static final int timestampLeftShift = sequenceBits + workerIdBits + clusterIdBits;
// 最大數據中心
private static final int maxDatacenterId = -1 ^ -1 << clusterIdBits;
// 服務器標示最大值
private static final int maxWorkerId = -1 ^ -1 << workerIdBits;
// 最大種子值
private static final int maxSeed = -1 ^ -1 << (workerIdBits + clusterIdBits);
// 序列號掩碼
private static final int sequenceMask = -1 ^ -1 << sequenceBits;
// 上一毫秒
private long lastTimestamp = -1L;
// 限制每毫米產生的數據大小不能超過最大數
private long lastTimestampSequenceSize = 0;
// 跨毫米遞增
private long sequence = -1L;
public IdWorker(int seed) {
super();
if (seed > maxSeed || seed < 0) {
throw new IllegalArgumentException(String.format("seed Id can't be greater than %d or less than 0", maxSeed));
}
this.clusterId = seed >> workerIdBits & maxDatacenterId;
this.workerId = seed & maxWorkerId;
}
/**
* 指定id初始化
*
* @param workerId
*/
public IdWorker(int clusterId, int workerId) {
super();
if (clusterId > maxDatacenterId || clusterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
this.clusterId = clusterId;
this.workerId = workerId;
}
/**
* 獲取一個id
*
* @return
*/
public synchronized long nextId() {
long timestamp = this.timeGen();
// 同一毫秒內取多個id
if (this.lastTimestamp == timestamp) {
this.lastTimestampSequenceSize = this.lastTimestampSequenceSize + 1 & sequenceMask;
// 一毫秒內取的id次數大於上限,等待下一毫秒
if (this.lastTimestampSequenceSize == 0) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
lastTimestampSequenceSize = 0;
}
this.sequence = this.sequence + 1 & sequenceMask;
// 運維可能會修改系統時間,如果重啓服務那就完蛋了!!!lastTimestamp做持久化性能會大打折扣
if (timestamp < this.lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
}
this.lastTimestamp = timestamp;
return timestamp - twepoch << timestampLeftShift | clusterId << clusterIdShift | workerId << workerIdShift | this.sequence;
}
/**
* 獲取id中的時間
*
* @param id
* @return
*/
public long decodeTime(long id) {
return (id >> timestampLeftShift) + twepoch;
}
/**
* 獲取種子
*
* @param id
* @return
*/
public int decodeSeed(long id) {
return (int) (id >> workerIdShift & maxSeed);
}
/**
* 獲取id中的數據中心
*
* @param id
* @return
*/
public int decodeDatacenterId(long id) {
return (int) (id >> clusterIdShift & maxDatacenterId);
}
/**
* 獲取id中的服務編號
*
* @param id
* @return
*/
public int decodeWorkerId(long id) {
return (int) (id >> workerIdShift & maxWorkerId);
}
/**
* 獲取id中的序列
*
* @param id
* @return
*/
public int decodeSequence(long id) {
return (int) (id & sequenceMask);
}
/**
* 等待下一個毫米數
*
* @param lastTimestamp
* @return
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
/**
* 當前時間的毫米數
*
* @return
*/
private long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
IdWorker idWorker = new IdWorker(0, 0);
long id = 116603368553578496L;
System.out.println("id=" + id);
System.out.println("timestamp = " + idWorker.decodeTime(id) + ";datacenterId = " + idWorker.decodeDatacenterId(id) + ";workerId = " + idWorker.decodeWorkerId(id) + ";sequence = " + idWorker.decodeSequence(id));
System.out.println(idWorker.nextId());
System.out.println(idWorker.nextId());System.out.println(idWorker.nextId());
System.out.println(idWorker.nextId());
}
}