分片(Sharding)的全局ID生成(二)

2014-11-24 02:36:01 · 作者: · 浏览: 4
ncrby命令得到。

例如,用户发一个文章,要生成一个文章ID,假定用户ID是14532,则

time <-- math.currentMiliseconds();
shardindId  <-- 14532 % 1024;     //即196
articleId <-- incrby idgenerator_next_196 1  //1是增长的步长
用lua脚本表示是:

local step = redis.call('GET', 'idgenerator_step');
local shardId = KEYS[1] % 1024;
local next = redis.call('INCRBY', 'idgenerator_next_' .. shardId, step);
return {math.currentMiliseconds(), shardId, next};
“idgenerator_step"这个key用来存放增长的步长。

客户端用eva l执行上面的脚本,得到三元组之后,可以自由组合成64bit的全局ID。

上面只是一个服务器,那么如何解决单点问题呢?

上面的“idgenerator_step"的作用就体现出来了。

比如,要部署三台redis做为ID生成服务器,分别是A,B,C。那么在启动时设置redis-A下面的键值:

idgenerator_step = 3
idgenerator_next_1, idgenerator_next_2, idgenerator_next_3 ... idgenerator_next_1024 = 1

设置redis-B下面的键值:

idgenerator_step = 3
idgenerator_next_1, idgenerator_next_2, idgenerator_next_3 ... idgenerator_next_1024 = 2

设置redis-C下面的键值:

idgenerator_step = 3
idgenerator_next_1, idgenerator_next_2, idgenerator_next_3 ... idgenerator_next_1024 = 3

那么上面三台ID生成服务器之间就是完全独立的,而且平等关系的。任意一台服务器挂掉都不影响,客户端只要随机选择一台去用eva l命令得到三元组即可。

我测试了下单台的redis服务器每秒可以生成3万个ID。那么部署三台ID服务器足以支持任何的应用了。

测试程序见这里:

https://gist.github.com/hengyunabc/9032295

缺点:

如果不熟悉lua脚本,可以定制自己的ID规则等比较麻烦。

优点:

非常的快,而且可以线性部署。

可以随意定制自己的Lua脚本,生成各种业务的ID。

其它的东东:

MongoDB的Objectid,这个实在是太长了要12个字节:

ObjectId is a 12-byte BSON type, constructed using:

a 4-byte value representing the seconds since the Unix epoch,
a 3-byte machine identifier,
a 2-byte process id, and
a 3-byte counter, starting with a random value.

总结:

生成全局ID并不很难实现的东东,不过从各个网络的做法,及演进还是可以学到很多东东。有时候一些简单现成的组件就可以解决问题,只是缺少思路而已。

参考:

http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/

http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram

https://github.com/twitter/snowflake/

http://docs.mongodb.org/manual/reference/object-id/

http://www.redisdoc.com/en/latest/script/eva l.html redis脚本参考