缓存的一些开胃菜

缓存

1. 缓存的概念

在服务端编程中,缓存主要是指将数据库的数据加载到内存中,之后对该数据的访问都在内存中完成,从而减少了对数据库的访问,解决了高并发场景中数据库容器成为性能瓶颈的问题;以及基于内存的访问速度高于磁盘的访问速度的原理(数据库读取数据一般需要从磁盘读取),提高了数据的访问速度和程序性能。

基本思想

基本思想就是空间换时间,它对系统的性能提升的性价比非常高。

缓存的思想实际在操作系统被大量用到。比如CPU Cache缓存的是内存数据用于解决CPU处理速度和内存不匹配的问题,内存缓存的是硬盘数据用于解决硬盘访问速度过慢的问题等等。我们为了避免用户在请求数据的时候获取过于缓慢,所以我们选择在数据库上面增加了缓存这一层。

分类

根据缓存是否与应用进程属于同一进程,可以分为本地缓存和分布式缓存。

  • 本地缓存:在同一进程内的内存空间缓存数据,数据读写都在同一哥进程内完成。
  • 分布式缓存:在一个独立的进程并且一般都是与应用进程部署在不同的机器,故需要通过网络来完成分布式缓存数据读写操作的数据传输。

2. 本地缓存

  1. 实现

    • JDK自带的HashMapConcurrentHashMap

      ConcurrentHashMap 可以看作是线程安全版本的 HashMap ,两者都是存放 key/value 形式的键值对。但是,大部分场景来说不会使用这两者当做缓存,因为只提供了缓存的功能,并没有提供其他诸如过期时间之类的功能。一个稍微完善一点的缓存框架至少要提供:过期时间淘汰机制命中率统计这三点。

    • Spring CacheGuava Cache 本地缓存框架

      • Guava CacheSpring Cache 两者的话比较像。Guava 相比于 Spring Cache 的话使用的更多一点,它提供了 API 非常方便我们使用,同时也提供了设置缓存有效时间等功能。它的内部实现也比较干净,很多地方都和 ConcurrentHashMap 的思想有异曲同工之妙。
      • 使用 Spring Cache 的注解实现缓存的话,代码会看着很干净和优雅,但是很容易出现问题比如缓存穿透、内存溢出。
  2. 优缺点

    • 优点
      • 访问速度快:由于数据不需要跨网络传输
    • 缺点
      • 无法进行大数据存储:由于占用了应用的内存空间,不能进行大数据量的存储
      • 数据随应用进程的重启而丢失:由于数据存储在应用进程的内存空间中,当应用进程重启时,数据会丢失。故需要持久化数据
      • 集群的数据一致性问题:由于数据库数据更新,需要保持每一个本地缓存数据一致性问题
  3. 适用场景

    数据一致性要求不高,访问频率高,总数据量小,数据不经常变动。

3. 分布式缓存

  1. 实现
    • MemCached
      • MemCached相当于本地缓存的主要差别是以独立进程的方式存在,数据集中存储,数据不随应用进程的重启而丢失。Key/Value键值对的value只是一个简单的对象类型,不支持数据结构的特性
      • MemCached 进程相当于是在内存维护了一个非常大的哈希表来存储数据,对应的数据操作复杂度都是O(1)
    • Redis
      • Redis是在此基础上,丰富了Key/Value 键值对的value的数据结构,即可在Redis中完成value的相关数据结构操作。例如 Set集合去重,ZSet集合实现数据排序。
      • Redis是单线程多路IO复用的模型(Redis 6.0 引入了多线程 IO ),不存在并发数据读写的线程安全问题,以及更重要的保证的数据读写操作的顺序性
  2. 优缺点
    • 优点
      • 支持大数据量存储,不受应用进程重启影响:由于是独立的进程,拥有独立的内存空间,不会受到应有进程重启的影响
      • 数据读写分离、高性能、高可用:由于一般支持数据副本机制,可以实现读写分离
      • 数据集中存储,保证数据一致性:应有进程采用集群方式部署时,集群的每一个部署节点都通过一个统一的分布式缓存进行数据存取操作
    • 缺点
      • 数据跨网络传输,性能低于本地缓存:需要通过网络来进行数据传输
  3. 适用场景

​ 在高并发场景或对于较大其不可预见的用户访问时,采用分布式缓存,如在某些短时热门活动中可以将Redis当做数据库来用。

4. 缓存失效问题

4.1 缓存穿透

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中,将去查询数据库,但数据库也无此记录,我们没有将这次查询的null写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。

解决方法

  • 缓存空对象:null结果缓存,并加入短暂过期时间。
  • 布隆过滤器:将所有可能存在的key哈希到一个足够大的bitmap中,一个一定不存在的key会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

4.2 缓存雪崩

缓存雪崩:指在我们设置缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

解决方法

  • 采用Redis集群:既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。
  • 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 设置不同过期时间:随机设置缓存的失效时间,让缓存失效的时间点尽量均匀。

4.3 缓存击穿

缓存击穿:对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。如果这个key在大量请求同时进来前正好失效,那么所有对这个key的数据查询都落到DB。

解决方法

  1. 设置热点数据永不过期:从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
  2. 加互斥锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值