中文字幕av高清_国产视频一二区_男女羞羞羞视频午夜视频_成人精品一区_欧美色视_在线视频这里只有精品

千鋒教育-做有情懷、有良心、有品質的職業教育機構

手機站
千鋒教育

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

千鋒教育

掃一掃進入千鋒手機站

領取全套視頻
千鋒教育

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

當前位置:首頁  >  技術干貨  > 分布式系統里用戶ID生成有什么好的方法和規則能滿足“少數、盡量短、不能直接看出規則”這幾個條件?

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

來源:千鋒教育
發布人:xqq
時間: 2023-10-12 21:06:57 1697116017

1、基于UUID

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

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

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

優點:生成足夠簡單,本地生成無網絡消耗,具有少數性。

缺點:

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

2、基于數據庫自增ID

基于數據庫的auto_increment自增ID完全可以充當分布式ID,具體實現:需要一個單獨的MySQL實例用來生成ID,建表結構如下:

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

優點:實現簡單,ID單調自增,數值類型查詢速度快

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

3、基于數據庫集群模式

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

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

MySQL_1 配置:

MySQL_2 配置:

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

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

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

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

優點:解決DB單點問題。

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

4、基于數據庫的號段模式

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

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 '業務類型',  version int(20) NOT NULL COMMENT '版本號',  PRIMARY KEY (id)) 
biz_type?:代表不同業務類型max_id?:當前最大的可用idstep?:代表號段的長度version?:是一個樂觀鎖,每次都更新version,保證并發時數據的正確性

等這批號段ID用完,再次向數據庫申請新號段,對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

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

5、基于Redis模式

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

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

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

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

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

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

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

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

public class SnowFlakeShortUrl {    private final static long START_TIMESTAMP = 1480166465631L;    private final static long SEQUENCE_BIT = 12;   //序列號占用的位數    private final static long MACHINE_BIT = 5;     //機器標識占用的位數    private final static long DATA_CENTER_BIT = 5; //數據中心占用的位數    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;  //數據中心    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) {            //相同毫秒內,序列號自增            sequence = (sequence + 1) & MAX_SEQUENCE;            //同一毫秒的序列數已經達到最大            if (sequence == 0L) {                currTimeStamp = getNextMill();            }        } else {            //不同毫秒內,序列號置為0            sequence = 0L;        }        lastTimeStamp = currTimeStamp;        return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //時間戳部分                | dataCenterId << DATA_CENTER_LEFT       //數據中心部分                | 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是由百度技術部開發,項目GitHub地址為https://github.com/baidu/uid-generator。uid-generator是基于Snowflake算法實現的,與原始的snowflake算法不同在于,uid-generator支持自定義時間戳、工作機器ID和?序列號?等各部分的位數,而且uid-generator中采用用戶自定義workId的生成策略。

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

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

少數性:生成的ID全局少數,在特定范圍內沖突概率極小。有序性:生成的ID按某種規則有序,便于數據庫插入及排序。可用性:可保證高并發下的可用性,確保任何時候都能正確的生成ID。自主性:分布式環境下不依賴中心認證即可自行生成ID。安全性:不暴露系統和業務的信息,如:訂單數,用戶數等。
聲明:本站稿件版權均屬千鋒教育所有,未經許可不得擅自轉載。
10年以上業內強師集結,手把手帶你蛻變精英
請您保持通訊暢通,專屬學習老師24小時內將與您1V1溝通
免費領取
今日已有369人領取成功
劉同學 138****2860 剛剛成功領取
王同學 131****2015 剛剛成功領取
張同學 133****4652 剛剛成功領取
李同學 135****8607 剛剛成功領取
楊同學 132****5667 剛剛成功領取
岳同學 134****6652 剛剛成功領取
梁同學 157****2950 剛剛成功領取
劉同學 189****1015 剛剛成功領取
張同學 155****4678 剛剛成功領取
鄒同學 139****2907 剛剛成功領取
董同學 138****2867 剛剛成功領取
周同學 136****3602 剛剛成功領取
相關推薦HOT
主站蜘蛛池模板: 亚洲成人精品在线 | 精品国产成人 | 欧美在线一二三 | 国产欧美精品一区二区三区四区 | 国产成人免费视频网站视频社区 | 亚洲男人天堂网 | 国产日韩精品视频 | 国产一区二区三区在线免费 | 欧美色图一区 | 成人国产一区 | 日韩大片 | 青青草中文字幕 | 天天干夜夜爽 | 日韩久久久久久久久久久 | 日韩高清一区 | 亚洲自拍偷拍第一页 | 国产精品久久久久久久久久免费 | 中文字幕一区二区三区免费视频 | 色婷婷综合久久久 | 亚洲欧美日韩另类精品一区二区三区 | 精品视频一区二区 | 亚州中文字幕 | 精品久久久久久亚洲综合网站 | 欧美视频在线免费 | 精品久久一 | 色婷婷狠狠 | 久久毛片 | 色在线免费视频 | 午夜小视频免费 | 天天澡天天狠天天天做 | 中国av在线 | 色婷婷在线视频观看 | 日韩不卡一区 | 国产真实乱全部视频 | 美日韩三级 | 欧美美女爱爱视频 | 精品超碰 | 成人日韩| 日韩精品资源 | 在线亚洲精品 | 久色视频在线 |