聊聊 ID Generator 的实现和应用

2018-03-12 nicolashope58

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

为什么需要一个发号器

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

大公司是如何做的

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
REPLACE into tag (id, name) values (2, 'aaaa');

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

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

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

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

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

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

TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

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

评价

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

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