可扩展性
可扩展性表示描述系统应对负载增长能力。它很重要,但是在实践中又很难做好,因为存在一个基本矛盾 :只有能存活下来的产品才有资格谈扩展,而过早为扩展设计往往活不下去 。
但是可以了解一些基本的概念,来应对可能会暴增的负载。
衡量负载
应对负载前,先找到合适的方法来衡量负载,如负载参数
应用的日活、月活
每秒向web服务器发出的请求
数据库的读写比率
聊天室里同时活跃的用户数
缓存命中率
在上述情况里,也许平均情况更重要,也是少数的极端场景才是你的瓶颈
下面以推特在2012年11月发布的数据为例,推特的两个主要业务是:
发布推文用户可以向其粉丝发布新消息,平均4.6k请求/秒,峰值超过12k请求/秒
主页时间线用户可以查询他们关注的人发布的推文,即推荐feed流,300K请求/秒
处理每秒12000次写入(发推文的速率峰值)还是很简单的,但是推特的可扩展性挑战并不是来自推特量,而是来自扇出(fan-out)-- 每个用户关注了许多人,也被很多人关注
大体来说,这一对操作有两种实现方式:
发布推文时,只需要将新推文插入全局推文集合即可。当一个用户请求自己的推荐feed流主页时间线时,首先查找所有他关注的人,查询这些被关注用户发布的推荐并按时间顺序合并。如下图的关系型数据库里,可以写出来这样的查询:
SELECT tweets.*, users.*
FROM tweets
JOIN users ON tweets.sender_id = users.id
JOIN follows ON follows.followee_id = users.id
WHERE follows.follower_id = current_user
- 为每个用户的主页时间线维护一个缓存,就像每个用户的推文收件箱。当一个用户发布推文时,查找所有关注该用户的人,并将新的推文插入到每个主页时间线缓存中。因为结果已经提前计算好了,所以读取主页时间线的请求开销很小。
简单来说就是推和拉两种方式:
拉:每个人查看其首页推荐feed流时,从数据库现拉取所有关注用户的推文,合并后呈现
推:为每个用户保存一个feed流视图,当用户发推文时,将其插入所有关注者的feed流视图中。
前者是Lazy的,用户只有查看时才会去拉取,不会有无效计算和请求,但每次都需要现算,呈现速度较慢,而且流量一大也扛不住。
后者事先计算出视图,而不管用户看不看,呈现速度较快,但是引入很低无效请求。
后者的效果更好,因为发推频率比查询主页时间线频率几乎低了两个数量级,所以在这种情况下,最好是在写入时做更多的工作,而在读取时做更少的工作。