第5章:数据库俱乐部的保镖:只读副本

0 阅读7分钟

作为创业公司,当用户数从几千达到10万级别的时候,业务重心会发生显著变化。早期主要关注的是冷启动获客以及确保刚上线的服务能正常工作。出现的问题也多半是显而易见的,比如服务器宕机,网站应用崩溃了。解决方案也比较简单粗暴:重启服务器,或者加钱买更好配置的服务器。

但是当用户数达到10万的里程碑之后,新类型的问题就会出现。肉眼可见的火情会被缓慢悄无声息的发热取代。系统可能不会突然崩溃,但是可能会逐渐力不从心,变得愈发迟缓。问题重心不再是有没有顾客用,而是如何提高性能满足更多用户的需求。所以相应的解决办法不再那么简单粗暴,需要更多如外科手术般的精准下药。就像盖房子,此时不能仅满足于屋里的灯还能亮,而是要开始思考建筑物本身的结构是否足够稳固等更深层的问题。

我们通过水平拓展的服务器集群以及负载均衡技术成功解决了厨房产能的问题。现在我们面临的问题是储藏室/图书馆开始变得非常拥挤以至于甚至让人都迈不开步。

第1节:图书馆里的交通阻塞

用上负载均衡之后的确很爽。应用层的流量处理充满技术美感。当我们实时监控到流量和应用服务器CPU占用率暴涨的时候,我们只需要点击几下鼠标就能增加一个新服务器到集群然后就能看到工作负荷神奇地被自动分配到新加的服务器,然后其它服务器的负载就会降下来。应用服务层面完全可拓展,一切尽在掌握。

我们的用户数很快就彪过了5万,接着是8万,迅速接近起初难以想象的10万大关。每个店铺卖家都拥有各自的顾客群体,意味着浏览Dukaan网站的用户人数可能是百万级别的。我们正在服务的流量已经远超预想。

熟悉的恐惧感又悄悄回来了。我们又开始收到来自用户的抱怨,但这次不是关于网站宕机了,而是响应太慢了。

  • “我的顾客需要等待5到6秒才能看到我家店铺”
  • “有时候当我保存一个新商品的时候,网页要加载很久才能保存成功。”

这种网页响应迟缓的情况在印度日常工作高峰时间段(上午11点至下午5点)达到顶峰。我和苏米特目不转睛盯着监控图表。应用服务器看上去表现还行,CPU的工作负荷分配均衡且极少超过50%。负载均衡服务工作地很出色。

但我们那台数据库服务器的性能图表却是完全不同的情景。CPU占用率基本维持在80%到90%的高位。硬盘输入输出数据已经爆表,表明硬盘已经达到基线负荷了。曾经扮演我们救星的独立强大的数据库服务器现在正被负载压得喘不过气。我们的图书馆已经满满当当挤满了人,我们的英雄图书馆管理员变得不堪重负。

确定瓶颈:只是看一看的人数实在太多了

简单地说“数据库响应太慢了”就如同一位医生说“这个病人确实生病了。”一样毫无意义。这不是精确的诊断,只是表面的观察。如果要实现有效治疗,必须搞清楚具体明确的病因。我们需要深入数据库内部,确认它正为之头疼忙碌的具体任务。

深入技术细节:区分数据库操作(只读与写入)

说到底,数据库主要有两种不同的任务类型,理解它们之间的差异非常重要。

  1. 写入改变的数据的操作。主要指令包括INSERT(增加新数据),UPDATE(修改已有数据)以及DELETE(删除数据)。
    • 类比:可以把这些想象成图书馆管理员调整藏书的工作。INSERT就是新书到库。UPDATE就像图书馆管理员修改索引目录卡片上的一个错别字。DELETE类似从书架上移除一本年代久远且已损坏的旧书。
    • 这些操作很关键。必须谨慎处理避免伤及馆藏图书的完整性。通常来说,这些写入需要“某种锁机制”确保不会有两个图书馆管理员同时修改同一个信息。所以一般这些操作相对处理速度不会太快,并且更耗费资源。在Dukaan场景里,这些操作对应的就是店家增加商品,修改价格或者顾客下了一个订单。
  2. 只读查询:这是只涉及读取数据的操作。主要指令是SELECT。
    • 类比:就像一位普通市民走进图书馆想要查阅某本馆藏书籍。它并不会修改任何信息。只是查找书籍,阅读,然后把书籍放回书架。只是信息的消费。
    • 这样的操作一般都比写入处理速度更快,也消耗更少资源。对于Dukaan来说,这就对应着顾客浏览店铺和商品名录产生的流量洪流。

我们为了确定解决问题的关键方向特意安装了一款分析数据库请求的工具。我们发现我们面对的问题在互联网应用行业十分常见,甚至大家都给这个现象专门起了一个名字。

95/5法则(读写差异)

我们的分析揭示了令人的不平衡状态。在数据库每100次请求中:

  • 95次是SELECT只读查询;
  • 只有5次是INSERT,UPDATE或者DELETE写入操作。

这其实很符合我们的观察。一位店家每天可能只会更新他们的商品(数据写入)寥寥数次,但是它的店铺会被数以千计的顾客反复查看(数千次只读操作)。我们的系统承载的绝大部分流量都来自只读请求。

只读操作如何拖累写入操作

这就是我们面临的问题核心:我们的数据库按照同等优先级处理只读和写入。它只有一个任务队列。

设想我们的图书馆管理员如何面对只有唯一入口以及唯一等待排队队伍的场景。在排队的人群中,95%只是想问“我在哪能这本书?”(这其实是一个很快的只读查询)。但同时剩余的5%可能是作家群体想要完成新书的馆藏登记,这样的处理请求通常需要填写很多表格并更新庞大的书籍索引(类似更慢的写入操作)。

作家们不得不与常规入馆的庞大读者等在同一个长长的队伍中。简单的数据只读查询请求量实在太大以至于产生了堵塞反而拖累了关键的数据写入请求。这就是问为什么店家保存新商品的时候会感觉很慢很慢,因为他们重要的写入操作已被淹没在成百上千来自匿名顾客对店铺的浏览请求之后,完全得不到被数据库处理的机会。

解决方案也比较明确了。不能让所有人都挤在同一个队伍里。需要为作家们单独创建一个专享专用的通道,同时为进馆借阅的众多读者提供一个更宽敞的空间。意味着我们需要把只读操作与写入操作分离开来。