分布式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

RedisChainIdBenchmark-Throughput

MySqlChainIdBenchmark-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 提交你的疑問。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM