缓存的使用


缓存一直是web2.0应用的性能核心,缓存大幅提高了应用QPS,减少了数据库的压力,降低了RT,让应用能够飞起来。缓存具有易于上手,难于精通的特点,缓存失效的设计是整个缓存应用的难点,在某些复杂情况,不适合的缓存设计增加了系统出错的可能。下面对缓存使用的几种典型情况做下分析。

本地缓存

  1. 用于缓存复杂算法的不可变计算结果。
  2. 在单机或者数据变化率极低的情况下,本地缓存是一个良好的策略,因为本地缓存的效率最高,相比于分布式缓存,具有更少的网络开销,响应时间也更短,使用更加的灵活。例如邮政编码,报表数据,或者离线处理后的中奖用户名单等场景非常适合使用本地缓存。
  3. 在某些场景,本地缓存作为分布式缓存的前置缓存使用,可以设置一个过期时间较长的分布式缓存,同时设置一个过期时间较短的本地缓存。
  4. 数据变化率极低的情况下(例如按天更新);往往也使用定时任务或者对象内置一个定时器从数据库获取最新的数据更新过期数据,相比按时间过期这种失效策略,这种定时更新的策略具有更好的性能。

分布式缓存

  1. 分布式缓存是应用开发中最常见的技术,分布式缓存解决了本地缓存的不一致问题,也避免了本地缓存可能带来的内存泄露问题。通常使用annotation的方式指定CRUD操作如何进行缓存,在annotation中指定key,过期时间,缓存策略(ehcache,memcache,分级缓存),在缓存具有高命中率的情况下可以极大的减少数据库压力,大大提高了系统的吞吐量。
  2. 当缓存失效,请求不能从缓存中取出数据时,一般查询数据库获取,当并发量很高的时候,会给数据库带来极高的压力甚至雪崩,导致服务不可用。一般有几种做法,1)增加一个锁,缓存失效时获取锁,只有获取锁的请求才去查询数据库。2)增加一个存活缓存,当存活缓存为空时,重置存活缓存,然后获取数据,这样只有第一个检查到存活缓存为空的请求会访问数据库。
    3) 优雅降级,对于某些不重要的数据(例如文章浏览量),当缓存失效时,可以直接返回空,前端不做展示。

缓存分页数据

在某些条件下,获取到的数据是list,并且需要(必然)进行分页展示。缓存在几种情况下不适合使用,其中一种就是缓存大对象,大对象会导致缓存的性能降低,网络开销和计算开销也比较大。按照每页的Key缓存数据也是不合适的,当有写操作时需要失效所有的key。对于需要缓存分页数据的需求,视情况可以 1)只缓存前2页数据,绝大部分的流量都集中在前2页。2)只缓存id的list,获取到id的list后进行分页,然后通过service获取每个id的缓存数据。

缓存写操作

在某些场景,具有高频的写操作(例如记录浏览数,游戏领域)如果对数据的准确性要求不高,可以通过缓存写操作,定时存盘的方式大幅提高性能。例如所有的浏览数都记录在缓存中,每分钟更新一次数据库,在某些关键操作后(例如击杀BOSS),可以强制更新数据库一次,保证数据不因为宕机而丢失。

分布式缓存计数器

分布式缓存提供了统一的存储和原子操作,便于集群环境下的使用。库存计算是分布式缓存的一个典型应用场景,在用户购买商品前会进行减库存,虽然在高并发时会将库存减为负数,但只要保证缓存中的数值大于等于0时才能购买就能防止超卖。
在秒杀的场景中可以本地缓存配合分布式缓存使用,因为秒杀的时间太短,用户并不关心秒杀过程中的库存数,例如秒杀商品只有5件,可以在每台服务器做一个库存数等于5的原子计数器,这样将极大减少分布式缓存的请求数。甚至在秒杀的场景中,可以完全使用内存缓存,通过zookeeper提前指定一台机器的内存库存数等于5,其他机器都设置为0。

参考:
高扩展性网站的50条原则
构建高性能web站点

posted @ 2017-02-23 11:10  minotaursu  阅读(254)  评论(0编辑  收藏  举报