您好,登錄后才能下訂單哦!
本篇內容介紹了“基于Ignite的分布式ID生成器怎么實現”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
JDK在1.5版本之后,提供了java.util.concurrent包,其中java.util.concurrent.atomic子包中包含了對于單一變量的線程安全的支持lock-free的編程實現。該包中的類,比如AtomicLong
,提供了和Long類型相對應的原子化操作,比如一些increment方法,基于這些功能,是可以開發出單JVM的序列生成器這樣的功能的,但是對于分布式環境,則無能為力。 在Ignite中,除了提供標準的基于鍵-值的類似于Map的存儲以外,還提供了一種分布式數據結構的實現,其中包括:IgniteAtomicLong
,IgniteSet
,IgniteQueue
,IgniteAtomicReference
,IgniteAtomicSequence
,IgniteCountDownLatch
,IgniteSemaphore
,這些類除了提供和JDK相同的功能外,就是增加了對分布式環境的支持,也就是支持集群范圍內的原子化操作。 鑒于本文重點是討論分布式ID生成器,所以下文的重點在于IgniteAtomicSequence。
IgniteAtomicSequence接口提供了分布式的原子性序列,類似于分布式原子性的Long類型,但是他的值只能增長,他特有的功能是支持預留一定范圍的序列值,來避免每次序列獲取下一個值時都需要的昂貴的網絡消耗和緩存更新,也就是,當在一個原子性序列上執行了incrementAndGet()(或者任何其他的原子性操作),數據結構會往前預留一定范圍的序列值,他會保證對于這個序列實例來說跨集群的唯一性。 這個類型的使用是非常簡單的,相關代碼如下:
Ignite ignite = Ignition.start(); IgniteAtomicSequence seq = ignite.atomicSequence("seqName",//序列名 0, //初始值 true//如果序列不存在則創建 ); for (int i = 0; i < 20; i++) { long currentValue = seq.get();//獲取當前值 long newValue = seq.incrementAndGet();//先加1再取值 ... }
這個樣例中創建的seq,初始值從0開始,然后遞增,看上去很完美,但是當系統不管什么原因重啟后,就又會從0開始,這顯然是無法保證唯一性的,因此這個方法還是不能在生產環境下使用。
按照前述,直接按照初始值0創建IgniteAtomicSequence,是有很大風險的,無法在生產環境下使用,而且存在長度不固定問題,所以還需要進一步想辦法,研究的重點在于解決初始值的問題。 因為IgniteAtomicSequence的值為long型,而在Java中long類型的最大值是9223372036854775807,這個數值長度為19位,對于實際應用來說,是一個很大的值,但是對于常見的沒有環境依賴的ID生成器來說,還是比較短的。因此我們打算在這方面做文章。 因為系統重置的一個重要指標就是時間,那么我們以時間作為參照,然后加上一個擴展,可能是一個比較理想的選擇,我們以如下的規則作為初始值: 時間的yyyyMMddHHmmss+00000 這個長度正好是19位,然后每次加1,因為現在是2016年,這個規則在常規應用場景中,是不會超過long類型的最大值的。 但是,這個規則存在一個風險,就是假設不考慮實際應用和實際性能,如果增加操作業務量特別大,會使這個序列值快速進位,如果某個時間節點宕機后瞬間重啟,是有可能存在重啟后的初始值小于原來的最大值的,這時就無法保證唯一性了。下面就對這個理論情況下的最大值做一個計算,然后開發者就會知道在自己的應用中如何改進這個規則以滿足個性化需求了。
假定不考慮實際性能,我們以最簡單的情況為例,就是啟動后一秒鐘內訪問達到峰值,然后宕機后瞬間重啟這種情況,這個很容易就能看出來,不需要計算,就是5個0對應的最大值10萬,以此類推,考慮到時間的進位和十進制進位的不同,我們可以計算出一分鐘后、一小時后、一天后、一月后、一年后宕機換算出的交易量的極大值,如下:
每秒 | 每分 | 每小時 | 每天 | 每月 | 每年 | |
---|---|---|---|---|---|---|
1秒 | 10萬 | - | - | - | - | - |
1分 | 16.6萬 | 1000萬 | - | - | - | - |
1小時 | 27.7萬 | 1666萬 | 10億 | - | - | - |
1天 | 115萬 | 6944萬 | 41.66億 | 1000億 | - | - |
1月 | 165萬 | 9920萬 | 59.5億 | 3571.43億 | 10萬億 | - |
1年 | 3215萬 | 19億 | 1157億 | 2.7萬億 | 83萬億 | 1000萬億 |
以1分鐘為例進行說明,假設初始值為2016011815341200000,一分鐘后宕機瞬間重啟,對應的初始值為2016011815351200000,這個差額是10000000,對應的每秒交易量為16.6萬。 從上圖來看,對于這樣的規則,能承載的交易量還是很大的,當今世界最繁忙的交易系統,也不會超過這個極限情況下的極值,也就是說,這個規則就目前來說,具有普遍適用性。 而在實際生產中,瞬間重啟是不存在的,隨著重啟時間向后推移,新的初始值會和原來的最大值拉開差距,更不可能出現沖突了。 關于性能,我在一臺2011年的舊筆記本上進行測試,很容易就能達到50K/s的序列生成速度,這個還是可以的,但是這是在開啟預留的前提下實現的,如果不開啟預留,性能可能下降到13K/s。在一個具體的集群環境下,通常不會拿Ignite單獨建立服務做ID分發中心,所以實際環境下性能能不能滿足需求,開發者需要自行進行測試,評估然后做選擇。另外,開啟了預留會導致最終生成的ID可能不是隨時間線性增長的,這個也需要注意。
前述的基于Ignite的分布式ID生成器,優點是實現簡單,將一個jar包嵌入應用后ID生成系統和應用生命周期一致,設置了備份后不存在單點故障,數值線性遞增可比較大小,規則按照業務定制后可以做得更短,如果轉成十六進制后,會非常短,不依賴數據庫,不對數據庫產生壓力,缺點可能就是性能以及一些特定的業務需求了。 生成全局唯一ID的需求是剛性的,尤其是分布式環境中,問題顯得尤為復雜。當前,這方面的實現方案非常多,通用的不通用的,本文不做詳細的論述,只做簡單的列舉:
UUID 優點是性能好,缺點是比較長,128位,無規則,無法比較大小。
ID分發中心 比如twitter的snowflake服務,可以做到高性能,高可用,低延遲,時間上有序,缺點就是使整個系統變得復雜,維護工作量加大。
MongoDB的ObjectID(類似UUID) MongoDB的驅動中提供了objectId的生成功能,優點是相對于UUID要短些,時間上有序,而且這個id包含了很多有用的信息,解碼后就可以獲得。
數據庫生成 有很多的基于數據庫的方案,比如基于Oracle和PostgreSQL的序列,Flickr和Instagram也有基于數據庫的比較復雜的方案。
其他 根據不同的業務場景,可以做出各種各樣的、滿足各種需求的ID生成方案,需求越多,實現也會越復雜。
“基于Ignite的分布式ID生成器怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。