交互分布式系統(tǒng)下如何生成唯一序列
1 介紹
在常見(jiàn)的業(yè)務(wù)場(chǎng)景中,比如全局訂單Id,唯一標(biāo)識(shí)的支付編號(hào)等,都需要這個(gè)來(lái)保證。
那生成ID都有哪些解決方案呢?特別是在復(fù)雜的分布式系統(tǒng)業(yè)務(wù)場(chǎng)景中,我們應(yīng)該采用哪種解決方案來(lái)實(shí)現(xiàn)這個(gè)唯一序列呢?
一般來(lái)說(shuō),這個(gè)唯一序號(hào)有如下幾種特征:
全局唯一性:確保生成的序列是全局唯一的,不可重復(fù)。
有序性:確保生成的ID值對(duì)于某個(gè)用戶或者業(yè)務(wù)是按一定的數(shù)字有序遞增的。
高可用性:確保生成ID功能的高可用,能夠承接較大峰值,能夠保證序列生成的有效性(不重復(fù)且有序)。
帶時(shí)間標(biāo)記:ID中有時(shí)間片段組成,可是清晰識(shí)別出操作的時(shí)間。
下面是業(yè)內(nèi)幾種常見(jiàn)的分布式唯一序列生成方案,我們一一來(lái)介紹下。
2 數(shù)據(jù)庫(kù)自增
數(shù)據(jù)庫(kù)主鍵設(shè)置自增序號(hào) auto_increment,可以按照一定的趨勢(shì)自增,保證主鍵ID的唯一性。
這個(gè)方案簡(jiǎn)單易操作,優(yōu)點(diǎn)是明顯、可控。
但由于它是在數(shù)據(jù)庫(kù)的單表上進(jìn)行操作,對(duì)數(shù)據(jù)庫(kù)性能依賴比較明顯,高并發(fā)下的壓力也很大。所以不是唯一ID生成的最佳方法。
1 create table `t_generator_id` 2 ( 3 `id` bigint(20) not null auto_increment, -- 表示自增列 4 -- 其他字段信息 5 )
3 系統(tǒng)時(shí)間毫秒數(shù)
我們可以使用當(dāng)前系統(tǒng)時(shí)間精確到毫秒數(shù)(或者時(shí)間戳)+業(yè)務(wù)屬性+用戶屬性+隨機(jī)數(shù)+...等參數(shù)組合形式來(lái)確保ID的唯一性,缺點(diǎn)是ID的有序性難以保證,如果對(duì)有序性由強(qiáng)需求的業(yè)務(wù)不建議使用。
類似京東淘寶等電商的訂單號(hào)生成。因?yàn)橛唵翁?hào)和用戶id在業(yè)務(wù)上的區(qū)別,訂單號(hào)盡可能要多些冗余的業(yè)務(wù)信息,比如
滴滴:時(shí)間+起點(diǎn)編號(hào)+車牌號(hào) ; 淘寶訂單:時(shí)間戳+用戶ID,類似滴滴訂單的唯一序號(hào)如下:

4 UUID(GUID)
Java自帶的生成UUID的方式(.Net體系下也有GUID可以對(duì)應(yīng)),生成的是Length=32的16進(jìn)制格式的字符串,如果回退為byte數(shù)組共16個(gè)byte元素,即UUID是一個(gè)128bit長(zhǎng)的數(shù)字,一般用16進(jìn)制表示。
可以保證唯一性,但缺點(diǎn)是它不包含時(shí)間標(biāo)識(shí)、業(yè)務(wù)數(shù)據(jù)可讀性太差了,而且也不能ID的有序遞增。優(yōu)點(diǎn)生成方式,簡(jiǎn)單,高效,一般業(yè)務(wù)系統(tǒng)中比較少用。
5 批量預(yù)生成ID
1、在內(nèi)存(緩存)中,按需批量生成N個(gè)ID,并將最大ID值記錄到數(shù)據(jù)庫(kù)中。比如生成 1~10000,把max=10000持久化到數(shù)據(jù)庫(kù)中,內(nèi)存中記錄的是current=1和max=10000。
2、所有的使用都在內(nèi)存中進(jìn)行,每消耗一次序號(hào),current + 1。
3、當(dāng)current==max的時(shí)候,重復(fù)第一個(gè)步驟,再次批量生成 10001~20000的值,并將數(shù)據(jù)庫(kù)中的max改成20000。
優(yōu)點(diǎn)是避免了每次生成ID都要訪問(wèn)數(shù)據(jù)庫(kù)并帶來(lái)壓力。
缺點(diǎn)是只能是單點(diǎn)服務(wù),如果服務(wù)重啟勢(shì)必會(huì)造成ID丟失不連續(xù)的情況,而且這種方式也不利于水平擴(kuò)展。

6 Redis生成唯一序列
Redis可以使用簡(jiǎn)易的String類型,它的 incr/decr key 語(yǔ)法,支持高效快速的增減值,能夠保證生成的ID肯定是唯一有序的。
這種方式不依賴數(shù)據(jù)庫(kù)持久化,速度快,算是比較好的辦法了。但系統(tǒng)中引入Redis這一中間件,無(wú)形中增加維護(hù)成本。在超大流量、超高并發(fā)的情況下,單實(shí)例Redis還是無(wú)法滿足的,需要橫向擴(kuò)展Redis集群來(lái)進(jìn)行支撐。
1 <strong>incr</strong>/<strong>decr key</strong> // 自增減 1 2 <strong>incrby</strong>/<strong>decrby key</strong> increment // 自增減指定數(shù)值 3 <strong>incrbyfloat</strong>/<strong>decrbyfloat key</strong> increment // 自增減浮點(diǎn)數(shù)
還可以利用像Zookeeper中的znode數(shù)據(jù)版本來(lái)生成序列號(hào),及MongoDB的ObjectId等,但是性能不如Redis,不是很推薦。
7 snowflake算法
Twitter在把存儲(chǔ)系統(tǒng)從MySQL遷移到Cassandra的過(guò)程中由于Cassandra沒(méi)有順序ID生成機(jī)制,于是自己開(kāi)發(fā)了一套全局唯一ID生成服務(wù):Snowflake。

如上圖的所示,Twitter的snowflake算法下面幾部分組成:
41位的時(shí)間序列,精確到毫秒,可以使用69年
10位的機(jī)器標(biāo)識(shí),最多支持部署1024個(gè)節(jié)點(diǎn)
12位的序列號(hào),支持每個(gè)節(jié)點(diǎn)每毫秒產(chǎn)生4096個(gè)ID序號(hào),最高位是符號(hào)位始終為0。
這種方案性能好,在單機(jī)上是遞增的,但是由于涉及到分布式環(huán)境,每臺(tái)機(jī)器上的時(shí)鐘不可能完全同步,也許有時(shí)候也會(huì)出現(xiàn)不是全局遞增的情況。
而且這個(gè)項(xiàng)目在2010就停止維護(hù)了,但這個(gè)設(shè)計(jì)思路被很多廠家參考,應(yīng)用于各個(gè)業(yè)務(wù)的ID生成器及變種。
8 UidGenerator
UidGenerator是百度開(kāi)源的一款分布式高性能的唯一ID生成器,使用Java實(shí)現(xiàn)的, 基于Snowflake算法的唯一ID生成器。
在實(shí)現(xiàn)上, UidGenerator通過(guò)借用未來(lái)時(shí)間來(lái)解決sequence天然存在的并發(fā)限制; 采用RingBuffer來(lái)緩存已生成的UID, 并行化UID的生產(chǎn)和消費(fèi), 同時(shí)對(duì)CacheLine補(bǔ)齊,避免了由RingBuffer帶來(lái)的硬件級(jí)「?jìng)喂蚕怼箚?wèn)題. 最終單機(jī)QPS可達(dá)600萬(wàn)。
具體的GitHub地址如下:
https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md
9 Leaf
Leaf是美團(tuán)開(kāi)源的分布式ID生成器,能保證全局唯一性、趨勢(shì)遞增、單調(diào)遞增、信息安全,同時(shí)也需要依賴關(guān)系數(shù)據(jù)庫(kù)、Zookeeper等中間件。
美團(tuán)技術(shù)社區(qū)有詳細(xì)的說(shuō)明,同時(shí)也對(duì)分布式ID生成有一些比較好的分析和建議:http://www.dhdzp.com/article/235968.htm
10 總結(jié)
個(gè)人覺(jué)得最好的是Redis方案和snowflake算法,無(wú)論是性能還是可用性程度上。另外各大廠也有自己的一些做法,比如百度的UidGenerator 和 美團(tuán)的Leaf,
主要也是根據(jù)現(xiàn)有的方案進(jìn)行優(yōu)化和改造,達(dá)到比較契合他們自己業(yè)務(wù)的目標(biāo)。
以上就是交互分布式系統(tǒng)下如何生成唯一序列的詳細(xì)內(nèi)容,更多關(guān)于交互分布式系統(tǒng)下的唯一序列的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mssql數(shù)據(jù)同步實(shí)現(xiàn)數(shù)據(jù)復(fù)制的步驟
需要用到mssql數(shù)據(jù)同步的朋友可以參考本文和上一篇文章2008-09-09
DophinScheduler定期刪除日志實(shí)例代碼
Apache DophinScheduler 運(yùn)行一段時(shí)間后,實(shí)例調(diào)度日志越來(lái)越多,需要定期清理,這篇文章主要介紹了DophinScheduler定期刪除日志實(shí)例代碼,需要的朋友可以參考下2014-04-04
深入理解數(shù)據(jù)庫(kù)之表的唯一、自增等七大約束
真正約束字段的是數(shù)據(jù)類型,但是數(shù)據(jù)類型約束很單一,需要有一些額外的約束,更好的保證數(shù)據(jù)的合法性,從業(yè)務(wù)邏輯角度保證數(shù)據(jù)的正確性,本文就來(lái)介紹一下數(shù)據(jù)庫(kù)之表的唯一、自增等七大約束,感興趣的可以了解一下2023-09-09
Doris?數(shù)據(jù)模型ROLLUP及前綴索引官方教程
本文檔主要從邏輯層面,描述 Doris 的數(shù)據(jù)模型 ROLLUP 以及前綴索引的概念,以幫助用戶更好的使用 Doris 應(yīng)對(duì)不同的業(yè)務(wù)場(chǎng)景,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
Access轉(zhuǎn)成SQL數(shù)據(jù)庫(kù)的方法
很多朋友想用SQL2000數(shù)據(jù)庫(kù)的編程方法,但是卻又苦于自己是學(xué)ACCESS的,對(duì)SQL只是一點(diǎn)點(diǎn)的了解而已,這里我給大家提供以下參考---將ACCESS轉(zhuǎn)化成SQL2000的方法和注意事項(xiàng)。2015-09-09
數(shù)據(jù)庫(kù) SQL千萬(wàn)級(jí)數(shù)據(jù)規(guī)模處理概要
我在前年遇到過(guò)過(guò)億條的數(shù)據(jù)。以至于一個(gè)處理過(guò)程要幾個(gè)小時(shí)的。后面慢慢優(yōu)化,查找一些經(jīng)驗(yàn)文章。才學(xué)到了一些基本方法。綜合敘之,與君探討之。2009-07-07
SQL SERVER 里的錯(cuò)誤處理(try catch)
SQL SERVER里,也有TRY CATCH。格式如下2009-02-02
SQL 隨機(jī)查詢 包括(sqlserver,mysql,access等)
SQL 隨機(jī)查詢 包括(sqlserver,mysql,access等),需要的朋友可以參考下,目的一般是為了隨機(jī)讀取數(shù)據(jù)庫(kù)中的記錄。2009-10-10
mongoDB和mysql對(duì)比分析及選擇(詳細(xì)版)
這篇文章主要介紹了mongoDB和mysql對(duì)比分析及選擇(詳細(xì)版),需要的朋友可以參考下2023-06-06

