Redis

Redis 单线程为什么还这么快?

  1. 命令执行基于内存操作。
  2. 单线程操作,没有线程切换的开销。
  3. 基于IO多路复用机制,提升Redis的I/O利用率。
  4. 高效的数据存储结构:全局hash表以及多种高效的数据结构(比如:跳表、压缩列表、链表)

缓存穿透

缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常服务端出于容错的考虑,如果从存储层查不到数据则不写入缓存层

缓存穿透模型,协助理解

缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义,
解决方案:

  1. 缓存空对象
    当存储层不命中时,仍然将空对象保存到缓存层中,之后在访问这个数据的话,将从缓存中获取,从而保护了后端数据源
    缓存空对象也有几个问题
    1. 空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间(如果是攻击,问题更严重),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。
    2. 缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储数据的不一致,此时可以利用消息系统或者其他方法清除掉缓存层中的空对象。
  2. 布隆过滤器拦截
    在访问缓存层和存储层之前,将存在的key用布隆过滤器提前保存起来,做第一层拦截。
    • https://en.wikipedia.org/wiki/Bloom_filter
    • https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter
  3. 两种解决办法总结


    方案对比

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

  1. 预先设置热门数据:在redis高峰访问前,把一些热门数据提前存入redis中,加大这些热门数据key的时长实时调整 现场监控哪些数据是热门数据,实时调整key的过期时长。
  2. 加互斥锁:互斥锁可以控制查询数据库的线程访问,但这种方案会导致系统的吞吐量下降,需要根据实际情况使用。
public String get(key) {
  String value = redis.get(key);
  if (value == null) { // 代表缓存值过期
      // 设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
      if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  // 代表设置成功
          value = db.get(key);
          redis.set(key, value, expire_secs);
          redis.del(key_mutex);
      } else {  // 这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
          sleep(50);
          get(key);  // 重试
      }
  } else {
      return value;      
  }
}

你可能感兴趣的:(Redis)