Java生鲜电商平台-缓存架构实战
说明:在Java生鲜电商中,缓存起到了非常重要的作用,目前整个项目中才用的是redis做分布式缓存.
缓存集群
缓存集群存在的问题
1.热key
缓存集群中的某个key瞬间被数万甚至十万的并发请求打爆。
2.大value
某个key对应的value可能有GB级的大小,导致查询value的时候导致网络相关的故障问题。
缓存集群作用
在缓存里放一些平时不怎么变动的数据,然后用户在查询大量的平时不怎么变动的数据的时候,可以直接从缓存里走了。缓存集群的并发能力是很强的,而且读缓存的性能是很高的。
缓存实践案例
- 假设系统每秒有2万请求,但是其中90%都是读请求,假如每秒1.8万请求都是在读一些不太变化的数据。那此时你把这些数据都放在数据库里,然后每秒发送2万请求到数据库上读写数据,感受一下这样合适?
- 如果要用数据库承载每秒2万请求的话,那很可能就得搞分库分表 + 读写分离。
- 那得分3个主库,承载每秒2000的写入请求,然后每个主库挂3个从库,一共9个从库承载每秒1.8万的读请求。
- 这样的话,就需要一共是12台高配置的数据库服务器,这是很耗费钱的,成本非常高,很不合适。
- 因此,可以把平时不太变化的数据放在缓存集群里,缓存集群可以采用2主2从,主节点用来写入缓存,从节点用来读缓存。
- 以缓存集群的性能,2个从节点完全可以用来承载每秒1.8万的大量读请求,然后3个数据库主库就是承载每秒2000的写请求和少量其他读请求就OK了(数据一致性问题)。
- 这样一来,耗费的机器瞬间变成了4台缓存机器 + 3台数据库机器 = 7台机器,是不是比之前的12台机器减少了很大的资源开销?
- 缓存其实在系统架构里是非常重要的组成部分。很多时候,对于那些很少变化但是大量高并发读的数据,通过缓存集群来抗高并发读,是非常合适的。
热点缓存
- 所谓热点缓存问题就是突然因为莫名的原因,出现大量的用户访问同一条缓存数据。碰巧这些key都存在于一台缓存机器上。
- 假设每秒突然奔过来20万请求到这台机器上,那台被20万请求指向的缓存机器就会过度操劳而宕机的。
- 读请求发现读不到数据,会从数据库里提取原始数据,然后放入剩余的其他缓存机器里去。但是接踵而来的每秒20万请求,会再次压垮其他的缓存机器。
- 以此类推,最终导致缓存集群全盘崩溃,引发系统整体宕机。
基于流式计算技术的缓存热点自动发现
- 其实这里关键的一点,就是对于这种热点缓存,你的系统需要能够在热点缓存突然发生的时候,直接发现他,然后瞬间立马实现毫秒级的自动负载均衡。
- 一般出现缓存热点的时候,每秒并发肯定是很高的,可能每秒都几十万甚至上百万的请求量过来,这都是有可能的。
- 所以,此时完全可以基于大数据领域的流式计算技术来进行实时数据访问次数的统计,比如storm、spark streaming、flink。
- 一旦在实时数据访问次数统计的过程中,比如发现一秒之内,某条数据突然访问次数超过了1000,就直接立马把这条数据判定为是热点数据,可以将这个发现出来的热点数据写入比如zookeeper中(监听事件)。
- 流式计算系统在进行数据访问次数统计的时候,会不会也存在说单台机器被请求每秒几十万次的问题呢?否!!!
- 流式计算技术,尤其是storm这种系统,他可以做到同一条数据的请求过来,先分散在很多机器里进行本地计算,最后再汇总局部计算结果到一台机器进行全局汇总。
- 所以几十万请求可以先分散在比如100台机器上,每台机器统计了这条数据的几千次请求。
- 然后100条局部计算好的结果汇总到一台机器做全局计算即可,所以基于流式计算技术来进行统计是不会有热点问题的。
热点缓存自动加载为JVM本地缓存
- 我们自己的系统可以对zookeeper指定的热点缓存对应的znode进行监听,如果有变化立马就可以感知到了。
- 此时系统层就可以立马把相关的缓存数据从数据库加载出来,然后直接放在自己系统内部的本地缓存里即可。
- 这个本地缓存,用ehcache、hashmap,其实都可以,一切看自己的业务需求。我们这里主要说的就是将缓存集群里的集中式缓存,直接变成每个系统自己本地实现缓存即可,每个系统本地是无法缓存过多数据的。
- 因为一般这种普通系统单实例部署机器可能就一个4核8G的机器,留给本地缓存的空间是很少的,所以用来放这种热点数据的本地缓存是最合适的,刚刚好。
- 假设系统层集群部署了100台机器,此时你100台机器瞬间在本地都会有一份热点缓存的副本。
- 然后接下来对热点缓存的读操作,直接系统本地缓存读出来就给返回了,不用再走缓存集群了。
- 这样的话,变成100台机器每台机器承载数千请求,那么那数千请求就直接从机器本地缓存返回数据了,这是没有问题的。
限流熔断保护
- 在每个系统内部,还应该专门加一个对热点数据访问的限流熔断保护措施。
- 每个系统实例内部,都可以加一个熔断保护机制,假设缓存集群最多每秒承载4万读请求,那么你一共有100个系统实例。
- 应该提前限制好,每个系统实例每秒最多请求缓存集群读操作不超过400次,一超过就可以熔断掉,不让请求缓存集群,直接返回一个空白信息,然后用户稍后会自行再次重新刷新页面之类的。
-
通过系统层自己直接加限流熔断保护措施,可以很好的保护后面的缓存集群、数据库集群之类的不要被打死。