千鋒教育-做有情懷、有良心、有品質(zhì)的職業(yè)教育機(jī)構(gòu)

手機(jī)站
千鋒教育

千鋒學(xué)習(xí)站 | 隨時(shí)隨地免費(fèi)學(xué)

千鋒教育

掃一掃進(jìn)入千鋒手機(jī)站

領(lǐng)取全套視頻
千鋒教育

關(guān)注千鋒學(xué)習(xí)站小程序
隨時(shí)隨地免費(fèi)學(xué)習(xí)課程

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

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

來源:千鋒教育
發(fā)布人:xqq
時(shí)間: 2023-10-12 21:06:57 1697116017

1、基于UUID

在Java的世界里,想要得到一個(gè)具有少數(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卻并不適用于實(shí)際的業(yè)務(wù)需求。像用作訂單號(hào)UUID這樣的字符串沒有絲毫的意義,看不出和訂單相關(guān)的有用信息;而對(duì)于數(shù)據(jù)庫來說用作業(yè)務(wù)主鍵ID,它不僅是太長還是字符串,存儲(chǔ)性能差查詢也很耗時(shí),所以不推薦用作分布式ID。

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

缺點(diǎn):

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

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

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

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

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

缺點(diǎn):DB單點(diǎn)存在宕機(jī)風(fēng)險(xiǎn),無法扛住高并發(fā)場(chǎng)景

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

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

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

MySQL_1 配置:

MySQL_2 配置:

這樣兩個(gè)MySQL實(shí)例的自增ID分別就是:

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

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

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

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

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

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

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

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

等這批號(hào)段ID用完,再次向數(shù)據(jù)庫申請(qǐng)新號(hào)段,對(duì)max_id字段做一次update操作,update max_id= max_id + step,update成功則說明新號(hào)段獲取成功,新的號(hào)段范圍是(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è)務(wù)端可能同時(shí)操作,所以采用版本號(hào)version樂觀鎖方式更新,這種分布式ID生成方式不強(qiáng)依賴于數(shù)據(jù)庫,不會(huì)頻繁的訪問數(shù)據(jù)庫,對(duì)數(shù)據(jù)庫的壓力小很多。

5、基于Redis模式

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

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

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

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

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

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

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

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

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

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

延伸閱讀1:分布式ID的特點(diǎn)

少數(shù)性:生成的ID全局少數(shù),在特定范圍內(nèi)沖突概率極小。有序性:生成的ID按某種規(guī)則有序,便于數(shù)據(jù)庫插入及排序。可用性:可保證高并發(fā)下的可用性,確保任何時(shí)候都能正確的生成ID。自主性:分布式環(huán)境下不依賴中心認(rèn)證即可自行生成ID。安全性:不暴露系統(tǒng)和業(yè)務(wù)的信息,如:訂單數(shù),用戶數(shù)等。
聲明:本站稿件版權(quán)均屬千鋒教育所有,未經(jīng)許可不得擅自轉(zhuǎn)載。
10年以上業(yè)內(nèi)強(qiáng)師集結(jié),手把手帶你蛻變精英
請(qǐng)您保持通訊暢通,專屬學(xué)習(xí)老師24小時(shí)內(nèi)將與您1V1溝通
免費(fèi)領(lǐng)取
今日已有369人領(lǐng)取成功
劉同學(xué) 138****2860 剛剛成功領(lǐng)取
王同學(xué) 131****2015 剛剛成功領(lǐng)取
張同學(xué) 133****4652 剛剛成功領(lǐng)取
李同學(xué) 135****8607 剛剛成功領(lǐng)取
楊同學(xué) 132****5667 剛剛成功領(lǐng)取
岳同學(xué) 134****6652 剛剛成功領(lǐng)取
梁同學(xué) 157****2950 剛剛成功領(lǐng)取
劉同學(xué) 189****1015 剛剛成功領(lǐng)取
張同學(xué) 155****4678 剛剛成功領(lǐng)取
鄒同學(xué) 139****2907 剛剛成功領(lǐng)取
董同學(xué) 138****2867 剛剛成功領(lǐng)取
周同學(xué) 136****3602 剛剛成功領(lǐng)取
相關(guān)推薦HOT
為什么Oracle收購MySQL后仍保證其開源免費(fèi)?

一、為什么Oracle收購MySQL后仍保證其開源免費(fèi)MySQL在收購前一直使用GPL許可分發(fā),而GPL許可是不可撤銷的,這意味著至少被收購之前的最后一個(gè)Re...詳情>>

2023-10-12 22:52:49
為什么MySQL對(duì)SQL標(biāo)準(zhǔn)中很多基本用法都不支持?

一、為什么MySQL對(duì)SQL標(biāo)準(zhǔn)中很多基本用法都不支持因?yàn)楫?dāng)年,在微軟.net技術(shù)棧下開發(fā)應(yīng)用,用的就是sql server數(shù)據(jù)庫。在特性方面,不僅緊跟sql...詳情>>

2023-10-12 22:49:20
數(shù)據(jù)庫與Microsoft Excel有什么區(qū)別?

一、數(shù)據(jù)庫與Microsoft Excel的區(qū)別1、結(jié)構(gòu)不同excel即是電子數(shù)據(jù)表,顯示由一系列行與列構(gòu)成的網(wǎng)格。。其中的單元格可以用于存放數(shù)值、計(jì)算式...詳情>>

2023-10-12 22:47:09
web頁面安全是什么?

一、web頁面安全是什么Web頁面安全指的是保護(hù)Web應(yīng)用程序和用戶數(shù)據(jù)免受惡意攻擊和不當(dāng)使用的一系列措施。它旨在確保Web頁面的機(jī)密性、完整性和...詳情>>

2023-10-12 22:35:44
為什么分布式數(shù)據(jù)庫這么喜歡用kv store?

一、為什么分布式數(shù)據(jù)庫這么喜歡用kv store雖然不論是單機(jī)數(shù)據(jù)庫(MySQL、PostgreSQL等等),還是題主說到的分布式數(shù)據(jù)庫(CockroachDB、TiDB)...詳情>>

2023-10-12 22:21:18
快速通道
久久亚洲中文字幕精品一区四,亚洲日本另类欧美一区二区,久久久久久久这里只有免费费精品,高清国产激情视频在线观看
亚洲字幕中文在线乱码AV | 中文字幕久久35一 | 亚洲成年看片在线观看 | 最新日本一区二区三区视频 | 午夜福利性爱片在线播放 | 午夜三A级免费在线视频 |