在平时我们常常使用 Redis ,这里总结一下 Redis 的相关面试题和一些常见问题的解决方案。

Redis 在互联网公司一般有已下应用:

  • String:缓存、限流、计数器、分布式锁、分布式 Session
  • Hash:存储用户信息、用户主页访问量、组合查询
  • List:微博关注人时间轴列表、简单队列
  • Set:踩、赞、标签、好友关系
  • Zset:排行榜

Redis 为什么会这么快?

  • Redis 是纯内存操作,并且异步持久化到硬盘中。
  • Redis 是单线程,从而避免了多线程中上下文频繁切换的问题。
  • Redis 底层数据结构简单,对数据的操作也简单。
  • Redis 使用非阻塞多路 I/O 复用模型,效率高。

Redis 缓存雪崩该如何避免?

1.对缓存设置相同的过期时间,导致某段时间内缓存失效,请求全部走数据库

在缓存的时候给过期时间加上一个随机值,这样就会大幅减少缓存再同一时间过期。

2.Redis 挂掉了,请求全部走数据库

事发前:实现 Redis 的高可用
事发中:本地缓存(ehcache)+限流(hystrix),尽可能的减少数据库被干掉的可能
事发后:使用 Redis 的持久化机制,重启后快速恢复缓存数据

缓存击穿如何避免?

缓存击穿是指查询一个一定不存在的数据,由于缓存不命中,并且从数据库查询不到的数据不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,失去了缓存的意义。

1.对请求的参数使用布隆过滤器(BloomFilter)或者提前拦截这个请求,不让其请求到数据库。
2.我们也可以对这个空值对象缓存到 Redis,当然我们必须设置一个较短的过期时间,以防止这空值对象对业务数据产生影响。

缓存与数据库双写一致

一般的我们队读操作都有一个套路:
1.如果我们的数据在缓存中有,我们就直接取缓存中的。
2.如果缓存中没有我们想要的数据,我们会先去查询数据库,然后将数据库查询出来的数据写到缓存中。
3.最后将数据返回给用户请求。

高并发情况下,无论是先操作数据库还是后操作数据库,再加上缓存更新,就更加容易导致数据库与缓存数据不一致的问题。如果每次更新了数据库都去更新缓存将是一个非常耗费性能的操作,不如将缓存直接删除掉。等下次读取的时候,缓存没有找到,再到数据库找,再将数据库中的数据缓存到 Redis 中。

解决思路:
先更新数据库,再删除缓存
这样可以解决大部分的并发问题,但是也存在一定的概率(极小)导致缓存不一致的情况。

  • 缓存刚好失效
  • 线程 A 查询数据库,得到一个旧值
  • 线程 B 将新值写入数据库
  • 线程 B 删除缓存
  • 线程 A将插到的旧数据写入缓存

先删除缓存,再删除数据库
这样其实也可以解决并发问题,但是其实也会有一定概率导致缓存不一致的情况。

  • 线程 A 删除了缓存
  • 线程 B 查询,发现缓存已不存在
  • 线程 B 去数据库查询得到旧数据
  • 线程 B 将旧数据写入缓存
  • 线程 A 将新值写入数据库

但是上面两种方式都是在很极端的情况下才会发生的情况,如果在需要缓存和数据强一致性的情况下可以将删除缓存、修改数据库放在一个队列中去执行,实现串行化,这样就可以保证缓存和数据库的一致性。