一个热门键被打爆的时候,单台机器再强也扛不住
大部分缓存键的访问是散的,少数键却集中了绝大部分请求。一个商品被推到首页,或者一条内容突然被大量转发,对应的那个键就会在短时间里被反复读。单台机器的网卡和处理能力都有上限,扛不住的时候,响应就慢下来了。一、热点是怎么被发现的
靠平均值看不出来。整体命中率可能还有九成多,可某个键每秒被读几万次,这个信息藏在明细里。做法是采样统计,把访问次数靠前的键定期打出来,看PM变化。演练的时候可以主动把缓存节点停掉一个,看看剩下的节点和库能不能接住,这比纸上推演有用。
也可以按业务预判。运营要推的活动、要上的首页位,事先就知道会集中到哪里,把这些键提前列出来单独照看,比事后救火省事。预判的清单要跟运营对一遍,他们临时加的位置,往往就是下一个热点。预判的清单要跟运营对一遍,他们临时加的位置,往往就是下一个热点。一个键存的内容越大,序列化和反序列化的开销越高,能拆成几个小键的就别硬塞在一起。
二、本地缓存先挡一层
热点键适合放在应用进程里。请求进来先在本地内存找,找到就直接返回,不用走网络。同一台机器上的几千次请求,一次远程调用都不用发,压力自然就下来了。本地缓存要放在离请求很近的那一层,绕开中间的转发,才省得下时间。命中率掉了不一定是缓存坏了,也可能是请求的参数变了,先看键的分布再下结论。
代价是数据可能略旧。缓解的办法是给本地那份设一个很短的过期时间,几秒就够;再让服务端在值变化时发一条通知,收到就把它清掉。通知本身也可能丢,所以那个很短的过期时间不能省。缓存里不要存用户的敏感信息,一旦这一层被别的服务读到,责任说不清楚也补不回来。
三、把一个键拆成几个
如果热点集中在单个键上,可以给它加上后缀,拆成十几份,分散到不同节点。读的时候按一个随机数选一份,写的时候全部更新。这样单节点的压力就被摊开了。拆分之后统计要跟着改,不然看到的还是拆分前那个总数。
拆分的份数不是越多越好。份数多了,更新时要写的次数也多,一致性维护更麻烦。通常按峰值流量除以单节点能承受的量来估,够用就行。拆出来的那几份要能单独失效,其中一份更新失败,不能拖着其他几份一起等。灰度期间新旧两套键规则可能同时在跑,键名里带上版本号,切换的时候才不会互相覆盖。
四、读写分开走
热点的读远多于写,可以给它配几个只读副本,读请求按权重分过去。主节点只管写和同步,压力小很多。副本的延迟要盯住,延迟大了读到旧值的概率就上来。
副本数量按读的量来定,别一上来铺太多。每个副本都要同步一份数据,同步本身也是开销,铺得过多,主节点的负担反而更重。回源的耗时也要监控,缓存没命中只是多走一趟,库慢才是真正把响应时间拖长的原因。
五、突发流量要有预案
活动开始前把热点键预热好,别等用户来了才头一次回源。同时把回源的上限调低一点,让请求在缓存这层就返回,宁可让少量请求等一下,也别把后面的库拖垮。
预案里要写清楚触发条件:响应时间超过多少、回源量涨到多少,就启动降级。写清楚谁来决定、按什么顺序操作,出事的时候才不至于各说各话。演练一次比写十页预案有用,至少知道谁在什么时候该做什么。演练一次比写十页预案有用,至少知道谁在什么时候该做什么。定期把访问量靠后的键清一批,占着内存又没什么人读的数据,留着只是让淘汰更频繁。
六、看哪些指标
单键的访问次数、单节点的网络出口、缓存服务的响应时间,这三个放在一张图上,热点的形成过程就看得很清楚。只看集群总量,会被平均掉。
还要看热点消失之后的表现。流量退下去,本地缓存里的那份旧值如果还留着,就会出现一段时间的数据偏差。过期时间设短,这个尾巴就短。缓存和库的写顺序要在文档里写清楚,新来的人照着做,才不会各写各的。
热点这件事,说到底是流量分布不均。手段就那么几样,关键在于提前知道哪里会热,以及热起来之后手上有没有现成的动作。 没想到还有这么多细节,之前一直没注意到,感谢楼主提醒和科普。 每一段都很有分量,认真读完全篇感觉整个人都提升了不少。 内容写得很详细,逻辑也很清晰,对新手来说非常友好,支持一下楼主。 楼主的经历很有参考价值,给了我很多新的思考方向,非常感谢。 经验很宝贵,尤其是亲身实践过的总结,比网上随便找的靠谱多了。 逻辑严谨,条理分明,一看就是经过认真思考和整理的内容。 这样的优质帖子越多越好,希望论坛能多一些这样真诚的交流。
页:
[1]