聊聊 ID Generator 的实现和应用

发号器是互联网技术场景中经常需要的一种技术实践,本文将讨论几种典型的发号器的实现以及应用。

为什么需要一个发号器

需要保证数据全局唯一,典型如数据库的分库分表场景中需要数据的主键始终保持唯一,因为分库分表很可能会夸物理机甚至夸机房。

大公司是如何做的

flickr

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

flickr 在自己的数据库业务场景中设计了一个centralized 发号器,这个发号器利用了 mysql 的 auto increment 和 replace :

MySQL uses the following algorithm for REPLACE (and LOAD DATA ... REPLACE):

  1. Try to insert the new row into the table
  2. While the insertion fails because a duplicate-key error occurs for a primary key or unique index:
    1. Delete from the table the conflicting row that has the duplicate key value
    2. Try again to insert the new row into the table
1
REPLACE into tag (id, name) values (2, 'aaaa');

再结合 LAST_INSERT_ID 函数就能取得一个自增的全局唯一的 id

具体执行先建立一个只有一条记录的 ticket table

1
2
3
4
5
6
CREATE TABLE `tickets64` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`stub` char(1) NOT NULL default '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM

在需要号码时执行下列语句:

1
2
REPLACE INTO tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

为了防止单点故障,flickr 在设计中使用了两台独立的 database 实例作为发号器,分布设置不同步长

1
2
3
4
5
6
7
TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1
TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

这样就保证了从不同发号器获得的号码也是唯一的,同时可防止单点,保证了高可用。

评价

利用 MySQL 的自增主键实现的分布式发号器非常简单,但是由于步长固定容易猜测,而且不易扩展,比如

如果有两台发号器,一台初始值是 2,步长是 2,一台初始值是 1 步长是 2,两台发号器分别发号对应的是双数发号器和单号发号器,如果再进行扩展就会变得比较麻烦了,因为单双号分离变得麻烦。

twitter

待续

三月沙 wechat
扫描关注 wecatch 的公众号