在平时我们常常使用 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 将新值写入数据库
但是上面两种方式都是在很极端的情况下才会发生的情况,如果在需要缓存和数据强一致性的情况下可以将删除缓存、修改数据库放在一个队列中去执行,实现串行化,这样就可以保证缓存和数据库的一致性。