1. 程式人生 > 其它 >分散式ID(CosId)之號段鏈模式效能(1.2億/s)解析

分散式ID(CosId)之號段鏈模式效能(1.2億/s)解析

分散式ID(CosId)之號段鏈模式效能(1.2億/s)解析

上一篇文章《分散式ID生成器(CosId)設計與實現》我們已經簡單討論過CosId的設計與實現全貌。

但是有很多同學有一些疑問:CosId的號段鏈模式(SegmentChainId)效能有些難以置信(TPS峰值效能1.2億/s),甚至是不同效能級別號段分發器(RedisIdSegmentDistributorJdbcIdSegmentDistributor)均能夠達到這樣的效能級別。

所以本篇文章將深度解析CosId的號段鏈模式(SegmentChainId)的設計思路與實現優化。

新同學最好先查閱上一篇文章《分散式ID生成器(CosId)設計與實現》

背景(為什麼需要SegmentChainId

通過上一篇文章《分散式ID生成器(CosId)設計與實現》我們知道號段模式(SegmentId)主要有以下問題:

  • 穩定性:SegmentId的穩定性問題主要是因為號段用完之後獲取ID的執行緒需要同步進行NextMaxId導致的(會產生網路IO)。
  • 步長(Step):設定多大的步長是一個權衡問題,設定太小會影響整體效能,設定太大會導致全域性ID亂序的程度增加。
  • 本機單調遞增、全域性趨勢遞增: 本地單調遞增是我們期望看到的,但是如何理解/衡量全域性趨勢遞增呢。下面我們來解釋一下什麼是全域性趨勢遞增:

從上圖的號段模式設計中我們可以看出:

  • Instance 1
    每次獲取的NextMaxId,一定比上一次大,意味著下一次的號段一定比上一次大,即F(Tn+1)>F(Tn),所以從單例項上來看是單調遞增的。
  • Instance 1Instance 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依然是亂序的。

所以不難理解的是影響全域性ID亂序的因素有倆個:叢集規模、Step大小。叢集規模是我們不能控制的,但是Step是可以調節的。

所以SegmentChainId就是在這樣的背景下誕生的。

號段鏈模式(SegmentChainId)的優勢

通過SegmentChainId設計圖中我們可以看到,號段鏈模式新增了一個角色PrefetchWorker
PrefetchWorker主要的職責是維護和保證號段鏈頭部到尾部的安全距離,也可以近似理解為緩衝距離。
有了安全距離的保障不難得出的結論是所有獲取ID的執行緒只要從程序記憶體的號段裡邊獲取下次ID即可,理想情況下不需要再進行NextMaxId(向號段分發器請求NextMaxId,網路IO)的,所以效能可以達到近似AtomicLongTPS 效能:12743W+/s的級別。

SegmentChainIdSegmentId的增強版,相比於SegmentId有以下優勢:

  • TPS效能:可達到近似 AtomicLongTPS 效能: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的時候RedisChainIdBenchmarkMySqlChainIdBenchmarkTPS效能幾乎一致(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)

  1. <=TPS(AtomicLong):因為SegmentChainId的內部號段就是使用的AtomicLong,所以這是效能上限。
  2. Step*ExpansionExpansion可以理解為飢餓膨脹係數,預設的飢餓膨脹係數是2。在MySqlChainIdBenchmarkMySqlChainIdBenchmark基準測試中這個值是一樣的。
  3. TPS(IdSegmentDistributor): 這是公式中唯一的不同。指的是請求號段分發器NextMaxId的TPS。
  4. T: 可以理解為基準測試執行時常。

從上面的公式中不難看出RedisChainIdBenchmarkMySqlChainIdBenchmark主要差異是分發器的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


本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。