这个简单的索引错误差点让我们阴沟翻船
周一上午,我正坐在办公桌前,一边喝着咖啡一边等待大脑苏醒。突然间,销售总监走过来拍了拍我的肩膀。
“今天是个大日子,我们最主要的发布商要切换到我们的附属平台上。你们做好准备了吗?”
我坐在那里,心里想着:“我们根本就不知道这件事啊!”雪上加霜的是,我的经理还去休陪产假了,近两个月来,我一直都在充当技术团队领导的角色。
我对销售总监说,我根本就不知道这次迁移的事情。
我看到他明显颤抖了一下。
于是我对他说:“别担心,应该没什么事。技术团队已经做过很多次内部平台迁移了。”
就这样,噩梦般的一周来临了。
决堤的洪水
那个发布商按下了切换的按钮。
我在屏幕前等待,其他人也站在周围等待。但是什么都没有发生。
人们开始变得焦急,因为屏幕上的报告窗口依然是空白一片。
我在Slack上联系了一些同事,我意识到发布商的网站没有完成配置,这意味着所有流量都被mark为失败,直接进入了failed log。
还好,数据没有丢。我只需要花时间配置一下,然后在failed log找回数据就好了。
经过配置之后,数据开始从failed log中恢复。
流量事件翻了三倍。
流量聚合也翻了三倍。
我靠!主数据库的CPU使用率达到了90%!
据说硬件能解决一切问题
我们的基础设施是多个运行Kafka、Hadoop、Yarn和Sanza的机器。AWS上运行的是ElastiCache和Postgres数据库。另外,客户的页面会推送JavaScript事件给我们。Schedulers负责在背后拉取数据。
基础设施架构概览视图
继续投入硬件
现在我们的主要工作,就是重新掌握系统的控制。在确定问题出现的原因之前,我们能做的,就是投入更多硬件。
技术团队决定升级主数据库的instance,直至AWS所支持的最大值。在升级之后,我们的确看到了一些改善,但是并不足以解决问题。
单纯投入硬件无法解决问题,以为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 = ‘2016–11–01 21:00:00’ AND
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 = ‘0’ AND
device_is_mobile = ‘false’ AND
device_os = ‘Windows NT 4.0’ AND
device_browser = ‘Chrome’ AND
device_type = ‘Desktop’
上面的请求看上去很无辜,但是在运行了一个explain之后,问题就出现了。
--stamp column is indexed.
SELECT aggregated_id from lvl2_unique_traffic.unique_traffic_201611 WHERE
stamp = ‘2016–11–01 21:00:00’
--Count: ~50,000 rows
我意识到,所有数据都被聚合在一起了,这让索引变得毫无意义。在请求应用索引之后,它必须使用过滤器在~50,000 rows上运行WHERE从句。而且它必须要再每一个聚合上使用这个从句。
使用了大O符号之后,这个请求实际上使用了N²,而不是索引所提供的标准的N log(N)。
下面这个图标可以让你更清楚的看到问题:
我们怎么会忽略这么简单的事情?
传说中,有一天毕加索正在公园里画画,一个女人走上前来,请毕加索给她画一张肖像。
这个女人对毕加索画的肖像非常满意,想要买下它。
“5000块。”毕加索回答说。
这个女人十分不解:“一张画怎么会这么贵?你只用了几分钟就画完了啊!”
毕加索回答道:“女士,我花了一生的时间,才让自己有了这样的能力。”
有经验的开发者了解索引必须要减少数据的总量,需要对其进行观察和过滤。他们知道瓶颈在哪里,也了解使用索引的后果,以及如何修复这些问题——这些其实都不难懂。
但是当基础设施较为复杂的时候,这些简单的概念也会变得复杂。
基础设施详细视图
如果你仔细看看我们的基础设施的复杂程度,或许你就能了解我们为何会犯这样的错误。
所有人都会犯错
写这篇文章的过程中,我的一个朋友也遇到了类似的问题。在开始使用索引的时候,他们忘记了把索引添加到特定栏中,以至于数据库CPU用量飙升。
事后分析
通过调整索引解决了问题
教训
沟通是关键
实现获取客户的指标,了解需求的增加程度
从小部分开始迁移,不要一次性进行整体迁移
了解你正在索引的数据
本文文字及图片出自 www.sdk.cn
你也许感兴趣的:
- 我是如何在第一款登月游戏中发现一个 55 年前的漏洞的
- 【外评】航空公司总是把 101 岁的老太太误认为婴儿
- 【译文】经常嗡嗡叫的虫子(bug)
- 【程序员搞笑图片】要不要上报?
- 【译文】满月时,代码工作异常
- 【译文】bug 经济学
- 【译文】一行代码如何造成 6000 万美元的损失
- 我所见过的最奇怪的Bug
- Google在一个函数中放入2万个变量,引发Firefox大崩溃
- 离职两年后,程序员遭前东家索赔:Bug 是你写的
你对本文的反应是: