分散式ID(CosId)之號段鏈模式效能(1.2億/s)解析
分散式ID(CosId)之號段鏈模式效能(1.2億/s)解析
上一篇文章《分散式ID生成器(CosId)設計與實現》我們已經簡單討論過CosId的設計與實現全貌。
但是有很多同學有一些疑問:CosId的號段鏈模式(SegmentChainId)效能有些難以置信(TPS峰值效能1.2億/s),甚至是不同效能級別號段分發器(RedisIdSegmentDistributor、JdbcIdSegmentDistributor)均能夠達到這樣的效能級別。
所以本篇文章將深度解析CosId的號段鏈模式(SegmentChainId)的設計思路與實現優化。
新同學最好先查閱上一篇文章《分散式ID生成器(CosId)設計與實現》
。
背景(為什麼需要SegmentChainId)
通過上一篇文章《分散式ID生成器(CosId)設計與實現》我們知道號段模式(SegmentId)主要有以下問題:
- 穩定性:SegmentId的穩定性問題主要是因為號段用完之後獲取ID的執行緒需要同步進行
NextMaxId
導致的(會產生網路IO)。 - 步長(Step):設定多大的步長是一個權衡問題,設定太小會影響整體效能,設定太大會導致全域性ID亂序的程度增加。
- 本機單調遞增、全域性趨勢遞增: 本地單調遞增是我們期望看到的,但是如何理解/衡量全域性趨勢遞增呢。下面我們來解釋一下什麼是全域性趨勢遞增:
從上圖的號段模式設計中我們可以看出:
- Instance 1
NextMaxId
,一定比上一次大,意味著下一次的號段一定比上一次大,即F(Tn+1)>F(Tn),所以從單例項上來看是單調遞增的。 - Instance 1、Instance 2例項各自持有的不同的號段,意味著在一個時間段內不同例項生成的ID是亂序的。但是多例項在獲取
NextMaxId
時是單調遞增的,所以整體趨勢的遞增,即全域性趨勢遞增。
全域性趨勢遞增反向說明的是ID在一個時間週期內是會亂序的,所以我們要儘可能讓ID的亂序程度降低。這是一個優化點。
號段模式生成的ID,在資料庫視角可以近似的理解為上面的趨勢遞增圖(Tn>Tn-s)。ID亂序的程度受到步長(Step)影響,步長越小ID亂序的程度越小。這裡我們使用邊界值(Step=1
- 當
Step=1
時,即每次都獲取NextMaxId
,將無限接近單調遞增(資料庫視角)。需要注意的是這裡是無限接近而非等於單調遞增,具體原因你可以思考一下這樣一個場景:- 號段分發器T1時刻給Instance 1分發了
ID=1
,T2時刻給Instance 2分發了ID=2
。因為機器效能、網路等原因,Instance 2
網路IO寫請求先於Instance 1
到達。那麼這個時候對於資料庫來說,ID依然是亂序的。
- 號段分發器T1時刻給Instance 1分發了
所以不難理解的是影響全域性ID亂序的因素有倆個:叢集規模、Step
大小。叢集規模是我們不能控制的,但是Step
是可以調節的。
所以SegmentChainId就是在這樣的背景下誕生的。
號段鏈模式(SegmentChainId)的優勢
通過SegmentChainId設計圖中我們可以看到,號段鏈模式新增了一個角色PrefetchWorker。
PrefetchWorker主要的職責是維護和保證號段鏈頭部到尾部的安全距離,也可以近似理解為緩衝距離。
有了安全距離的保障不難得出的結論是所有獲取ID的執行緒只要從程序記憶體的號段裡邊獲取下次ID即可,理想情況下不需要再進行NextMaxId
(向號段分發器請求NextMaxId
,網路IO)的,所以效能可以達到近似AtomicLong
的 TPS 效能:12743W+/s的級別。
SegmentChainId是SegmentId的增強版,相比於SegmentId有以下優勢:
- TPS效能:可達到近似
AtomicLong
的 TPS 效能:12743W+/s JMH 基準測試。通過引入了新的角色PrefetchWorker用以維護和保證安全距離,理想情況下使得獲取ID的執行緒幾乎完全不需要進行同步的等待NextMaxId
獲取。 - 穩定性:P9999=0.208(us/op),通過上面的TPS效能描述中我們可以看到,SegmentChainId消除了同步等待的問題,所以穩定性問題也因此迎刃而解。
- 適應性:從SegmentId介紹中我們知道了影響ID亂序的因素有倆個:叢集規模、
Step
大小。叢集規模是我們不能控制的,但是Step
是可以調節的。Step
應該儘可能小才能使得ID單調遞增的可能性增大。Step
太小會影響吞吐量,那麼我們如何合理設定Step
呢?答案是我們無法準確預估所有時點的吞吐量需求,那麼最好的辦法是吞吐量需求高時,Step自動增大,吞吐量低時Step自動收縮。- SegmentChainId引入了飢餓狀態的概念,PrefetchWorker會根據飢餓狀態檢測當前安全距離是否需要膨脹或者收縮,以便獲得吞吐量與有序性之間的權衡,這便是SegmentChainId的自適應性。
- 所以在使用SegmentChainId時我們可以配置一個比較小的
Step
步長,然後由PrefetchWorker根據吞吐量需求自動調節安全距離,來自動伸縮步長。
常見問題
RedisIdSegmentDistributor、JdbcIdSegmentDistributor 均能夠達到TPS=1.2億/s?
RedisChainIdBenchmark-Throughput
MySqlChainIdBenchmark-Throughput
上面的兩張圖給許多同學帶來了困擾,為什麼在Step=1000
的時候RedisChainIdBenchmark、MySqlChainIdBenchmarkTPS效能幾乎一致(TPS=1.2億/s)。
RedisIdSegmentDistributor應該要比JdbcIdSegmentDistributor效能更高才對啊,為什麼都能達到AtomicLong效能上限呢?
如果我說當Step=1
時,只要基準測試的時間夠長,那麼他們依然能夠達到AtomicLong效能級別(TPS=1.2億/s),你會不會更加困惑。
其實這裡的障眼法是PrefetchWorker的飢餓膨脹導致的,SegmentChainId的極限效能跟分發器的TPS效能沒有直接關係,因為最終都可以因飢餓膨脹到效能上限,只要給足夠的時間膨脹。
而為什麼在上圖的Step=1
時TPS差異還是很明顯的,這是因為RedisIdSegmentDistributor膨脹得更快,而基準測試又沒有給足測試時間而已。
SegmentChainId基準測試TPS極限效能可以近似使用以下的公式的表示:
TPS(SegmentChainId)極限值=(Step*Expansion)*TPS(IdSegmentDistributor)*T/s<=TPS(AtomicLong)
<=TPS(AtomicLong)
:因為SegmentChainId的內部號段就是使用的AtomicLong
,所以這是效能上限。Step*Expansion
:Expansion可以理解為飢餓膨脹係數,預設的飢餓膨脹係數是2。在MySqlChainIdBenchmark、MySqlChainIdBenchmark基準測試中這個值是一樣的。TPS(IdSegmentDistributor)
: 這是公式中唯一的不同。指的是請求號段分發器NextMaxId
的TPS。T
: 可以理解為基準測試執行時常。
從上面的公式中不難看出RedisChainIdBenchmark、MySqlChainIdBenchmark主要差異是分發器的TPS效能。
分發器的TPS(IdSegmentDistributor)
越大,達到TPS(AtomicLong)
所需的T
就越少。但只要T
足夠長,那麼任何分發器都可以達到近似TPS(AtomicLong)
。
這也就解釋了為什麼不同TPS效能級別的號段分發器(IdSegmentDistributor)都可以達到TPS=1.2億/s。
CosId需要部署服務端嗎?
CosId是以本地SDK的形式存在的,使用者只需要安裝一下CosId的依賴包做一些簡單配置(快速開始、DEMO)即可。
分散式ID是不適合使用服務端部署模式的(C/S)。使用服務端部署模式,必然會產生網路IO(Client通過遠端過程呼叫Server,獲取ID),你想想我們費了那麼大勁消除網路IO是為了什麼?
PrefetchWorker 是如何維護安全距離的?
- 定時維護:每隔一段時間PrefetchWorker會主動檢測安全距離是否滿足配置要求,如果不滿足則執行
NextMaxId
預取,保證安全距離。 - 被動飢餓喚醒:當獲取ID的執行緒獲取ID時沒有可用號段,會嘗試獲取新的號段,並主動喚醒PrefetchWorker並告訴他你太慢了,被喚醒的PrefetchWorker會檢測安全距離是否需要膨脹,然後進行安全距離的維護。
本機單調、全域性趨勢遞增-為什麼還要儘可能保證單調遞增?
從上文的論述中我們不難理解本機單調遞增,全域性趨勢遞增是權衡後的設計結果。
但是全域性趨勢遞增的背面是週期內ID亂序,所以儘可能向單調遞增優化(降低ID亂序程度)是優化目標,這倆點並不衝突。
如果各位同學還有其他問題請至 Issues 提交你的疑問。
作者:Ahoo Wang (阿虎)
Github: https://github.com/Ahoo-Wang/
SmartSql(高效能、高生產力,超輕量級的ORM!): https://github.com/Ahoo-Wang/SmartSql
SmartCode(不只是程式碼生成器!): https://github.com/Ahoo-Wang/SmartCode
CoSky 高效能、低成本微服務治理平臺 : https://github.com/Ahoo-Wang/CoSky
CosId 通用、靈活、高效能的分散式 ID 生成器 : https://github.com/Ahoo-Wang/CosId
Govern EventBus 歷經多年生產環境驗證的事件驅動架構框架: https://github.com/Ahoo-Wang/govern-eventbus
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。