scadb的唯一ID产生原理

Scadb是一个分布式MySQL中间件,目前它支持的SQL语句是MySQL的一个子集,而且也没打算创造独特的语法出来,这样方便业务方可以在MySQL和Scadb之间无缝的切换。那么Scadb是否能够支持自增id呢?

一、Mongodb的ObjectID的启示

我们首先在Mongodb中产生一个ObjectID,

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

Mongodb没有支持自增id,缺省的文档都会产生一个ObjectID,是唯一ID,Mongodb的ObjectID格式如下所示,一个ObjectID主要由4个部分组成:

95----------63---------39--------23-----0
—————————————^
时间戳 --------机器 -----进程 --自增计数器

    1. 时间戳:将刚才生成的objectid的前4位进行提取“59197e92”,十进制为1494843026,这是我们产生数据时候的时间戳;
      
    1. 机器:接下来的三个字节就是“3879ad”,这三个字节是主机信息,一般的实现为组合所有的网卡信息成一个字符串,然后进行计算散列值;
    1. PID:上面的机器信息是为了确保在不同机器产生的objectId不冲突,而pid就是为了在同一台机器不同的mongodb进程产生了objectId不冲突,接下来的“7925”两位就是产生objectId的进程标识符,我们知道linux操作系统进程id是int型,所以两个字节是存储不下的,mongodb这里也是采用了进程信息的散列值然后取2个byte。
    1. 自增计数器:前面的九个字节是保证了一秒内不同机器不同进程生成objectId不冲突,这后面的三个字节“b2a3f0”是一个自动增加的计数器,用来确保在同一秒内产生的objectId也不会发现冲突,只要一个进程一秒内产生的数据少于16777216,就能保证记录的唯一性。

ObjectId的这个主键生成策略,很好地解决了在分布式环境下高并发情况主键唯一性问题,值得学习借鉴。Scadb也是一种分布式数据库,无法实现自增id。那么在设计之初,本来也考虑参考Mongodb的唯一ID方案,但是ObjectID是一个12个字节的数值,而Scadb是MySQL的中间件,MySQL没有一个一种合适的类型的来支持12个字节,当然,我们可以考虑用字符串,但是也从性能方面考虑,于是决定要采用整形BIGINT来存储唯一ID。

二、Scadb如何支持唯一ID

从上面的分析我们知道,Mongodb采用了3个byte存储机器ID,2个字节存储进程ID,就是为了保证每一个启动的Mongodb的进程能够产生一个唯一的进程信息(其实根据前面讲解的原理,还是无法100%保证进程标识的唯一性);

与此同时Scadb还要把整个ID号存储在一个BIGINT也就是8个字节的整数中,所以这种方案行不通。但是思路可以借鉴,我们只需要保证每个启动的进程有一个唯一的ID号。

进程号生成

由于Scadb的server进程不是多租户模式,也即是说所以我们只需要保证服务于一个业务的Scadb Server进程能够有一个唯一的进程号即可。Scadb的配置是基于Zookeeper的,这样我们很容易基于Zookeeper来生成一个唯一的进程号。
假设现在我们服务的业务名是test,那么它在zookeeper上的主路径是:/scadb/businesses/test,创建一个子目录:/scadb/businesses/test/runners。
在该子目录下,我们让每个Scadb server在启动的时候,在/scadb/businesses/test/runners目录下创建一个临时节点,节点创建成功,那么这个节点就作为Scadb server的进程号。由于是临时节点,一旦进程被杀死或者因为某种原因挂了,那么这个节点也会自动消失。
具体的逻辑为:

1、启动时,随机一个整数runnerId;
2、创建临时节点:/scadb/businesses/test/runners/${runnerId}
3、如果创建成功,结束;否则runnerId = (runnerId + 1) % 512,重复第2步。

唯一ID生成

有了进程号以后,那么唯一ID的生成就很简单了,Scadb的唯一ID的格式为:

63-62---------30----28------19--------- 0
————————————
预留 时间戳 预留 进程号 自增计数器

最高位(63):我预留了,主要是因为Java等语言对无符号64位整数支持不友好,所以这一位没有使用;
时间戳(31-62):占用4个字节;
预留2位(30,29):主要考虑到以后其他的可扩展的功能;
进程号(20-28):占用了9位,每个业务可以支持512个进程,我压测过一个scadb可以支持5万次的普通数据库访问操作,所以512个进程对于一个业务来说够用了;
自增计数器(0-19):占用了20位,可以支持每秒1048576次不同值的生成,对于一个进程来说,这个数已经是非常高了。

具体代码不在这里列举了,大家可以参考源码中的ScadbUUID类。

三、Scadb唯一ID实战

首选我们创建一个有自增字段的表,如下所示:

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语法的统一性,这里仍然采用了AUTO_INCREMENT,这样b表就是一个支持唯一ID的分区表了;
  • 2.这里自增字段只能是bigint型,不支持int型;

我们可以通过下面的语法来查看表的结构:

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)

现在我们插入一条记录:

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

由于插入时没有指定ID,那么Scadb会自动为我们产生一个ID号,我们查询一下上次产生的自增ID:

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

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

现在我们根据该ID号查询一下,Scadb产生的整条记录:

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

四、最后

唯一ID不不仅仅数据库中需要用到,在业务开发中,我们也经常会碰到需要产生唯一ID的场景,希望本文的原理能够帮助到大家。

另外如果想了解scadb的架构,请阅读:scadb架构介绍

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 一,题记 所有的业务系统,都有生成ID的需求,如订单id,商品id,文章ID等。这个ID会是数据库中的唯一主键,在...
    架构师小秘圈阅读 4,035评论 1 18
  • 什么是数据库? 数据库是存储数据的集合的单独的应用程序。每个数据库具有一个或多个不同的API,用于创建,访问,管理...
    chen_000阅读 4,026评论 0 19
  • 背景: 阅读新闻 12C CDB模式下RMAN备份与恢复 [日期:2016-11-29] 来源:Linux社区 作...
    阳屯okyepd阅读 3,309评论 0 7
  • 最近在proxy的分库分表,需要给表中的主字段产生一个全局唯一ID,考虑到后期DBA会拿这个ID做索引的,所以产生...
    luomoxyz阅读 3,205评论 0 4
  • Entrance 数据在分片时,典型的是分库分表,就有一个全局ID生成的问题。 单纯的生成全局ID并不是什么难题,...
    天下无敌强阅读 2,251评论 2 5