public final class SnowflakeShardingKeyGenerator implements ShardingKeyGenerator {
public static final long EPOCH;
private static final long SEQUENCE_BITS = 12L;
private static final long WORKER_ID_BITS = 10L;
//mask的作用就是將sequence所代表的最后12位全部置為1影涉,方便后面做位于運(yùn)算
private static final long SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1;
private static final long WORKER_ID_LEFT_SHIFT_BITS = SEQUENCE_BITS;
private static final long TIMESTAMP_LEFT_SHIFT_BITS = WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;
private static final long WORKER_ID_MAX_VALUE = 1L << WORKER_ID_BITS;
private static final long WORKER_ID = 0;
private static final int MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS = 10;
@Setter
private static TimeService timeService = new TimeService();
@Getter
@Setter
private Properties properties = new Properties();
private byte sequenceOffset;
/**
* 毫秒內(nèi)自增序列(0~4095) 既最后的12個(gè)bit位莱褒,2^12-1
*/
private long sequence;
private long lastMilliseconds;
static {
Calendar calendar = Calendar.getInstance();
calendar.set(2016, Calendar.NOVEMBER, 1);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
EPOCH = calendar.getTimeInMillis();
}
@Override
public String getType() {
return "SNOWFLAKE";
}
@Override
public synchronized Comparable<?> generateKey() {
long currentMilliseconds = timeService.getCurrentMillis();
//處理時(shí)間回溯問題
if (waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {
currentMilliseconds = timeService.getCurrentMillis();
}
//如果是在同一毫秒中調(diào)用此方法提佣,
if (lastMilliseconds == currentMilliseconds) {
//這里是一個(gè)位運(yùn)算肚吏,因?yàn)閟equence最多只允許占用最低的12位方妖,一旦+1的步驟達(dá)到4096,
//達(dá)到了13位须喂,既2^13,通過和SEQUENCE_MASK做位與運(yùn)算,重置為0
if (0L == (sequence = (sequence + 1) & SEQUENCE_MASK)) {
//一旦重置為0后趁蕊,說明這一毫秒能生成的key已達(dá)到上限坞生,則進(jìn)入循環(huán)等待下一個(gè)毫秒。
// 因?yàn)閮H僅是一個(gè)毫秒的差距掷伙,所以沒有使用sleep是己,直接while循環(huán)節(jié)省cpu調(diào)用
currentMilliseconds = waitUntilNextTime(currentMilliseconds);
}
} else {
//如果不是在同一毫秒中并發(fā)調(diào)用此方法
vibrateSequenceOffset();
sequence = sequenceOffset;
}
lastMilliseconds = currentMilliseconds;
/**
* (currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) 時(shí)間毫秒數(shù)往右移動(dòng)22位,避開workId和sequence任柜,放到高位的41位
* (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) 將workId左移到隨后的10位上
* 最后是sequence占據(jù)低位的12位
*
* 最后用或運(yùn)算將三個(gè)部分組合到一個(gè)long中,返回最終的結(jié)果
*/
return ((currentMilliseconds - EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (getWorkerId() << WORKER_ID_LEFT_SHIFT_BITS) | sequence;
}
/**
* 如果當(dāng)前時(shí)間沒有回溯過卒废,直接返回false,不等待
* 如果當(dāng)前時(shí)間回溯過宙地,如果在允許的時(shí)間范圍內(nèi)摔认,就sleep,返回true宅粥,表示等待過
* 如果超過了允許的時(shí)間范圍参袱,直接拋出異常。
* @param currentMilliseconds
* @return
*/
@SneakyThrows
private boolean waitTolerateTimeDifferenceIfNeed(final long currentMilliseconds) {
if (lastMilliseconds <= currentMilliseconds) {
return false;
}
//回溯的時(shí)間毫秒數(shù)
long timeDifferenceMilliseconds = lastMilliseconds - currentMilliseconds;
Preconditions.checkState(timeDifferenceMilliseconds < getMaxTolerateTimeDifferenceMilliseconds(),
"Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", lastMilliseconds, currentMilliseconds);
Thread.sleep(timeDifferenceMilliseconds);
return true;
}
private long getWorkerId() {
long result = Long.valueOf(properties.getProperty("worker.id", String.valueOf(WORKER_ID)));
Preconditions.checkArgument(result >= 0L && result < WORKER_ID_MAX_VALUE);
return result;
}
private int getMaxTolerateTimeDifferenceMilliseconds() {
return Integer.valueOf(properties.getProperty("max.tolerate.time.difference.milliseconds", String.valueOf(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS)));
}
private long waitUntilNextTime(final long lastTime) {
long result = timeService.getCurrentMillis();
while (result <= lastTime) {
result = timeService.getCurrentMillis();
}
return result;
}
/**
* byte的初始值0000 0000
* ~取反后就是-1秽梅,二進(jìn)制1111 1111
* 和1做位于運(yùn)算,0000 0001抹蚀,可以發(fā)現(xiàn),甭管你是多少企垦,最后都只會(huì)留下最低位环壤,1或者0
* sequenceOffset最開始是0,結(jié)果就是1
* sequenceOffset變成1后钞诡,一通操作后結(jié)果就是0
*
* 所以這個(gè)sequenceOffset每次調(diào)用循環(huán)變成0郑现,1.
* 這個(gè)原理是為了讓在分布式環(huán)境中低QPS的時(shí)候湃崩,末尾不總是0,就不會(huì)全是偶數(shù)懂酱。造成切片堆積
* http://www.reibang.com/p/2428ac820055
*/
private void vibrateSequenceOffset() {
sequenceOffset = (byte) (~sequenceOffset & 1);
}
}
分布式ID生成器,snowFlake算法在sharding-jdbc中的實(shí)現(xiàn)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門震放,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人驼修,你說我怎么就攤上這事殿遂。” “怎么了乙各?”我有些...
- 文/不壞的土叔 我叫張陵墨礁,是天一觀的道長。 經(jīng)常有香客問我耳峦,道長恩静,這世上最難降的妖魔是什么? 我笑而不...
- 正文 為了忘掉前任蹲坷,我火速辦了婚禮驶乾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘循签。我一直安慰自己级乐,他們只是感情好,可當(dāng)我...
- 文/花漫 我一把揭開白布县匠。 她就那樣靜靜地躺著唇牧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪聚唐。 梳的紋絲不亂的頭發(fā)上丐重,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼豫领!你這毒婦竟也來了抡柿?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬榮一對(duì)情侶失蹤等恐,失蹤者是張志新(化名)和其女友劉穎洲劣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體课蔬,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡囱稽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了二跋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片战惊。...
- 正文 年R本政府宣布各拷,位于F島的核電站,受9級(jí)特大地震影響襟锐,放射性物質(zhì)發(fā)生泄漏撤逢。R本人自食惡果不足惜膛锭,卻給世界環(huán)境...
- 文/蒙蒙 一粮坞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧初狰,春花似錦莫杈、人聲如沸。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腥光,卻和暖如春关顷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背武福。 一陣腳步聲響...
- 正文 我出身青樓平痰,卻偏偏與公主長得像汞舱,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宗雇,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 本文在分布式自增序列的實(shí)現(xiàn)(一) ---分布式序號(hào)生成器基礎(chǔ)上成文昂芜,因此直接上解決辦法,省去問題的討論赔蒲。請(qǐng)先閱讀分...
- 本文在分布式自增序列的實(shí)現(xiàn)(一) ---分布式序號(hào)生成器基礎(chǔ)上成文泌神,因此直接上解決辦法,省去問題的討論嘹履。請(qǐng)先閱讀分...
- 人生就是一次次幸福的相聚砾嫉,夾雜著一次次傷感的別離幼苛。我不是在最好的時(shí)光遇見了你們,而是遇見了你們焕刮,我才有了...
- 這個(gè)故事講的是老師給馬蒂爾德和于連留的作業(yè)舶沿,是關(guān)于尋找一個(gè)在這座城市里發(fā)生的真實(shí)故事。 他們先去圖書管里尋找資料配并,...