百万级QPS,支撑淘宝双11需要哪些技术( 二 )


4、缓存预热
为了解决缓存为空穿透到DB将DB打挂的风险,可以对商品进行预热,提前将商品数据加载到Tair缓存中,将请求直接拦截在Tair,避免大量商品数据同时穿透DB,打挂DB 。
具体预热哪些商品了?
这个其实不难选择,将热点商品统计出来即可,例如以下几类:
1)在双11零点付款前,大部分用户都会将要买的商品放到购物车,因此可以对购物车的数据进行一个统计,将其中的热点数据计算出来即可 。
2)对一些有参与优惠或秒杀活动的商品进行统计,参与活动的商品一般也会被抢购的比较厉害 。
3)最近一段时间销量比较大的商品,或者浏览量比较大的商品 。
4)有参与到首页活动的商品,最近一段时间收藏夹的商品等等...
淘宝背后有各种各样的数据,统计出需要预热的商品并不难 。
通过预热,可以大大降低DB被穿透的风险 。
 
5、本地缓存
阿里云官网的数据【4096GB集群性能增强版】的QPS参考值超过6000万+,但是这个值是在请求分布比较均匀的情况下的参考值,256个分片上每个分片二三十万这样 。
通常个别分片高一点也没事,五六十万估计也ok,但是一般不能高太多,否则可能出现带宽被打满、节点处理不过来等情况,导致整个集群被打垮 。
这个时候就需要祭出我们的最终神器了,也就是本地缓存 。本地缓存的性能有多牛逼了,我们看下这张图 。
这张图是caffeine(一个高性能JAVA缓存框架)官方提供的本地测试结果,并不是服务器上的测试结果 。
测试运行在 macBook Pro i7-4870HQ CPU @ 2.50GHz (4 core) 16 GB Yosemite系统,简单来说,比较一般的配置,大部分服务器配置应该都会比这个高 。
在这个基准测试中,8 线程对一个配置了最大容量的缓存进行并发读 。

百万级QPS,支撑淘宝双11需要哪些技术

文章插图
 
可以看到,caffeine支持每秒操作数差不多是1.5亿,而另一个常见的缓存框架Guava差不多也有2000多万的样子 。
 
而在服务器上测试结果如下:
服务器配置是单插槽 Xeon E5-2698B v3 @ 2.00GHz (16 核,禁用超线程),224 GB,Ubuntu 15.04 。
百万级QPS,支撑淘宝双11需要哪些技术

文章插图
 
可以看到caffeine在使用16线程时支持每秒操作数已经达到3.8亿次,其他的框架也基本都是千万级以上 。
通过上面的数据,大家应该都明白了,本地缓存在抗读流量上理论上是无敌的 。当然本地缓存有一些缺点,例如需要占用服务器的本地内存,因此通常我们只会存储少量的热点数据,严格配置好参数,控制好本地缓存的占用内存上限,避免影响服务器本身的使用 。
因此,我们会对之前的热点数据,再进行一次筛选,选出“热点中的热点”,将这些数据提前预热到本地缓存中 。
可能有同学会问,如果本地缓存里的商品数据发生了变更,怎么办?
一个办法是使用类似ZK的监听方式,当本地缓存的商品发生变更时,触发更新操作,本地缓存去拉取最新数据,因为本地缓存的商品数较少,所以ZK整体压力不会太大 。
另一个办法是本地缓存定期拉取最新数据,例如每隔N秒后,就主动查询一次DB,将数据更新为最新数据,具体延迟几秒,根据业务上能接受的来控制 。
具体选哪种看业务的选择吧,这些被筛选进入本地缓存的数据基本都是最热的那些商品,无论是商家还是运营都心里有数,肯定在活动前会再三确认,所以出现变更的几率其实不大 。
 
6、访问DB加锁
尽管我们对热点数据进行了预热,但是我们必须考虑到可能会有这么一些缓存击穿的场景:
1)某个热点数据在缓存里失效了,大量流量瞬间打到DB,导致DB被打垮 。
2)某个商品并不属于热点商品,所以并没有预热,但是在活动开始后成为热点商品,导致流量大量打到DB,DB被瞬间打垮 。
等等,这些场景都可能会导致DB瞬间被打垮,DB是生命线,DB一挂就凉了,因此我们必须要有相应的措施来应对 。
解决方案在之前讲缓存击穿的文章里其实提过了,就是在访问DB时加锁,保证单台服务器上对于同一个商品在同一时刻,只会有一个线程去请求DB,其他的全部原地阻塞等待该线程返回结果 。
注意,这边我们是不会加分布式锁的,只会加JVM锁,因为JVM锁保证了在单台服务器上只有一个请求走到数据库,通常来说已经足够保证数据库的压力大大降低,同时在性能上比分布式锁更好 。这个在Guava中就有现成的实现,有兴趣的可以看看 。


推荐阅读