从MySQL到HBase
Imgur是MySQL的重度用户。MySQL从一开始就成为了我们技术栈的一部分。然而,随着我们的发展,在MySQL上开发新功能变得越来越困难。比如我们最近的一次功能升级,我们重新实现了通知系统,并且把数据从MySQL迁移到了HBase。在接下来的文章中,我会介绍我们是如何用HBase来解决我们的问题的,还有我们用到的HBase的特性。
先稍微说下背景情况,以前我们支持两种通知类型:消息和回复,都存储在MySQL。这次升级,我们决定支持更多的通知类型。并且引入了一些规则来规定什么时候来发送通知。这样的改变让我们很难在之前的模型上继续开发,所以我们重新开始开发。
一开始,在设计阶段,我们希望继续保持MySQL作为主要存储。我们合并了一些表和查询,来防止我们出现巨大的错误。我们不得不为没一个通知类型创建一个列,之后要添加一种类型的话就意味着要改表。我们的select查询需要和其他应用的表进行join。我们设计了一个可以用的架构,但是我们牺牲了解偶,简单,可伸缩,可扩展的特性。
我们其中的一些通知要求只是在到达里程碑的时候进行发送。比如,如果一篇文章达到了100分,Imgur会通知用户。我们不希望在这中间的99次去打扰用户。所以,在大规模的数据中,我们怎么知道一篇文章达到了100分?
考虑到MySQL是我们文章的主要存储,一种实现的方法就是在文章表里加一个分数列,然后通过另外一个查询来获取数据,判断是否达到100分。这个实现有些问题。分数增长和查询是一个竞争条件,可能会导致两个客户端都认为他们到达了100分,然后发送两条通知。另外一个问题就是产生了而外的查询。每一次递增,我们都需要查询分数,增加了MySQL的负载。
虽然MySQL在技术上可以实现,通过使用事务和读锁。但是作为我们网站上一个高频的应用,锁会消耗很多的资源。考虑到我们已经在其他地方使用了HBase,于是我们把这个功能切换到了用HBase来实现。下面介绍,我们是如何使用HBase来强化我们的通知系统,在大规模下保持了实时性。
稀疏列(Sparse columns)
在Imgur里,每条通知是由一个或多个事件组成。下面的图片可以看到我们的通知是如何展示的。
可以看到,每一条通知是由一个或多个事件组成。我们把一条通知对应的写到HBase的一行里,同时每一个事件对应到不同的列,每一个列都是一个计数器。这样的模型让我们的列非常的稀疏,不同类型的通知有不同类型的事件。在MySQL里,这样做的话就意味着有很多为NULL值的列存在。我们没有被禁锢在一个严格定义的表结构里,所以我们可以在现有的模型上轻松的添加通知类型。
原子自增(Atomic increments)
HBase有原子自增的操作,可以在自增的同时返回自增后的新值。这虽然是个简单的特性,但是我们的实现就是依赖这个特性的。这让我们在客户端的通知发送逻辑变得轻量:自增之后当且仅当数量超过里程碑的时候会发送通知。没有额外的调用开销。在一些场景中,我们有了两个计数的地方。一个在MySQL文章表里,一个在HBase里。技术上来说,他们可能会出现数据同步问题,但是这是一个我们在改进的案例。
在HBase里做自增的另一个好处就是可以让我们把通知的逻辑和应用逻辑解偶。我们需要知道就只是计数器是否超过了设置的阈值。比如,我们不再需要知道怎么去获取文章,然后拿到计数的数据。HBase让我们用更通用的方式解决了我们的问题。
扫描表的速度快
我们同时还维护了一个二级的排序表。它存放了通知的引用,并且以他们最后一次发送的逆时间戳排序的。当用户打开他们的通知下拉列表的时候,我们通过用户ID查询到他最新的消息。我们还可以通过使用扫描过滤器支持扫描不同类型的通知。
其他优势
使用HBase让我们得到了很多其他的好处,比如线性扩展和副本。我们可以把数据发送到另外一个集群来跑很多任务。我们还得到了强一致性的保证,这对保证我们的通知系统在达到里程碑的时候发且只发一次消息极其重要。我们可以保证我们所知道的HBase有行级的强一致性。虽然我们将来可能会用到versioning功能,但是即使不用它,对我们来说HBase依然是一个很好的选择。
Imgur通知系统是一个年轻的项目,我们会继续改进它。随着它的成长我们也从中学习到新的知识,同时我希望把我们用开源软件开发的产品分享给大家。
原文地址: https://medium.com/imgur-engineering/imgur-notifications-from-mysql-to-hbase-9dba6fc44183