標籤:ret 64bit count add 存在 需要 時間 star ued
本篇文章僅僅是起一個拋磚迎玉的作用,舉一個如何修改源碼的例子。文章的靈感來自 ZOOKEEPER-2784。
提一個問題先
之前的文章講過zxid的設計,我們先複習下:
zxid有64位,分成兩部分:
高32位是Leader的epoch:選舉時鐘,每次選出新的Leader,epoch累加1
低32位是在這輪epoch內的事務id:對於使用者的每一次更新操作叢集都會累加1。
這麼設計會存在什麼問題?
Zookeeper 的事務 ID 有可能會超過 32 位。
epoch增長非常慢,超過32位需要非常久的時間,幾乎可以忽略這個問題,但是事務 ID 似乎不行。我們來算下。
如果我們每秒鐘操作1000次 Zookeeper ,即 1k/s ops,那麼
2^32/(86400?1000) ≈ 49.7
49.7天后,事務 ID 就將溢出,那溢出會發生什麼,看代碼:
src/java/main/org/apache/zookeeper/server/quorum/Leader.java line1037
/** * create a proposal and send it out to all the members * * @param request * @return the proposal that is queued to send to all the members */ public Proposal propose(Request request) throws XidRolloverException { /** * Address the rollover issue. All lower 32bits set indicate a new leader * election. Force a re-election instead. See ZOOKEEPER-1277 */ if ((request.zxid & 0xffffffffffL) == 0xffffffffffL) { String msg = "zxid lower 32 bits have rolled over, forcing re-election, and therefore new epoch start"; shutdown(msg); throw new XidRolloverException(msg); }
從上面的代碼可以看到,
Zookeeper 的 Leader 節點會throw new XidRolloverException(msg) 強制進行 re-election重新選舉,
即服務會停止一段時間,在一些情境下,這種情況過於頻繁是不能容忍的,那我們來看看如何解決。
如何解決?
上面說了epoch增長速度慢到可以忽略它溢出的問題,那麼可以重新設計 ZXID,
設計成高 24 位用於 epoch,低 40 位用於 事務 ID 增長。
我們再來算一下:
2^40/(86400?1000) ≈ 12725.8 即 12725.8/365 ≈ 34.9 年
1k/s ops 的情況下, 34.9 年之後才會進行一次強制選舉。
設想不錯,可以解決我們的問題,那我們繼續。
還有一個擔心
從作業系統的底層來說,對於32位作業系統,單次操作能處理的最長長度為32bit,而long類型8位元組64bit,所以對long的讀寫都要兩條指令才能完成(即每次讀寫64bit中的32bit)。
為什麼說這個,因為也許有人會把這個和 ZXID 的設計聯想起來,上面的 ZOOKEEPER-2784裡面也提到了這個問題。
However, i thought the ZXID is long
type, reading and writing the long type (and double
type the same) in JVM, is divided into high 32bit and low 32bit part of the operation, and because the ZXID
variable is not modified with volatile
and is not boxed for the corresponding reference type (Long
/ Double
), so it belongs to [non-atomic operation]
我大概翻譯一下:
ZXID 是 long 類型,32 bit 的 JVM 在對 long 讀寫時(和 double 類型一樣),是分為高 32 位和 低 32 位兩部分進行操作的,由於 ZXID 變數沒有用 volatile 修飾,且也沒有裝箱為對應的參考型別(Long / Double),屬於非原子操作。
這位老哥擔心對 ZXID 重新設計時把高 32 位和 低 32 位改成高 24 位和 低 40 位,可能會存在並發的問題。
會不會有這個問題,我們先來看看源碼:
Iterator<Integer> iterator = servers.iterator(); long zxid = Long.valueOf(m.group(2)); int count = (int)zxid;// & 0xFFFFFFFFL; int epoch = (int)Long.rotateRight(zxid, 32);// >> 32;
注意這個& 0xFFFFFFFFL,實際上後面的代碼還有很多這種按位與的操作,就不貼出來了。
翻了這一塊的源碼就可以知道,這個擔心是多餘的,關於ZXID的所有操作都是位操作而不是“=”的賦值操作,它不會造成JVM層級的並發問題。
如何修改
接下來我們就用源碼中“位與”的方式,把 32 為改成 40 位。
即:zxid按位於(&)0xffffffffffL(40位)獲得zxid的後40位。
注意要把count之前的int類型改為long類型,因為int為32bit,long為64bit,此時count有40位所以換成long。
Iterator<Integer> iterator = servers.iterator(); long zxid = Long.valueOf(m.group(2)); // int count = (int)zxid;// & 0xFFFFFFFFL; // int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; long count = zxid & 0xffffffffffL; int epoch = (int)Long.rotateRight(zxid, 40);// >> 40;
後面還有多處類似的地方要修改,就不一一列出來了,有興趣的可以看這裡github
zookeeper篇到這裡就完結了,關於zookeeper大家還有什麼想知道的可以留言,我覺得有價值的話會再更新些新的文章。
推薦閱讀
大資料需要學什嗎?
大資料不就是寫SQL嗎?
zookeeper-如何修改源碼-《每日五分鐘搞定大資料》