全域性唯一ID生成常見的幾種方式和twitter/snowflake(雪花演算法)解析
全域性唯一ID生成常見的幾種方式和twitter/snowflake(雪花演算法)解析
全域性唯一ID生成常見的幾種方式:
1,(twitter/snowflake)雪花演算法
2,利用資料庫的auto_increment特性
3,UUID
4,其他(如redis也有incr,redis加lua指令碼實現twitter/snowflake演算法)
一、 (twitter/snowflake)
使用了long型別,long型別為8位元組工64位。可表示的最大值位2^64-1(18446744073709551615,裝換成十進位制共20位的長度,這個是無符號的長整型的最大值)。
單常見使用的是long 不是usign long所以最大值為2^63-1(9223372036854775807,裝換成十進位制共19的長度,這個是long的長整型的最大值)
下面程式來自大象部落格:
http://www.blogjava.net/bolo/archive/2015/07/13/426200.html
public class IdGen {
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long twepoch = 1288834974657L;
//Thu, 04 Nov 2010 01:42:54 GMT
private long workerIdBits = 5L;
//節點ID長度
private long datacenterIdBits = 5L;
//資料中心ID長度
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
//最大支援機器節點數0~31,一共32個
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
//最大支援資料中心節點數0~31,一共32個
private long sequenceBits = 12L;
//序列號12位
private long workerIdShift = sequenceBits;
//機器節點左移12位
private long datacenterIdShift = sequenceBits + workerIdBits;
//資料中心節點左移17位
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
//時間毫秒數左移22位
private long sequenceMask = -1L ^ (-1L << sequenceBits);
//最大為4095
private long lastTimestamp = -1L;
private static class IdGenHolder {
private static final IdGen instance = new IdGen();
}
public static IdGen get(){
return IdGenHolder.instance;
}
public IdGen() {
this(0L, 0L);
}
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只有12bit,所以和sequenceMask相與一下,去掉高位
sequence = (sequence + 1) & sequenceMask;
//判斷是否溢位,也就是每毫秒內超過4095,當為4096時,與sequenceMask相與,sequence就等於0
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
//自旋等待到下一毫秒
}
} else {
sequence = 0L;
//如果和上次生成時間不同,重置sequence,就是下一毫秒開始,sequence計數重新從0開始累加
}
lastTimestamp = timestamp;
// 最後按照規則拼出ID。
// 000000000000000000000000000000000000000000 00000 00000 000000000000
// time datacenterId workerId sequence
// return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
// | (workerId << workerIdShift) | sequence;
long longStr= ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
// System.out.println(longStr);
return longStr;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
}
測試程式
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
public class GeneratorTest {
@Test
public void testIdGenerator() {
long avg = 0;
for (int k = 0; k < 10; k++) {
List<Callable<Long>> partitions = new ArrayList<Callable<Long>>();
final IdGen idGen = IdGen.get();
for (int i = 0; i < 1400000; i++) {
partitions.add(new Callable<Long>() {
@Override
public Long call() throws Exception {
return idGen.nextId();
}
});
}
ExecutorService executorPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
try {
long s = System.currentTimeMillis();
executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
long s_avg = System.currentTimeMillis() - s;
avg += s_avg;
System.out.println("完成時間需要: " + s_avg / 1.0e3 + "秒");
executorPool.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("平均完成時間需要: " + avg / 10 / 1.0e3 + "秒");
}
}
我們生產也是按照這個twitter/snowflake的演算法來寫的。
二、 利用auto_increment特性
insert into
replace into
三、 UUID
常見的方式。可以利用資料庫也可以利用程式生成,一般來說全球唯一。
優點:
1)簡單,程式碼方便。
2)生成ID效能非常好,基本不會有效能問題。
3)全球唯一,在遇見資料遷移,系統資料合併,或者資料庫變更等情況下,可以從容應對。
缺點:
1)沒有排序,無法保證趨勢遞增。
2)UUID往往是使用字串儲存,查詢的效率比較低。
3)儲存空間比較大,如果是海量資料庫,就需要考慮儲存量的問題。
4)傳輸資料量大
5)不可讀。
變種的UUID
1)為了解決UUID不可讀,可以使用UUID to Int64的方法。
2)為了解決UUID無序的問題,NHibernate在其主鍵生成方式中提供了Comb演算法(combined guid/timestamp)。保留GUID的10個位元組,用另6個位元組表示GUID生成的時間(DateTime)。
四、 其他
如:1,redis的incr 和INCRBY來實現可以實現自增。 2,redis-lua指令碼實現twitter/snowflake演算法。3,MongoDB的ObjectId。
相關文章
- Snowflake 全域性唯一Id 生成
- 分散式系統全域性唯一Id(SnowFlake)雪花演算法實現分散式演算法
- Java使用雪花演算法實現生成全域性唯一idJava演算法
- 分散式唯一ID生成方案選型!詳細解析雪花演算法Snowflake分散式演算法
- 忘掉 Snowflake,感受一下效能高出 587 倍的全域性唯一 ID 生成演算法演算法
- Twitter雪花演算法SnowFlake演算法的java實現演算法Java
- Twitter的分散式雪花演算法 SnowFlake 每秒自增生成26個萬個可排序的ID (Java版)分散式演算法排序Java
- 探索 PHP 如何生成全域性唯一的 idPHP
- 全域性唯一ID(GUID)生成方案對比GUI
- 分散式唯一ID的幾種生成方案分散式
- php生成唯一id的幾種解決方法PHP
- 分散式唯一id:snowflake演算法思考分散式演算法
- PHP 實現 Snowflake 生成分散式唯一 IDPHP分散式
- 分散式全域性唯一ID分散式
- 前後端常見的幾種鑑權方式後端
- 【開發經驗】幾種常見的加密方式加密
- 生成分散式唯一ID的幾種解決方案分散式
- 幾種常見的Vue元件間的傳參方式Vue元件
- Vue2 幾種常見開局方式Vue
- 幾種常見的JS遞迴演算法JS遞迴演算法
- 35.幾種常見的排序演算法排序演算法
- 幾種常見的排序演算法總結排序演算法
- 幾種常見的濾波演算法(轉)演算法
- Spring Boot 工程整合全域性唯一ID生成器 VestaSpring Boot
- 分散式 ID 生成演算法 — SnowFlake分散式演算法
- 幾種常見的延遲執行處理方式
- 幾種常見排序演算法總結排序演算法
- 分散式唯一ID解決方案-雪花演算法分散式演算法
- 解析xml的幾種方式XML
- Twitter的分散式自增ID演算法snowflake (Java版)分散式演算法Java
- 幾種常見網路抓包方式介紹
- Android生成ViewModel例項的幾種方式AndroidView
- 開源一個比雪花演算法更好用的ID生成演算法(雪花漂移)演算法
- 框架篇:分散式全域性唯一ID框架分散式
- 生成按時間增長的全域性唯一主鍵
- 幾種常見的NO SQL DBSQL
- 雪花演算法和UUID演算法UI
- Linux上安裝MySQL的幾種常見方式的過程和步驟LinuxMySql