1、方案說明
1.1、本方案沒有使用時間戳,因爲加入時間戳後長度會比較長。時間戳的方案可以參考百度和美團的。
1.2、本方案只利用分段自增的特性,在虛擬機中自增,不依賴redis,mysql等中間件的自增屬性。
2、實現方案
2.1架構圖
2.2 流程說明
代碼中需要配置變量bit_type。
集羣服務啓動後,通過mysql的悲觀鎖(for update) 各個服務先後獲取段的max_id,並更新保存新的max_id。
服務把號段生成數組保存在虛擬機中。
採用雙號段,當前號段使用超過20%,則啓動線程去更新空閒的號段。
2.3 設計和實現
數據庫設計
算法
1、每個服務先後通過數據庫鎖和定義的步長計算自己的起始和結束序號。
2、把自增序列增長轉換成二進制(長度要大於10位)。
3、使用0到1023的隨機數,轉換爲二進制,從自增的第10位開始插入。
示例
java代碼
public void test001() {
Long startIndex = 460000L;
for(int i=0;i<800000;i++) {
Integer f = RandomUtils.nextInt(1024);
String r = full(Integer.toBinaryString(f));
String seqString = Long.toBinaryString(startIndex+i);
String seqFirString = seqString.substring(0, 10);
String seqSeString = seqString.substring(10);
String zString = seqFirString+r+seqSeString;
if(i%5123==0) {
System.out.println(seqFirString+"-"+r+"-"+seqSeString+"-"+Long.parseLong(zString, 2)+"-"+startIndex+i);
}
}
}
/**
* 填充位10位,防止少於10位
* @param src
* @return
*/
public String full(String src) {
int n = 10-src.length();
for(int i=0;i<n;i++) {
src="0"+src;
}
return src;
}
運行效果
1110000010-1011101111-011100000-471195360-4600000
1110001100-0101000100-011100011-476219619-4600005123
1110010110-0011001100-011100110-481401062-46000010246
1110100000-0100111011-011101001-486700777-46000015369
1110101010-1001010110-011101100-492088556-46000020492
1110110100-0100010101-011101111-497167087-46000025615
1110111110-0010101101-011110010-502356722-46000030738
1111001000-1111111100-011110101-508033269-46000035861
1111010010-0101001011-011111000-512923384-46000040984
1111011100-0100100010-011111011-518145275-46000046107
1111100110-0001011011-011111110-523286270-46000051230
1111110000-1101111101-100000001-528939777-46000056353
1111111010-0010010110-100000100-533802244-46000061476
//格式:前10位-10位隨機-剩餘位數-最後的id值-對應的自增id
問題:
1、對於qps比較高的應用step可以設置大些,這個可以通過監控更新頻率去設置。
2、本方案對數據庫依賴還是比較強,所以數據庫不能宕機很久。