㈠ 雪花演算法生成id重復的坑
它的時間判斷參數是一個成員變數,生命周期跟著當前類走。而調用的方法並不是個單例模式,所以每次新建一個對象,其內部判定的時間判斷參數都是獨立存在的,這樣的話在並行程序的過程中,是有可能生成相同的id的。原吵歲本懷疑是否是使用了java8的stream的原因。然而發現,人家默認就是串列流,要使用並行流是需要而外加方法的,所以和這個沒有關系。
解決方法,寫一個IdentifierGeneratorutil,既然DefaultIdentifierGenerator的Sequence不是單例,那麼我們就在外層做操作,把調用到的IdentifierGenerator變成單例。IdWorker這個類是嫌碰攜MyBatisPlus雪花演算法的實現,直接調用其方法獲取,它內部是單例實現的。ps(若沒有特殊需求,用官方提供的就好了)。雪花演算法的原始版本是scala版,用於生成分布式ID(純數字,時間順序),訂單編號等。最高位是符號位,始終為0,不可用。41位的時間序列,精確到毫秒級,41位的長度可以使用69年。時間位還有一個很重要的作用是可以根據時間進行排序。10位的機器標識,10位的長度最多支持部署1024個節點。12位的計數序列號,序列號即一系列的自增id,可以芹伏支持同一節點同一毫秒生成多個ID序號,12位的計數序列號支持每個節點每毫秒產生4096個ID序號。
㈡ 2022年雪花演算法的最大與最小值
最高1位固定值0。
雪花演算法,SnowFlake演算法,是Twitter開源的分布式id生成演算法。
其核心思想並鬧游就是:使用一彎則個64bit的long型的數字作為全局唯一id。最高1位固定值絕銷0,因為生成的id是正整數,如果是1就是負數了。
㈢ 雪花演算法與Mysql自增的優缺點
雪花演算法與Mysql自增的優缺點分別是:
雪花演算法優點是:
1、不會重復。
2、有序,不會造成空間浪費和胡亂插入影響性能。
3、生成很快特別是比UUid快得多。
4、相比UUid更小。
缺點是:時間回撥造成錯亂。
Mysql自增的優點是:
1、存儲空間小。
2、插入和查詢性能高。
缺點是:
1、int的范圍可能不夠大。
2、當要做數據遷移的時候,會很麻煩,主鍵容易沖突。
3、id自增,自身的業務增長情況很容易被別人掌握。
4、自增在高並發的情況下性能不好。
生成id的代碼是:
自增和UUid差異的原因是:mysql資料庫一般我們會採用支持事務的Innodb,在Innodb中,採用的是B+數索引。Innodb的存儲結構,是聚簇索引。對於聚簇索引順序主鍵和隨機主鍵的對效率的影響很大。
自增是順序主鍵存儲,查找和插入都很方便(插入會按順序插到前一個的後面),但UUid是無序的,通過計算獲得的hashcode也會是無序的(是按照hashcode選擇存儲位置)。
所以對於他的查找效率很低,而且因為他是無序的,他的插入有可能會插到前面的數據中,會造成很多其他的操作,很影響性能或者很多存儲空間因為沒有順序的存儲而被空缺浪費。
㈣ 如何保證資料庫集群中id的唯一性,假設每秒鍾並發20萬次
用雪花演算法的工具類,1秒內可以生成26萬不重復的值,資料庫的主鍵不要自增,手動設置
packageentity;
importjava.lang.management.ManagementFactory;
importjava.net.InetAddress;
importjava.net.NetworkInterface;
/**
*<p>名稱:IdWorker.java</p>
*<p>描述:分布式自增長ID</p>
*<pre>
*Twitter的SnowflakeJAVA實現方案
*</pre>
*核心代碼為其IdWorker這個類實現,其原理結構如下,我分別用一個0表示一位,用—分割開部分的作用:
*1||0------00000---00000---000000000000
*在上面的字元串中,第一位為未使用(實際上也可作為long的符號位),接下來的41位為毫秒級時間,
*然後5位datacenter標識位,5位機器ID(並不算標識符,實際是為線程標識),
*然後12位該毫秒內的當前毫秒內的計數,加起來剛好64位,為一個Long型。
*這樣的好處是,整體上按照時間自增排序,並且整個分布式系統內不會產生ID碰撞(由datacenter和機器ID作區分),
*並且效率較高,經測試,snowflake每秒能夠產生26萬ID左右,完全滿足需要。
*<p>
*64位ID(42(毫秒)+5(機器ID)+5(業務編碼)+12(重復累加))
*
*@authorPolim
*/
publicclassIdWorker{
//時間起始標記點,作為基準,一般取系統的最近時間(一旦確定不能變動)
privatefinalstaticlongtwepoch=1288834974657L;
//機器標識位數
=5L;
//數據中心標識位數
=5L;
//機器ID最大值
=-1L^(-1L<<workerIdBits);
//數據中心ID最大值
=-1L^(-1L<<datacenterIdBits);
//毫秒內自增位
=12L;
//機器ID偏左移12位
=sequenceBits;
//數據中心ID左移17位
=sequenceBits+workerIdBits;
//時間毫秒左移22位
=sequenceBits+workerIdBits+datacenterIdBits;
=-1L^(-1L<<sequenceBits);
/*上次生產id時間戳*/
=-1L;
//0,並發控制
privatelongsequence=0L;
privatefinallongworkerId;
//數據標識id部分
privatefinallongdatacenterId;
publicIdWorker(){
this.datacenterId=getDatacenterId(maxDatacenterId);
this.workerId=getMaxWorkerId(datacenterId,maxWorkerId);
}
/**
*@paramworkerId
*工作機器ID
*@paramdatacenterId
*序列號
*/
publicIdWorker(longworkerId,longdatacenterId){
if(workerId>maxWorkerId||workerId<0){
(String.format("workerIdcan'tbegreaterthan%dorlessthan0",maxWorkerId));
}
if(datacenterId>maxDatacenterId||datacenterId<0){
(String.format("datacenterIdcan'tbegreaterthan%dorlessthan0",maxDatacenterId));
}
this.workerId=workerId;
this.datacenterId=datacenterId;
}
/**
*獲取下一個ID
*
*@return
*/
publicsynchronizedlongnextId(){
longtimestamp=timeGen();
if(timestamp<lastTimestamp){
thrownewRuntimeException(String.format("Clockmovedbackwards.Refusingtogenerateidfor%dmilliseconds",lastTimestamp-timestamp));
}
if(lastTimestamp==timestamp){
//當前毫秒內,則+1
sequence=(sequence+1)&sequenceMask;
if(sequence==0){
//當前毫秒內計數滿了,則等待下一秒
timestamp=tilNextMillis(lastTimestamp);
}
}else{
sequence=0L;
}
lastTimestamp=timestamp;
//ID偏移組合生成最終的ID,並返回ID
longnextId=((timestamp-twepoch)<<timestampLeftShift)
|(datacenterId<<datacenterIdShift)
|(workerId<<workerIdShift)|sequence;
returnnextId;
}
privatelongtilNextMillis(finallonglastTimestamp){
longtimestamp=this.timeGen();
while(timestamp<=lastTimestamp){
timestamp=this.timeGen();
}
returntimestamp;
}
privatelongtimeGen(){
returnSystem.currentTimeMillis();
}
/**
*<p>
*獲取maxWorkerId
*</p>
*/
(longdatacenterId,longmaxWorkerId){
StringBuffermpid=newStringBuffer();
mpid.append(datacenterId);
Stringname=ManagementFactory.getRuntimeMXBean().getName();
if(!name.isEmpty()){
/*
*GETjvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
*MAC+PID的hashcode獲取16個低位
*/
return(mpid.toString().hashCode()&0xffff)%(maxWorkerId+1);
}
/**
*<p>
*數據標識id部分
*</p>
*/
(longmaxDatacenterId){
longid=0L;
try{
InetAddressip=InetAddress.getLocalHost();
NetworkInterfacenetwork=NetworkInterface.getByInetAddress(ip);
if(network==null){
id=1L;
}else{
byte[]mac=network.getHardwareAddress();
id=((0x000000FF&(long)mac[mac.length-1])
|(0x0000FF00&(((long)mac[mac.length-2])<<8)))>>6;
id=id%(maxDatacenterId+1);
}
}catch(Exceptione){
System.out.println("getDatacenterId:"+e.getMessage());
}
returnid;
}
publicstaticvoidmain(String[]args){
//推特26萬個不重復的ID
IdWorkeridWorker=newIdWorker(0,0);
for(inti=0;i<2600;i++){
System.out.println(idWorker.nextId());
}
}
}
㈤ 雪花演算法之【線上訂單號重復了一招搞定它!】
公司老的系統原先採用的時間戳生成訂單號,導致了如下情形
打斷一下:大家知道怎麼查系統某項重復的數據吧
不得了,這樣重復豈不是一單成功三方回調導致另一單也成功了。
多個服務差桐怎麼保證生成的戚正訂單號唯一呢?
先上code
以上是採用snowflake演算法生成分布式唯一ID
41-bit的時間可以表示 (1L<<41)/(1000L360024*365)=69 年的時間,10-bit機器可以分別表示1024台機器。如果我們對IDC劃分有需求,還可以將10-bit分5-bit給IDC,分5-bit給工作機器。
這樣就可以表示32個IDC,每個IDC下可以有32台機器,可以根據自身需求定義。12個自增序列號可以表示 2^12 個ID,理論上snowflake方案的QPS約為 409.6w/s ,這種分配方式虛仔坦可以保證在任何一個IDC的任何一台機器在任意毫秒內生成的ID都是不同的。
這種方式的優缺點是:
優點:
缺點:
一般來說,採用這種方案就解決了。
還有諸如,mysql的 auto_increment策略,redis的INCR,zookeeper的單一節點修改版本號遞增,以及zookeeper的持久順序節點。