Ticket Servers: Distributed Unique Primary Keys on the Cheap(经济高效的分布式ID生成方案) 翻译

本文是对Ticket Servers: Distributed Unique Primary Keys on the Cheap https://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/ 的翻译,此文主要讲述了flicker公司的用于生成分布式ID的Ticker Servers 的背景、原理、具体方法、以及存在的问题。

翻译的文中,穿插着我的一些总结和思考,如果影响阅读,可以移步至原文,如果有错误, 请各位批评指正。

目录

Ticket Servers:经济高效的分布式ID生成方案

Ticket Servers本身并无特别之处,但它们是Flickr的重要构建模块,是稍后将讨论的核心主题,比如分片和主-主复制。Ticket Servers为的分布式系统提供了全局唯一的整数ID。

为什么使用Ticket Servers?

分片(也称为数据分区)是扩展Flickr数据存储的方式。不是将所有数据存储在一个非常大的数据库上,而是拥有许多数据库,每个数据库存储部分数据,并在它们之间分散负载。有时需要在数据库之间迁移数据,所以需要的主要键是全局唯一的。此外,的MySQL分片构建为主-主复制对,以提高弹性。这意味着需要能够保证在一个分片内的唯一性,以避免键冲突。很想像其他人一样继续使用MySQL自增列作为主键,但MySQL不能保证跨越物理和逻辑数据库的唯一性

为什么不用GUIDs?

考虑到需要全局唯一的ID,一个明显的问题是,为什么不使用GUIDs?主要是因为GUIDs体积大,并且在MySQL中索引效果不佳。保持MySQL快速的一种方式是对所有想要查询的内容进行索引,并且只对索引进行查询。因此,索引大小是一个关键考虑因素。如果你无法将索引保持在内存中,你就无法保持数据库的快速。此外,TicketServer为提供了序列性,这具有一些非常好的属性,包括使报告和调试更加直接,并使得一些缓存技巧成为可能。(GUID太大,非单调增,无法索引)

为什么不用一致性哈希?

一些项目,如亚马逊的Dynamo,在数据存储之上提供了一个一致性哈希环来处理GUID/分片问题。这更适合写入成本低的环境(例如LSMTs),而MySQL则针对快速随机读取进行了优化。

直接使用集中自增的方式有什么问题?

如果不能使MySQL自增功能跨越多个数据库工作,那么如果只使用一个数据库会怎样?如果每次有人上传照片时,都向这个数据库插入一个新行,那么就可以直接使用该表的自增ID作为所有数据库的主键。

当然,以每秒60多张照片的速度,那个表会变得非常大。可以去掉有关照片的所有额外数据,只在集中式数据库中保留ID。即便如此,该表仍然会迅速变得难以管理。而且还有评论、收藏、群组发帖、标签等等,这些都需要ID。(直接使用数据库自增属性,会导致表快速增大,不便管理)

借助REPLACE INTO 避免表过大的问题

大约十多年前,MySQL推出了一个非标准的扩展,对ANSI SQL规范进行了修改,即"REPLACE INTO"。后来,"INSERT ON DUPLICATE KEY UPDATE"出现了,并更好地解决了原始问题。然而,REPLACE INTO仍然得到了支持。

REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted.

REPLACE操作与INSERT几乎完全相同,不同的是,如果表中已有的行在主键或唯一索引上的值与新行的值相同,那么在插入新行之前,旧行将被删除。

这允许在数据库中原子性地更新一个单独的行,并获取一个新的自动递增的主键ID。(核心方法是借助REPLACE INTO插件,这种方式就避免表过大的问题,同时能获得自增的ID。切记,目的是获取唯一自增ID,而不是真的插入数据)

具体实施方法

Flickr的Ticket Servers是一个专用的数据库服务器,上面只有一个数据库,在该数据库中,有像Tickets32这样的表用于32位ID,以及Tickets64用于64位ID。

Tickets64的模式看起来像这样:

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=InnoDB

从Tickets64中选择*返回一个单独的行,看起来像这样:

+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+

当我需要一个新的全局唯一的64位ID时,执行以下SQL:

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

如何处理单点故障

如果不想让Ticket Servers成为一个单点故障。可以通过运行两个Ticket Servers来实现"高可用性"。在当前的写入/更新量下,服务器之间的复制会有问题,而锁定会严重影响网站的性能。但是,可以通过将ID空间一分为二,分别处理偶数和奇数,具体配置如下:

TicketServer1:

auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:

auto-increment-increment = 2
auto-increment-offset = 2

通过轮流使用两个服务器来进行负载均衡和处理停机时间。两边确实会有点不同步,目前奇数对象比偶数对象多出几十万个,但是目前看这并无大碍。(使用两个服务器,分别处理奇数和偶数,来避免单点故障问题)

更多的序列

实际上,在TicketServer上的表不仅仅是Tickets32和Tickets64。还有照片的序列、账户的序列、离线任务的序列和群组的序列等。离线任务有自己的序列,因为消耗了太多,不想不必要地增加其他事物的计数。群组和账户有自己的序列,因为得到的相对较少。照片有自己的序列,在切换时确保与旧的自增表同步,因为知道上传了多少照片是很好的,使用ID作为跟踪的简写。(可以为不同的实体分别设置不同的表,以增加区分度,提高可扩展性,这是flicker自己的使用逻辑)

总结

所以就是这样 它并不是特别优雅,但自从2006年1月13日(黑色星期五)投入生产以来,它对来说工作得惊人的好,是Flickr工程"会工作的最愚蠢事物"设计原则的一个很好的例子。(有缺点,但是能用)

相关推荐
ezreal_pan8 分钟前
kafka消费能力压测:使用官方工具
分布式·kafka
宽带你的世界9 分钟前
TiDB 是一个分布式 NewSQL 数据库
数据库·分布式·tidb
xiao-xiang20 分钟前
kafka-集群缩容
分布式·kafka
比花花解语23 分钟前
Kafka在Windows系统使用delete命令删除Topic时出现的问题
windows·分布式·kafka
解决方案工程师25 分钟前
【Kafka】Kafka高性能解读
分布式·kafka
yellowatumn27 分钟前
RocketMq\Kafka如何保障消息不丢失?
分布式·kafka·rocketmq
python资深爱好者1 小时前
什么容错性以及Spark Streaming如何保证容错性
大数据·分布式·spark
HeartRaindj2 小时前
【中间件开发】kafka使用场景与设计原理
分布式·中间件·kafka
明达技术3 小时前
探索分布式 IO 模块网络适配器
分布式
爬山算法5 小时前
Zookeeper(58)如何在Zookeeper中实现分布式锁?
分布式·zookeeper·云原生