玄靖
劉兵,花名玄靖,目前供職于阿裡巴巴,開源技術愛好者,高性能redis中間件nredis-proxy作者,目前研究方向為java中間件,微服務等技術。
(一) 什麼是分布式發号器
說起分布式發号器的前生今世,咱們應該感恩這個時代;随着網際網路在中國越來越普及化,單機系統或者一個小系統已經無法滿足需要,随着使用者逐漸增多,資料量越來越大,單個應用或者單個資料庫已經無法滿足需求,在應用以至于微服務來臨,在資料庫存儲方面分庫分表來臨,可以解決問題;但是新的問題産生,怎麼樣做到多個應用可以有唯一主鍵或者序号,防止資料重複呢?分布式發号器正好為解決這個問題,可以讓大家無須為這個問題煩惱了,這是本人寫這篇文章初衷
(二) 分布式發号器優勢
1) 解決分庫分表中唯一序号的問題
2) 解決分布式應用或者微服務架構中唯一序号的問題
3) 提供可定制化生成規則,根據業務需求可自定義擴充
4) 性能高效且系統簡單穩定
5) 系統可任意擴充
(三) 分布式發号器架構圖
(四) 分布式發号器流程圖
1) 分布式發号器重要字段
2) concurrentvalue不存在的流程圖
3) concurrentvalue存在的流程圖
(五) 目前存在分布式發号器解決方案
1) uuid
universally unique identifier(uuid),有着正兒八經的rfc規範,是一個128bit的數字,也可以表現為32個16進制的字元(每個字元0-f的字元代表4bit),中間用"-"分割。
時間戳+uuid版本号: 分三段占16個字元(60bit+4bit)
clock sequence号與保留字段:占4個字元(13bit+3bit)
節點辨別:占12個字元(48bit)
2) hibernate
hibernate的customversiononestrategy.java,解決了之前version 1的兩個問題
時間戳(6bytes, 48bit):毫秒級别的,從1970年算起,能撐8925年....
順序号(2bytes, 16bit, 最大值65535): 沒有時間戳過了一毫秒要歸零的事,各搞各的,short溢出到了負數就歸0。
機器辨別(4bytes 32bit): 拿localhost的ip位址,ipv4呢正好4個byte,但如果是ipv6要16個bytes,就隻拿前4個byte。
程序辨別(4bytes 32bit): 用目前時間戳右移8位再取整數應付,不信兩條線程會同時啟動。
3) mongodb
mongodb的objectid.java
時間戳(4 bytes 32bit):是秒級别的,從1970年算起,能撐136年。
自增序列(3bytes 24bit, 最大值一千六百萬): 是一個從随機數開始(機智)的int不斷加一,也沒有時間戳過了一秒要歸零的事,各搞各的。因為隻有3bytes,是以一個4bytes的int還要截一下後3bytes。
機器辨別(3bytes 24bit): 将所有網卡的mac位址拼在一起做個hashcode,同樣一個int還要截一下後3bytes。搞不到網卡就用随機數混過去。
程序辨別(2bytes 16bits):從jmx裡搞回來到程序号,搞不到就用程序名的hash或者随機數混過去。
可見,mongodb的每一個字段設計都比hibernate的更合理一點,時間戳是秒級别的,自增序列變長了,程序辨別變短了。總長度也降到了12 bytes 96bit。
4) twitter的snowflake派号器
snowflake也是一個派号器,基于thrift的服務,不過不是用redis簡單自增,而是類似uuid version1,
隻有一個long 64bit的長度,是以idworker緊巴巴的配置設定成:
時間戳(42bit) :自從2012年以來(比那些從1970年算起的會過日子)的毫秒數,能撐139年。
自增序列(12bit,最大值4096):毫秒之内的自增,過了一毫秒會重新置0。
datacenter id (5 bit, 最大值32):配置值,支援多機房。
worker id ( 5 bit, 最大值32),配置值,因為是派号器的id,一個機房裡最多32個派号器就夠了,還會在zk裡做下注冊。
可見,因為是中央派号器,把至少40bit的節點辨別都省出來了,換成10bit的派号器辨別。是以整個uid能夠隻用一個long表達。
另外,這種派号器,client每次隻能一個id,不能批量取,是以額外增加的延時是問題,而且隻能1024台機器範圍之内。
以上幾種方案同一個問題,不可自定義,位數過長
來源:中生代技術
<a href="https://mp.weixin.qq.com/s/d9qiuzgyi6l23khnyqhzfw" target="_blank">原文連結</a>