日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

手把手教你千万级唯一ID如何生成

發布時間:2023/12/16 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 手把手教你千万级唯一ID如何生成 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天的話題,要給大家分享的大廠面試題是:千萬級唯一ID如何生成?!看起來這是一個非常具體的問題,沒錯!分布式項目中,無法避免這個問題,但是我想說的是,面試官通過打開這個問題,是可以從這個角度了解面試者在分布式應用中是否有豐富經驗的,分布式大型項目才會有這個問題吧,看似一個具體的問題,背后卻是面試官密謀最佳人選的經驗,今天威哥就來聊一聊這個話題。

為了讓小伙伴們有身臨其境的感覺,威哥會以場景化的面試方式來講解,小伙伴們準備好了嗎,馬上開整。

首先來看一下互聯網大廠必問題 :

  • 做過分布式項目嗎?

  • 知道分布式 ID 生成策略嗎?

  • 如何實現的?

注意面試官的預期

  • 是否有分布式項目經驗

  • 對分布式 ID 生成算法研究的深度

  • 面試官老哥:

    “一條大河向東流,ID 策略惹閑愁”

    在分布式項目中,你使用的分布式 ID 策略是什么?

    面試者小哥哥:

    “我敬歲月三杯酒,雪花算法來出頭”

    是的,我在分布式項目采用當前主流的雪花算法來實現。

    SnowFlake 算法,是 Twitter 開源的分布式 id 生成算法。其核心思想就是:使用一個 64 bit 的 long 型的數字作為全局唯一 id,在分布式系統中的應用十分廣泛。

    在復雜分布式系統中,往往需要對大量的數據和消息進行唯一標識。如在美團點評的金融、支付、餐飲、酒店、貓眼電影等產品的系統中,數據日漸增長,對數據分庫分表后需要有一個唯一ID來標識一條數據或消息,數據庫的自增ID顯然不能滿足需求;特別一點的如訂單、騎手、優惠券也都需要有唯一ID做標識。此時一個能夠生成全局唯一ID的系統是非常必要的。

    概括下來,那業務系統對ID號的要求有哪些呢?

    • 全局唯一性:不能出現重復的ID號,既然是唯一標識,這是最基本的要求。

    • 趨勢遞增:在MySQL InnoDB引擎中使用的是聚集索引,由于多數RDBMS使用B-tree的數據結構來存儲索引數據,在主鍵的選擇上面我們應該盡量使用有序的主鍵保證寫入性能。

    • 單調遞增:保證下一個ID一定大于上一個ID,例如事務版本號、IM增量消息、排序等特殊需求。

    • 信息安全:如果ID是連續的,惡意用戶的扒取工作就非常容易做了,直接按照順序下載指定URL即可;如果是訂單號就更危險了,競對可以直接知道我們一天的單量。所以在一些應用場景下,會需要ID無規則。

    常見方法生成 ID 帶來的問題?

    1.UUID:示例:550e8400-e29b-41d4-a716-446655 440000

    • 優點:性能非常高

    • 缺點:不易于存儲、信息不安全、不適合作為主鍵

    2.數據庫生成:以MySQL舉例,利用給字段設置auto_increment_increment和auto_increment_offset來保證ID自增。

    • 優點:非常簡單、ID號單調自增

    • 缺點:強依賴DB、ID發號性能瓶頸限制在單臺MySQL的讀寫性能

    分布式id生成器-雪花算法

    JAVA實現雪花算法:https://github.com/beyond fengyu/SnowFlake

    這 64 個 bit 中:

    其中1個bit是不用的,然后用其中的41 bit作為毫秒數,用10 bit作為工作機器id,12 bit作為序列號。

    面試官老哥:

    你們項目中是怎樣使用雪花算法的,可以講一講嗎?

    面試者小哥哥:

    關于這個問題,我們可以直接切入正題,以下以Leaf解決方案來展開,Leaf方案(https://github.com /Meituan-Dianping/Leaf)

    Leaf方案實現

    Leaf這個名字是來自德國哲學家、數學家萊布尼茨的一句話:世界上沒有兩片相同的樹葉(There are no two identical leaves in the world )

    Leaf實現了Leaf-segment(號段模式)和Leaf-snowflake方案(雪花算法)

    第一種Leaf-segment方案,在使用數據庫的方案上,做了如下改變:-原方案每次獲取ID都得讀寫一次數據庫,造成數據庫壓力大。改為利用proxy server批量獲取,每次獲取一個segment(step決定大小)號段的值。用完之后再去數據庫獲取新的號段,可以大大的減輕數據庫的壓力。各個業務不同的發號需求用biz_tag字段來區分,每個biz-tag的ID獲取相互隔離,互不影響。如果以后有性能需求需要對數據庫擴容,不需要上述描述的復雜的擴容操作,只需要對biz_tag分庫分表就行。

    重要字段說明:biz_tag用來區分業務,max_id表示該biz_tag目前所被分配的ID號段的最大值,step表示每次分配的號段長度。原來獲取ID每次都需要寫數據庫,現在只需要把step設置得足夠大,比如1000。那么只有當1000個號被消耗完了之后才會去重新讀寫一次數據庫。讀寫數據庫的頻率從1減小到了1/step。

    test_tag在第一臺Leaf機器上是1~1000的號段,當這個號段用完時,會去加載另一個長度為step=1000的號段,假設另外兩臺號段都沒有更新,這個時候第一臺機器新加載的號段就應該是3001~4000。同時數據庫對應的biz_tag這條數據的max_id會從3000被更新成4000。

    Leaf 還采用雙buffer對號段模式進行優化

    簡單的說就是:Leaf 取號段的時機是在號段消耗完的時候進行的,也就意味著號段臨界點的ID下發時間取決于下一次從DB取回號段的時間,并且在這期間進來的請求也會因為DB號段沒有取回來,導致線程阻塞。如果請求DB的網絡和DB的性能穩定,這種情況對系統的影響是不大的,但是假如取DB的時候網絡發生抖動,或者DB發生慢查詢就會導致整個系統的響應時間變慢。

    這樣DB取號段的過程能夠做到無阻塞,不需要在DB取號段的時候阻塞請求線程,即當號段消費到某個點時就異步的把下一個號段加載到內存中。而不需要等到號段用盡的時候才去更新號段。

    采用雙buffer的方式,Leaf服務內部有兩個號段緩存區segment。當前號段已下發10%時,如果下一個號段未更新,則另啟一個更新線程去更新下一個號段。當前號段全部下發完后,如果下個號段準備好了則切換到下個號段為當前segment接著下發,循環往復。

    Leaf-segment方案可以生成趨勢遞增的ID,同時ID號是可計算的,不適用于訂單ID生成場景,比如競對在兩天中午12點分別下單,通過訂單id號相減就能大致計算出公司一天的訂單量,這個是不能忍受的。面對這一問題,美團提供了 Leaf-snowflake方案。

    Leaf-snowflake方案完全沿用snowflake方案的bit位設計,即是“1+41+10+12”的方式組裝ID號。對于workerID的分配,當服務集群數量較小的情況下,完全可以手動配置。Leaf服務規模較大,動手配置成本太高。所以使用Zookeeper持久順序節點的特性自動對snowflake節點配置wokerID。Leaf-snowflake是按照下面幾個步驟啟動的:

    • 啟動Leaf-snowflake服務,連接Zookeeper,在leaf_forever父節點下檢查自己是否已經注冊過(是否有該順序子節點)。

    • 如果有注冊過直接取回自己的workerID(zk順序節點生成的int類型ID號),啟動服務。

    • 如果沒有注冊過,就在該父節點下面創建一個持久順序節點,創建成功后取回順序號當做自己的workerID號,啟動服務。

    解決時鐘問題

    因為這種方案依賴時間,如果機器的時鐘發生了回撥,那么就會有可能生成重復的ID號,需要解決時鐘回退的問題。

    參見上圖整個啟動流程圖,服務啟動時首先檢查自己是否寫過ZooKeeper leaf_forever節點:

    • 若寫過,則用自身系統時間與leaf_forever/${self}節點記錄時間做比較,若小于leaf_forever/${self}時間則認為機器時間發生了大步長回撥,服務啟動失敗并報警。

    • 若未寫過,證明是新服務節點,直接創建持久節點leaf_forever/${self}并寫入自身系統時間,接下來綜合對比其余Leaf節點的系統時間來判斷自身系統時間是否準確,具體做法是取leaf_temporary下的所有臨時節點(所有運行中的Leaf-snowflake節點)的服務IP:Port,然后通過RPC請求得到所有節點的系統時間,計算sum(time)/nodeSize。

    • 若abs( 系統時間-sum(time)/nodeSize ) < 閾值,認為當前系統時間準確,正常啟動服務,同時寫臨時節點leaf_temporary/${self} 維持租約。

    • 否則認為本機系統時間發生大步長偏移,啟動失敗并報警。

    • 每隔一段時間(3s)上報自身系統時間寫入leaf_forever/${self}。

    Leaf在美團點評公司內部服務包含金融、支付交易、餐飲、外賣、酒店旅游、貓眼電影等眾多業務線。目前Leaf的性能在4C8G的機器上QPS能壓測到近5w/s,TP999 1ms,已經能夠滿足大部分的業務的需求。每天提供億數量級的調用量。

    其他ID生產策略

    百度uid-generator

    https://github.com/baidu/uid-generator

    UidGenerator是Java實現的, 基于Snowflake算法的唯一ID生成器。UidGenerator以組件形式工作在應用項目中, 支持自定義workerId位數和初始化策略, 從而適用于docker等虛擬化環境下實例自動重啟、漂移等場景。在實現上, UidGenerator通過借用未來時間來解決sequence天然存在的并發限制; 采用RingBuffer來緩存已生成的UID, 并行化UID的生產和消費, 同時對CacheLine補齊,避免了由RingBuffer帶來的硬件級「偽共享」問題. 最終單機QPS可達600萬。

    滴滴 Tinyid

    https://github.com/didi/tinyid

    Tinyid是用Java開發的一款分布式id生成系統,基于數據庫號段算法實現,關于這個算法可以參考美團leaf或者tinyid原理介紹。Tinyid擴展了leaf-segment算法,支持了多db(master),同時提供了java-client(sdk)使id生成本地化,獲得了更好的性能與可用性。Tinyid在滴滴客服部門使用,均通過tinyid-client方式接入,每天生成億級別的id。

    最后小結一下

    本文我們一起聊了分布式 ID 生成策略,Snowflake算法的原理,實現方案,和國內大廠的開源實現,這點很重要,在面試過程,可以按照這個思路:找到問題,以及如何解決問題,這個思路不僅僅是在搞技術寫代碼上,在日常工作中的任何事情都可以有這個思路,你想啊,如果只提問題,不提解決問題的方法,那就成了抱怨了,這不是一個積極努力向上的人應該有的狀態。

    代碼人生,從代碼中還能領悟到做事的道理,你學會了嗎?歡迎各位前進路上的兄弟們評論關注,每天進步一點點,威哥與你同在。

    總結

    以上是生活随笔為你收集整理的手把手教你千万级唯一ID如何生成的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。