來選擇一個適合你的分佈式ID生成方案吧

在分佈式環境中,由於多負載,高併發等原因,很多業務都需要分佈式ID生成器來生成他們的唯一ID,其實方案有很多,今天就結合我們自己使用和了解過的幾種方案來記錄一下。希望多你也有幫助。

UUID

UUID是我們很常見的一個ID方案,理論上唯一,生成方式簡單。

但ID本身無意義所以不適合做ID需要表示含義的內容,如需要像客戶展示的訂單號;而且總長36位過長,去除"-"後也有32位,存儲到DB佔用存儲空間大。而且作爲聚集索引的話,每次去比較轉換添加新的索引時因爲b+tree要排序,會有嚴重的性能問題。

UUID適合一些ID無意義的場景。如我們在架構中應用到分佈式鏈路追蹤的ID上。

mysql 自增

這種方案其實就是利用mysql的自增主鍵,因爲mysql主鍵是聚集索引,物理上連續,並且自增主鍵存儲佔用少,效率高;但mysql存在單點問題,不利於數據遷移。而且db會有壓力和瓶頸。我們在一些不太容易變動的如管理員表,菜單表中使用自增主鍵。以達到最高的效率。並且沒有分庫分表,遷移等問題。

mysql自增+step

mysql自增方案有單點問題,所以在mysql集羣下使用自增主鍵+步長方案,比如3臺mysql,步長設置3,那麼第一個mysql中的主鍵就是1 4 7,第二臺是2 5 8,第三臺是3 6 9。

但step一旦設定好無法擴容。集羣無法擴容。我認爲該方案實用性一般。目前沒有實踐過。

redis

分佈式系統中採用redis做緩存很常見。我們也不例外。redis是基於內存的一種nosql數據庫,數據操作是單線程,利用原子性的incr可以獲取自定義的自增ID,比如取用當前時間如2019101010222,再從redis操作incr獲取自增序列拼接。好處就是靈活自定義,ID有含義,有趨勢遞增,缺點是佔用網絡IO。效率一般。我們目前使用該方案做銷售、出庫、採購等內部流轉訂單編號。

snowflake

這個就很知名了。64位2進制數,第一位不用➕41位時間戳➕10位機器碼(或5位機房碼+5位機器碼,可自由定製)➕12位序列(毫秒內計數),理論上毫秒內可生成1024*4096個id,並且趨勢遞增。

但缺點是依賴服務器時鐘,存在時間回撥問題,如果自己實現該算法可記錄上一次時間戳,進行比較,短回撥可等待,長回撥只能拋出異常。我們使用的mybatis plus內置的IdWorker來做對外訂單號。目前倒是還沒出現什麼問題。

mysql的buffer方案

做一個id表,根據不同業務,設置每個業務的起時值,當前最大值,業務服務每次請求獲取一個ID區間放入自身jvm中。然後更新起始值和最大值,當有ID需要使用時,業務服務在jvm內使用線程安全方式去獲取ID值。這樣就完美解決了每次獲取id到要去查db的性能問題以及步長導致的無法擴容問題,並且就算業務系統掛掉,重新獲取ID區間,也不會出現重複問題,當然也可做單獨的ID生成服務。

但這個方案問題是如果業務服務負載衆多時,會出現多個服務同時用完當前ID區間去請求新的區間,這也可以通過分佈式鎖,或者db鎖保證區間的唯一,但是還是會出現請求服務一會快一會慢的情況。

mysql的雙buffer方案

爲了解決buffer中忽快忽慢問題,設計使用雙buffer,即在jvm內配置兩個存儲id區間的buffer ,當第一次請求獲取的區間放到buffer1中,當使用達到10%時,另起線程請求新的ID區間,放入buffer2中.當buffer1中ID用盡,自動切換到buffer2,同理,buffer2用到10%時請求新的放入buffer1。但這個方案仍無法解決大面積宕機或者迭代升級後,服務重啓導致的多實例同時去獲取ID區間的併發問題。

 

記錄了這麼多方案,其實每個方案都有自己的最佳場景。選擇適合自己架構的纔是最好的。如果併發沒有一線互聯網公司這麼高,其實沒有必要上雙buffer,只不過是給自己徒增麻煩。如果也根本不遷移,不會涉及到分庫分表,其實自增ID更香一些。所以方案可以多瞭解,但是最重要選擇一個適合自己業務場景的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章