分布式系统漫谈【拾叁】_缓存带来的问题和解决方案


上篇文章:分布式系统漫谈【拾贰】_分库分表带来的问题和解决方案


关于缓存,本博此前已经总结了分布式缓存redis的原理及使用系列文章:


redis知识盘点【零】_redis常用命令


redis知识盘点【壹】_基础知识


redis知识盘点【贰】_五种类型


redis知识盘点【叁】_持久化


redis知识盘点【肆】_主从复制和sentinel哨兵


redis知识盘点【伍】_一致性哈希和cluster集群


redis知识盘点【陆】_客户端Jedis


redis知识盘点【柒】_redis补遗



本文将着重介绍下在分布式系统中缓存的应用场景及问题。


多级缓存


其实缓存归根结底的意义,就是使数据更加接近于使用者,让访问速度更快。工作机制是先从缓存中读取数据,如果没有再从慢速设备上服务实际数据并同步到缓存。在生产场景下,我们一般会采用多级缓存,比如下图:


分布式系统漫谈【拾叁】_缓存带来的问题和解决方案


上图可见我们一般会设计三级至四级缓存,从上到下分别为客户端缓存、网络层缓存(CDN)、路由层缓存(NGINX)和服务层缓存。其中,服务层的缓存还可根据实际情况,分为应用本地缓存(Ehcache)和分布式缓存(Redis)。请求是从上而下来读取缓存的,只有当上层没有缓存到数据,才会向下调用获取。这样做的目的就是尽量减少服务的请求压力,提升用户的相应速度,保障系统的稳定。


因为各级缓存的容量也是有限的,所以缓存也有失效的机制。通常我们会采取以下三种回收算法:


FIFO(First In First Out)

先进先出算法,即先放入缓存的先被移除;


LRU(Least Recently Used)

最近最小使用算法,即使用时间距离现在最久的先移除;


LFU(Least Frequently Uesd)

最不常用算法,即一定时间段内使用次数最少的先移除;


在分布式系统中使用缓存可能会遇到诸多问题,下面试分析:


缓存穿透


一般是指,当某个key在缓存中没有数据时,则需要去数据库中获取该值。如果数据库也没有,则返回空,但是如果代码编写不严谨,那么可能此时缓存中仍然没有该数据。那么问题就来了,当海量的请求到缓存读取该数据时,将全部穿透缓存而去直接读数据库,极端情况下会导致数据库宕机。


解决方法也很简单,首先要保证当从数据库中读取出空值的时候,也写入到缓存,不过注意设置过期时间。另外就是,对穿透缓存而读取数据库的请求进行限流,保障数据库能够正常工作。



热点问题


比如当缓存做分布式部署时,所有的数据被散列到各个节点中,但数据中可能会有热点的数据,当流量爆发时会直接突破节点主机性能极限而导致宕机。现实的案例就是新浪微博,最近比较出名的两次事故就是一次某明星发博声称自己老婆出轨了,一次是某明星公布了自己的女朋友(怎么都是这样的新闻)。当时由于这两条微博的访问量太高,直接导致节点(集群)宕机,该条微博无法访问。这就是非常典型的热点问题。


关于这个问题有这样的解决思路,首先保证热点在业务服务器本地做一个缓存,不要每次都从缓存服务器拉取,这样可以减少一部分不必要的请求;还有就是,考虑使用多个key_index,也就是将热点数据打散到多个节点中,读取相当于高并发读,从不同的节点中获取。


触发限流


有的时候我们看缓存服务器QPS不是很高,但是由于流量(QPS * 单个value大小)很高,还是会出发缓存单节点的流量限流。


解决方案就是根据一定规则动态修改key值,让其散列到多台缓存节点上,避免单台流量过高。


缓存无底洞


在分布式系统中由于通常采用hash函数将key映射到对应的实例,造成key的分布与业务无关,但是由于数据量、访问量的需求,需要使用分布式后(无论是客户端一致性哈希还是redis-cluster),批量操作比如批量获取多个key(例如redis的mget操作),通常需要从不同实例获取key值,相比于单机批量操作只涉及到一次网络操作,分布式批量操作会涉及到多次网络io。此时如果出现缓存加载慢的问题,单从增加缓存服务器的方式是无法解决的。

如果使用的是redis分布式缓存,那么可以考虑从以下两个角度去解决:


将多次请求命令转换为串行mget,以减少网络请求次数。好处是改造简单,当少量的keys时性能可以满足要求。缺点是当大量key请求时可能会延迟严重。


另一种方式是使用redis cluster模式中的hash_tags,可以将多个key强制分配到一个节点上。我们知道,在redis集群模式中,一个结构体中的数据都会落在集群中同一台节点进行保存,此时使用hget命令获取性能最高。但缺点是:业务维护成本较高和容易导致redis集群中节点数据分布不均匀。



分布式系统系列文章先总结到这里,但这个系列没有写完,后面有时间会继续补充。