• <del id="a8uas"></del>
    • 千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機構(gòu)

      400-811-9990
      手機站
      千鋒教育

      千鋒學習站 | 隨時隨地免費學

      千鋒教育

      掃一掃進入千鋒手機站

      領取全套視頻
      千鋒教育

      關注千鋒學習站小程序
      隨時隨地免費學習課程

      上海
      • 北京
      • 鄭州
      • 武漢
      • 成都
      • 西安
      • 沈陽
      • 廣州
      • 南京
      • 深圳
      • 大連
      • 青島
      • 杭州
      • 重慶
      當前位置:合肥千鋒IT培訓  >  技術(shù)干貨  >  分布式系統(tǒng)里用戶ID生成有什么好的方法和規(guī)則能滿足“少數(shù)、盡量短、不能直接看出規(guī)則”這幾個條件?

      分布式系統(tǒng)里用戶ID生成有什么好的方法和規(guī)則能滿足“少數(shù)、盡量短、不能直接看出規(guī)則”這幾個條件?

      來源:千鋒教育
      發(fā)布人:xqq
      時間: 2023-10-14 13:17:51

      1、基于UUID

      在Java的世界里,想要得到一個具有少數(shù)性的ID,首先被想到可能就是UUID,畢竟它有著全球少數(shù)的特性。那么UUID可以做分布式ID嗎?答案是可以的,但是并不是十分推薦。

      public static void main(String[] args) {        String uuid = UUID.randomUUID().toString().replaceAll("-","");       System.out.println(uuid); }

      UUID的生成簡單到只有一行代碼,輸出結(jié)果?c2b8c2b9e46c47e3b30dca3b0d447718,但UUID卻并不適用于實際的業(yè)務需求。像用作訂單號UUID這樣的字符串沒有絲毫的意義,看不出和訂單相關的有用信息;而對于數(shù)據(jù)庫來說用作業(yè)務主鍵ID,它不僅是太長還是字符串,存儲性能差查詢也很耗時,所以不推薦用作分布式ID。

      優(yōu)點:生成足夠簡單,本地生成無網(wǎng)絡消耗,具有少數(shù)性。

      缺點:

      無序的字符串,不具備趨勢自增特性;沒有具體的業(yè)務含義;長度過長16 字節(jié)128位,36位長度的字符串,存儲以及查詢對MySQL的性能消耗較大,MySQL官方明確建議主鍵要盡量越短越好,作為數(shù)據(jù)庫主鍵?UUID?的無序性會導致數(shù)據(jù)位置頻繁變動,嚴重影響性能。

      2、基于數(shù)據(jù)庫自增ID

      基于數(shù)據(jù)庫的auto_increment自增ID完全可以充當分布式ID,具體實現(xiàn):需要一個單獨的MySQL實例用來生成ID,建表結(jié)構(gòu)如下:

      當我們需要一個ID的時候,向表中插入一條記錄返回主鍵ID,但這種方式有一個比較致命的缺點,訪問量激增時MySQL本身就是系統(tǒng)的瓶頸,用它來實現(xiàn)分布式服務風險比較大。

      優(yōu)點:實現(xiàn)簡單,ID單調(diào)自增,數(shù)值類型查詢速度快

      缺點:DB單點存在宕機風險,無法扛住高并發(fā)場景

      3、基于數(shù)據(jù)庫集群模式

      前邊說了單點數(shù)據(jù)庫方式不可取,那對上邊的方式做一些高可用優(yōu)化,換成主從模式集群。害怕一個主節(jié)點掛掉沒法用,那就做雙主模式集群,也就是兩個Mysql實例都能單獨的生產(chǎn)自增ID。那這樣還會有個問題,兩個MySQL實例的自增ID都從1開始,會生成重復的ID怎么辦?

      解決方案:設置起始值和自增步長。

      MySQL_1 配置:

      MySQL_2 配置:

      這樣兩個MySQL實例的自增ID分別就是:

      1、3、5、7、92、4、6、8、10

      那如果集群后的性能還是扛不住高并發(fā)咋辦?就要進行MySQL擴容增加節(jié)點,這是一個比較麻煩的事。

      從上圖可以看出,水平擴展的數(shù)據(jù)庫集群,有利于解決數(shù)據(jù)庫單點壓力的問題,同時為了ID生成特性,將自增步長按照機器數(shù)量來設置。增加第三臺MySQL實例需要人工修改一、二兩臺MySQL實例的起始值和步長,把第三臺機器的ID起始生成位置設定在比現(xiàn)有最大自增ID的位置遠一些,但必須在一、二兩臺MySQL實例ID還沒有增長到第三臺MySQL實例的起始ID值的時候,否則自增ID就要出現(xiàn)重復了,必要時可能還需要停機修改。

      優(yōu)點:解決DB單點問題。

      缺點:不利于后續(xù)擴容,而且實際上單個數(shù)據(jù)庫自身壓力還是大,依舊無法滿足高并發(fā)場景。

      4、基于數(shù)據(jù)庫的號段模式

      號段模式是當下分布式ID生成器的主流實現(xiàn)方式之一,號段模式可以理解為從數(shù)據(jù)庫批量的獲取自增ID,每次從數(shù)據(jù)庫取出一個號段范圍,例如 (1,1000] 代表1000個ID,具體的業(yè)務服務將本號段,生成1~1000的自增ID并加載到內(nèi)存。表結(jié)構(gòu)如下:

      CREATE TABLE id_generator (  id int(10) NOT NULL,  max_id bigint(20) NOT NULL COMMENT '當前最大id',  step int(20) NOT NULL COMMENT '號段的布長',  biz_type    int(20) NOT NULL COMMENT '業(yè)務類型',  version int(20) NOT NULL COMMENT '版本號',  PRIMARY KEY (id)) 
      biz_type?:代表不同業(yè)務類型max_id?:當前最大的可用idstep?:代表號段的長度version?:是一個樂觀鎖,每次都更新version,保證并發(fā)時數(shù)據(jù)的正確性

      等這批號段ID用完,再次向數(shù)據(jù)庫申請新號段,對max_id字段做一次update操作,update max_id= max_id + step,update成功則說明新號段獲取成功,新的號段范圍是(max_id ,max_id +step]。

      update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX

      由于多業(yè)務端可能同時操作,所以采用版本號version樂觀鎖方式更新,這種分布式ID生成方式不強依賴于數(shù)據(jù)庫,不會頻繁的訪問數(shù)據(jù)庫,對數(shù)據(jù)庫的壓力小很多。

      5、基于Redis模式

      Redis也同樣可以實現(xiàn),原理就是利用redis的?incr命令實現(xiàn)ID的原子性自增:

      用redis實現(xiàn)需要注意一點,要考慮到redis持久化的問題。redis有兩種持久化方式RDB和AOF:

      RDB會定時打一個快照進行持久化,假如連續(xù)自增但redis沒及時持久化,而這會Redis掛掉了,重啟Redis后會出現(xiàn)ID重復的情況。AOF會對每條寫命令進行持久化,即使Redis掛掉了也不會出現(xiàn)ID重復的情況,但由于incr命令的特殊性,會導致Redis重啟恢復的數(shù)據(jù)時間過長。

      6、基于雪花算法(Snowflake)模式

      雪花算法(Snowflake)是twitter公司內(nèi)部分布式項目采用的ID生成算法,開源后廣受國內(nèi)大廠的好評,在該算法影響下各大公司相繼開發(fā)出各具特色的分布式生成器。

      Snowflake生成的是Long類型的ID,一個Long類型占8個字節(jié),每個字節(jié)占8比特,也就是說一個Long類型占64個比特。Snowflake ID組成結(jié)構(gòu):正數(shù)位(占1比特)+?時間戳(占41比特)+?機器ID(占5比特)+?數(shù)據(jù)中心(占5比特)+?自增值(占12比特),總共64比特組成的一個Long類型。

      名列前茅個bit位(1bit):Java中l(wèi)ong的較高位是符號位代表正負,正數(shù)是0,負數(shù)是1,一般生成ID都為正數(shù),所以默認為0。時間戳部分(41bit):毫秒級的時間,不建議存當前時間戳,而是用(當前時間戳 – 固定開始時間戳)的差值,可以使產(chǎn)生的ID從更小的值開始;41位的時間戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年工作機器id(10bit):也被叫做workId,這個可以靈活配置,機房或者機器號組合都可以。序列號部分(12bit):自增值支持同一毫秒內(nèi)同一個節(jié)點可以生成4096個ID

      根據(jù)這個算法的邏輯,只需要將這個算法用Java語言實現(xiàn)出來,封裝為一個工具方法,那么各個業(yè)務應用可以直接使用該工具方法來獲取分布式ID,只需保證每個業(yè)務應用有自己的工作機器id即可,而不需要單獨去搭建一個獲取分布式ID的應用。Java版本的****Snowflake算法實現(xiàn):

      public class SnowFlakeShortUrl {    private final static long START_TIMESTAMP = 1480166465631L;    private final static long SEQUENCE_BIT = 12;   //序列號占用的位數(shù)    private final static long MACHINE_BIT = 5;     //機器標識占用的位數(shù)    private final static long DATA_CENTER_BIT = 5; //數(shù)據(jù)中心占用的位數(shù)    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);    private final static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT);    private final static long MACHINE_LEFT = SEQUENCE_BIT;    private final static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;    private final static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;    private long dataCenterId;  //數(shù)據(jù)中心    private long machineId;     //機器標識    private long sequence = 0L; //序列號    private long lastTimeStamp = -1L;  //上一次時間戳    private long getNextMill() {        long mill = getNewTimeStamp();        while (mill <= lastTimeStamp) {            mill = getNewTimeStamp();        }        return mill;}    private long getNewTimeStamp() {        return System.currentTimeMillis();    }    public SnowFlakeShortUrl(long dataCenterId, long machineId) {        if (dataCenterId > MAX_DATA_CENTER_NUM || dataCenterId < 0) {            throw new IllegalArgumentException("DtaCenterId can't be greater than MAX_DATA_CENTER_NUM or less than 0!");        }        if (machineId > MAX_MACHINE_NUM || machineId < 0) {            throw new IllegalArgumentException("MachineId can't be greater than MAX_MACHINE_NUM or less than 0!");        }        this.dataCenterId = dataCenterId;        this.machineId = machineId;    }  public synchronized long nextId() {        long currTimeStamp = getNewTimeStamp();        if (currTimeStamp < lastTimeStamp) {            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");        }        if (currTimeStamp == lastTimeStamp) {            //相同毫秒內(nèi),序列號自增            sequence = (sequence + 1) & MAX_SEQUENCE;            //同一毫秒的序列數(shù)已經(jīng)達到最大            if (sequence == 0L) {                currTimeStamp = getNextMill();            }        } else {            //不同毫秒內(nèi),序列號置為0            sequence = 0L;        }        lastTimeStamp = currTimeStamp;        return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //時間戳部分                | dataCenterId << DATA_CENTER_LEFT       //數(shù)據(jù)中心部分                | machineId << MACHINE_LEFT             //機器標識部分                | sequence;                             //序列號部分    }    public static void main(String[] args) {        SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(2, 3);        for (int i = 0; i < (1 << 4); i++) {            System.out.println(snowFlake.nextId());        }    }}

      7、百度(uid-generator)

      uid-generator是由百度技術(shù)部開發(fā),項目GitHub地址為https://github.com/baidu/uid-generator。uid-generator是基于Snowflake算法實現(xiàn)的,與原始的snowflake算法不同在于,uid-generator支持自定義時間戳、工作機器ID和?序列號?等各部分的位數(shù),而且uid-generator中采用用戶自定義workId的生成策略。

      uid-generator需要與數(shù)據(jù)庫配合使用,需要新增一個WORKER_NODE表。當應用啟動時會向數(shù)據(jù)庫表中去插入一條數(shù)據(jù),插入成功后返回的自增ID就是該機器的workId數(shù)據(jù)由host,port組成。對于****uid-generator?ID組成結(jié)構(gòu):workId,占用了22個bit位,時間占用了28個bit位,序列化占用了13個bit位,需要注意的是,和原始的snowflake不太一樣,時間的單位是秒,而不是毫秒,workId也不一樣,而且同一應用每次重啟就會消費一個workId。

      延伸閱讀1:分布式ID的特點

      少數(shù)性:生成的ID全局少數(shù),在特定范圍內(nèi)沖突概率極小。有序性:生成的ID按某種規(guī)則有序,便于數(shù)據(jù)庫插入及排序。可用性:可保證高并發(fā)下的可用性,確保任何時候都能正確的生成ID。自主性:分布式環(huán)境下不依賴中心認證即可自行生成ID。安全性:不暴露系統(tǒng)和業(yè)務的信息,如:訂單數(shù),用戶數(shù)等。
      聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。

      猜你喜歡LIKE

      Hbase適合存哪些數(shù)據(jù)?

      2023-10-14

      什么是采購管理?

      2023-10-14

      什么是ai算法?

      2023-10-14

      最新文章NEW

      linux系統(tǒng)中的安全設置有哪些?

      2023-10-14

      golp是什么設備?

      2023-10-14

      app的啟動流程都包括哪些步驟?

      2023-10-14

      相關推薦HOT

      更多>>

      快速通道 更多>>

      最新開班信息 更多>>

      網(wǎng)友熱搜 更多>>