周一上午,我正坐在办公桌前,一边喝着咖啡一边等待大脑苏醒。突然间,销售总监走过来拍了拍我的肩膀。

“今天是个大日子,我们最主要的发布商要切换到我们的附属平台上。你们做好准备了吗?”

我坐在那里,心里想着:“我们根本就不知道这件事啊!”雪上加霜的是,我的经理还去休陪产假了,近两个月来,我一直都在充当技术团队领导的角色。

我对销售总监说,我根本就不知道这次迁移的事情。

我看到他明显颤抖了一下。

于是我对他说:“别担心,应该没什么事。技术团队已经做过很多次内部平台迁移了。”

就这样,噩梦般的一周来临了。

决堤的洪水

那个发布商按下了切换的按钮。

我在屏幕前等待,其他人也站在周围等待。但是什么都没有发生。

人们开始变得焦急,因为屏幕上的报告窗口依然是空白一片。

我在Slack上联系了一些同事,我意识到发布商的网站没有完成配置,这意味着所有流量都被mark为失败,直接进入了failed log。

还好,数据没有丢。我只需要花时间配置一下,然后在failed log找回数据就好了。

经过配置之后,数据开始从failed log中恢复。

流量事件翻了三倍。

这个简单的索引错误差点让我们阴沟翻船0

流量聚合也翻了三倍。

这个简单的索引错误差点让我们阴沟翻船1

我靠!主数据库的CPU使用率达到了90%!

这个简单的索引错误差点让我们阴沟翻船2

据说硬件能解决一切问题

我们的基础设施是多个运行Kafka、Hadoop、Yarn和Sanza的机器。AWS上运行的是ElastiCache和Postgres数据库。另外,客户的页面会推送JavaScript事件给我们。Schedulers负责在背后拉取数据。

基础设施架构概览视图

这个简单的索引错误差点让我们阴沟翻船3

继续投入硬件

现在我们的主要工作,就是重新掌握系统的控制。在确定问题出现的原因之前,我们能做的,就是投入更多硬件。

技术团队决定升级主数据库的instance,直至AWS所支持的最大值。在升级之后,我们的确看到了一些改善,但是并不足以解决问题。

这个简单的索引错误差点让我们阴沟翻船4

单纯投入硬件无法解决问题,以为9.5版本的PostgreSQL为single-threaded。而在PostgreSQL 9.6中,它支持平行请求,但是升级PostgreSQL,只能延缓问题的蔓延速度,无法解决问题。

不合时宜的索引 = N² vs N Log (N

这个时候你可能会觉得table上根本就没有任何索引。但是我们发现,时间栏上的确有一个索引。

现在的问题是,如果你不理解数据进入特定栏的方式,那么索引就是毫无意义的。

在聚合层上,PostgreSQL必须要检查一个小时内是否已经有一个存在的聚合,或者是否有一个新的聚合。

--stamp column is indexed 
SELECT aggregated_id from lvl2_unique_traffic.unique_traffic_201611 WHERE 
stamp = ‘20161101 21:00:00AND 
affiliate_id = 123 AND
affiliate_domain_id = 456 AND
campaign_id = 789 AND
network_id = 1 AND
site_id = 1 AND
entry_point_id = 389 AND
program_id = 4 AND
country_code = ‘US’ AND
state_prov = ‘DE’ AND
city = ‘Wilmington’ AND
ad_id = ‘0AND
device_is_mobile = ‘falseAND
device_os = ‘Windows NT 4.0AND
device_browser = ‘Chrome’ AND
device_type = ‘Desktop’

上面的请求看上去很无辜,但是在运行了一个explain之后,问题就出现了。

--stamp column is indexed.
SELECT aggregated_id from lvl2_unique_traffic.unique_traffic_201611 WHERE
stamp = ‘20161101 21:00:00’
--Count: ~50,000 rows 

我意识到,所有数据都被聚合在一起了,这让索引变得毫无意义。在请求应用索引之后,它必须使用过滤器在~50,000 rows上运行WHERE从句。而且它必须要再每一个聚合上使用这个从句。

使用了大O符号之后,这个请求实际上使用了N²,而不是索引所提供的标准的N log(N)。

下面这个图标可以让你更清楚的看到问题:

这个简单的索引错误差点让我们阴沟翻船5

我们怎么会忽略这么简单的事情?

传说中,有一天毕加索正在公园里画画,一个女人走上前来,请毕加索给她画一张肖像。

这个女人对毕加索画的肖像非常满意,想要买下它。

5000块。毕加索回答说。

这个女人十分不解:一张画怎么会这么贵?你只用了几分钟就画完了啊!

毕加索回答道:女士,我花了一生的时间,才让自己有了这样的能力。

有经验的开发者了解索引必须要减少数据的总量,需要对其进行观察和过滤。他们知道瓶颈在哪里,也了解使用索引的后果,以及如何修复这些问题——这些其实都不难懂。

但是当基础设施较为复杂的时候,这些简单的概念也会变得复杂。

基础设施详细视图

这个简单的索引错误差点让我们阴沟翻船6

如果你仔细看看我们的基础设施的复杂程度,或许你就能了解我们为何会犯这样的错误。

所有人都会犯错

写这篇文章的过程中,我的一个朋友也遇到了类似的问题。在开始使用索引的时候,他们忘记了把索引添加到特定栏中,以至于数据库CPU用量飙升。

这个简单的索引错误差点让我们阴沟翻船7

事后分析

通过调整索引解决了问题

这个简单的索引错误差点让我们阴沟翻船8

教训

沟通是关键

实现获取客户的指标,了解需求的增加程度

从小部分开始迁移,不要一次性进行整体迁移

了解你正在索引的数据

余下全文(1/3)

本文最初发表在www.sdk.cn,文章内容属作者个人观点,不代表本站立场。

分享这篇文章:

请关注我们:

发表评论

电子邮件地址不会被公开。 必填项已用*标注