scadb的唯一ID產(chǎn)生原理

Scadb是一個(gè)分布式MySQL中間件,目前它支持的SQL語句是MySQL的一個(gè)子集,而且也沒打算創(chuàng)造獨(dú)特的語法出來,這樣方便業(yè)務(wù)方可以在MySQL和Scadb之間無縫的切換。那么Scadb是否能夠支持自增id呢?

一、Mongodb的ObjectID的啟示

我們首先在Mongodb中產(chǎn)生一個(gè)ObjectID,

》 db.test.insert({foo:'bar'})
WriteResult({ "nInserted" : 1 })
》db.test.findOne()
{ "_id" : ObjectId("59197e923879ad7925b2a3f0"), "foo" : "bar" }

Mongodb沒有支持自增id,缺省的文檔都會(huì)產(chǎn)生一個(gè)ObjectID,是唯一ID,Mongodb的ObjectID格式如下所示,一個(gè)ObjectID主要由4個(gè)部分組成:

95----------63---------39--------23-----0
—————————————^
時(shí)間戳 --------機(jī)器 -----進(jìn)程 --自增計(jì)數(shù)器

    1. 時(shí)間戳:將剛才生成的objectid的前4位進(jìn)行提取“59197e92”,十進(jìn)制為1494843026,這是我們產(chǎn)生數(shù)據(jù)時(shí)候的時(shí)間戳;
      
    1. 機(jī)器:接下來的三個(gè)字節(jié)就是“3879ad”,這三個(gè)字節(jié)是主機(jī)信息,一般的實(shí)現(xiàn)為組合所有的網(wǎng)卡信息成一個(gè)字符串,然后進(jìn)行計(jì)算散列值;
    1. PID:上面的機(jī)器信息是為了確保在不同機(jī)器產(chǎn)生的objectId不沖突,而pid就是為了在同一臺(tái)機(jī)器不同的mongodb進(jìn)程產(chǎn)生了objectId不沖突,接下來的“7925”兩位就是產(chǎn)生objectId的進(jìn)程標(biāo)識(shí)符,我們知道linux操作系統(tǒng)進(jìn)程id是int型,所以兩個(gè)字節(jié)是存儲(chǔ)不下的,mongodb這里也是采用了進(jìn)程信息的散列值然后取2個(gè)byte。
    1. 自增計(jì)數(shù)器:前面的九個(gè)字節(jié)是保證了一秒內(nèi)不同機(jī)器不同進(jìn)程生成objectId不沖突,這后面的三個(gè)字節(jié)“b2a3f0”是一個(gè)自動(dòng)增加的計(jì)數(shù)器,用來確保在同一秒內(nèi)產(chǎn)生的objectId也不會(huì)發(fā)現(xiàn)沖突,只要一個(gè)進(jìn)程一秒內(nèi)產(chǎn)生的數(shù)據(jù)少于16777216,就能保證記錄的唯一性。

ObjectId的這個(gè)主鍵生成策略,很好地解決了在分布式環(huán)境下高并發(fā)情況主鍵唯一性問題,值得學(xué)習(xí)借鑒。Scadb也是一種分布式數(shù)據(jù)庫,無法實(shí)現(xiàn)自增id。那么在設(shè)計(jì)之初,本來也考慮參考Mongodb的唯一ID方案,但是ObjectID是一個(gè)12個(gè)字節(jié)的數(shù)值,而Scadb是MySQL的中間件,MySQL沒有一個(gè)一種合適的類型的來支持12個(gè)字節(jié),當(dāng)然,我們可以考慮用字符串,但是也從性能方面考慮,于是決定要采用整形BIGINT來存儲(chǔ)唯一ID。

二、Scadb如何支持唯一ID

從上面的分析我們知道,Mongodb采用了3個(gè)byte存儲(chǔ)機(jī)器ID,2個(gè)字節(jié)存儲(chǔ)進(jìn)程ID,就是為了保證每一個(gè)啟動(dòng)的Mongodb的進(jìn)程能夠產(chǎn)生一個(gè)唯一的進(jìn)程信息(其實(shí)根據(jù)前面講解的原理,還是無法100%保證進(jìn)程標(biāo)識(shí)的唯一性);

與此同時(shí)Scadb還要把整個(gè)ID號(hào)存儲(chǔ)在一個(gè)BIGINT也就是8個(gè)字節(jié)的整數(shù)中,所以這種方案行不通。但是思路可以借鑒,我們只需要保證每個(gè)啟動(dòng)的進(jìn)程有一個(gè)唯一的ID號(hào)。

進(jìn)程號(hào)生成

由于Scadb的server進(jìn)程不是多租戶模式,也即是說所以我們只需要保證服務(wù)于一個(gè)業(yè)務(wù)的Scadb Server進(jìn)程能夠有一個(gè)唯一的進(jìn)程號(hào)即可。Scadb的配置是基于Zookeeper的,這樣我們很容易基于Zookeeper來生成一個(gè)唯一的進(jìn)程號(hào)。
假設(shè)現(xiàn)在我們服務(wù)的業(yè)務(wù)名是test,那么它在zookeeper上的主路徑是:/scadb/businesses/test,創(chuàng)建一個(gè)子目錄:/scadb/businesses/test/runners。
在該子目錄下,我們讓每個(gè)Scadb server在啟動(dòng)的時(shí)候,在/scadb/businesses/test/runners目錄下創(chuàng)建一個(gè)臨時(shí)節(jié)點(diǎn),節(jié)點(diǎn)創(chuàng)建成功,那么這個(gè)節(jié)點(diǎn)就作為Scadb server的進(jìn)程號(hào)。由于是臨時(shí)節(jié)點(diǎn),一旦進(jìn)程被殺死或者因?yàn)槟撤N原因掛了,那么這個(gè)節(jié)點(diǎn)也會(huì)自動(dòng)消失。
具體的邏輯為:

1、啟動(dòng)時(shí),隨機(jī)一個(gè)整數(shù)runnerId;
2、創(chuàng)建臨時(shí)節(jié)點(diǎn):/scadb/businesses/test/runners/${runnerId}
3、如果創(chuàng)建成功,結(jié)束;否則runnerId = (runnerId + 1) % 512,重復(fù)第2步。

唯一ID生成

有了進(jìn)程號(hào)以后,那么唯一ID的生成就很簡單了,Scadb的唯一ID的格式為:

63-62---------30----28------19--------- 0
————————————
預(yù)留 時(shí)間戳 預(yù)留 進(jìn)程號(hào) 自增計(jì)數(shù)器

最高位(63):我預(yù)留了,主要是因?yàn)镴ava等語言對(duì)無符號(hào)64位整數(shù)支持不友好,所以這一位沒有使用;
時(shí)間戳(31-62):占用4個(gè)字節(jié);
預(yù)留2位(30,29):主要考慮到以后其他的可擴(kuò)展的功能;
進(jìn)程號(hào)(20-28):占用了9位,每個(gè)業(yè)務(wù)可以支持512個(gè)進(jìn)程,我壓測過一個(gè)scadb可以支持5萬次的普通數(shù)據(jù)庫訪問操作,所以512個(gè)進(jìn)程對(duì)于一個(gè)業(yè)務(wù)來說夠用了;
自增計(jì)數(shù)器(0-19):占用了20位,可以支持每秒1048576次不同值的生成,對(duì)于一個(gè)進(jìn)程來說,這個(gè)數(shù)已經(jīng)是非常高了。

具體代碼不在這里列舉了,大家可以參考源碼中的ScadbUUID類。

三、Scadb唯一ID實(shí)戰(zhàn)

首選我們創(chuàng)建一個(gè)有自增字段的表,如下所示:

mysql> /!scadb:partitionkey=id rule=rule10/CREATE TABLE b (
-> id bigint(20) NOT NULL AUTO_INCREMENT,
-> name varchar(50) COLLATE utf8_bin DEFAULT NULL,
-> t datetime DEFAULT NULL,
-> PRIMARY KEY (id)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
-> ;
Query OK, 0 rows affected (0.92 sec)

注意:

  • 1.雖然Scadb不支持唯一ID,但是為了保持與MySQL語法的統(tǒng)一性,這里仍然采用了AUTO_INCREMENT,這樣b表就是一個(gè)支持唯一ID的分區(qū)表了;
  • 2.這里自增字段只能是bigint型,不支持int型;

我們可以通過下面的語法來查看表的結(jié)構(gòu):

mysql> show create table b ;
+-------+------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------+
| b | /!scadb:partitionkey=id rule=rule10/CREATE TABLE b (
id bigint(20) NOT NULL AUTO_INCREMENT,

name varchar(50) COLLATE utf8_bin DEFAULT NULL,
t datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin |
+-------+----------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

現(xiàn)在我們插入一條記錄:

mysql> insert into b ( name ) values ( '123') ;
Query OK, 1 row affected (0.12 sec)

由于插入時(shí)沒有指定ID,那么Scadb會(huì)自動(dòng)為我們產(chǎn)生一個(gè)ID號(hào),我們查詢一下上次產(chǎn)生的自增ID:

mysql> select last_insert_id() ;
+---------------------+
| last_insert_id() |
+---------------------+

| 3208787689176782285 |
+---------------------+
1 row in set (0.03 sec)

現(xiàn)在我們根據(jù)該ID號(hào)查詢一下,Scadb產(chǎn)生的整條記錄:

mysql> select * from b where id = 3208787689176782285 ;
+---------------------+------+------+
| id | name | t |
+---------------------+------+------+
| 3208787689176782285 | 123 | NULL |
+---------------------+------+------+
1 row in set (0.07 sec)

四、最后

唯一ID不不僅僅數(shù)據(jù)庫中需要用到,在業(yè)務(wù)開發(fā)中,我們也經(jīng)常會(huì)碰到需要產(chǎn)生唯一ID的場景,希望本文的原理能夠幫助到大家。

另外如果想了解scadb的架構(gòu),請(qǐng)閱讀:scadb架構(gòu)介紹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一,題記 所有的業(yè)務(wù)系統(tǒng),都有生成ID的需求,如訂單id,商品id,文章ID等。這個(gè)ID會(huì)是數(shù)據(jù)庫中的唯一主鍵,在...
    架構(gòu)師小秘圈閱讀 4,149評(píng)論 1 18
  • 什么是數(shù)據(jù)庫? 數(shù)據(jù)庫是存儲(chǔ)數(shù)據(jù)的集合的單獨(dú)的應(yīng)用程序。每個(gè)數(shù)據(jù)庫具有一個(gè)或多個(gè)不同的API,用于創(chuàng)建,訪問,管理...
    chen_000閱讀 4,146評(píng)論 0 19
  • 背景: 閱讀新聞 12C CDB模式下RMAN備份與恢復(fù) [日期:2016-11-29] 來源:Linux社區(qū) 作...
    陽屯okyepd閱讀 3,860評(píng)論 0 7
  • 最近在proxy的分庫分表,需要給表中的主字段產(chǎn)生一個(gè)全局唯一ID,考慮到后期DBA會(huì)拿這個(gè)ID做索引的,所以產(chǎn)生...
    luomoxyz閱讀 3,395評(píng)論 0 4
  • Entrance 數(shù)據(jù)在分片時(shí),典型的是分庫分表,就有一個(gè)全局ID生成的問題。 單純的生成全局ID并不是什么難題,...
    天下無敵強(qiáng)閱讀 2,315評(píng)論 2 5

友情鏈接更多精彩內(nèi)容