- 面试题1:为什么要用 Redis ?业务在哪块儿用到的?
- 追问1:Redis里有哪些数据类型?
- 追问2:Redis与Memcached有哪些区别?
- 追问3:那Redis怎样防止异常数据不丢失的?如何持久化?
- 面试题2:Redis为啥是单线程的?
- 追问1:单线程只使用了单核CPU,太浪费,有什么办法发挥多核CPU的性能嘛?
- 面试题3:聊一下对缓存穿透、缓存击穿、缓存雪崩的理解吧?
- 追问1:那你说一下针对缓存击穿的解决方法?
面试题1:为什么要用 Redis ?业务在哪块儿用到的?
正经回答:
Redis是眼下最为人熟知的缓解高并发、提升高可用能力的手段之一,在提升服务器性能方面效果显著。
这里不得不提到高并发场景,我们知道,并发场景下核心点在数据库,引入缓存(以及引入任何负载均衡、集群等策略)的目的都是在减轻数据库压力,让更多原本打到DB上的请求,在中间被拦截处理掉。就像你请个假屁大点儿事还要大老板签字一样?
通俗易懂点儿说,高并发对服务器来说,就好比你被人锤一拳,这拳头可是硬得很,光着膀子的话一拳就给我干吐血。。那么我为了承受住这一拳?穿棉袄、穿护垫、穿…是吧,只要够厚,我都以为你在给我挠痒痒~同理,Redis就是一件又厚又弹的棉袄。
话说回来,它有多厚多弹呢?操作缓存就是直接操作内存,速度相当快,直接操作缓存能够承受的请求数是远远大于直接访问数据库的。
Redis优势:
- 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
- 支持大量集群节点。
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数Redis中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。同样,我们可以把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接打到缓存而不是数据库(即半路拦截掉了)。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
在我们业务中,包括热点词查询、一些实时排行榜数据、访问量点赞量统计、Session共享等等都可以引入Redis来处理。
深入追问:
追问1:Redis里有哪些数据类型?
丰富的数据类型,Redis有8种数据类型,当然常用的主要是 String、Hash、List、Set、 SortSet 这5种类型,他们都是基于键值的方式组织数据。每一种数据类型提供了非常丰富的操作命令,可以满足绝大部分需求,如果有特殊需求还能自己通过 lua 脚本自己创建新的命令(具备原子性);
追问2:Redis与Memcached有哪些区别?
两者都是非关系型内存键值数据库,现在公司一般都是用 Redis 来实现缓存,为什么不用Memcached呢?
- memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
- redis的速度比memcached快很多
- redis可以持久化数据到磁盘,这个很关键,宕机断电不再是硬伤。
追问3:那Redis怎样防止异常数据不丢失的?如何持久化?
RDB 持久化 (快照)
- 将某个时间点的所有数据生成快照,存放到硬盘上。当数据量很大时,会很慢。
- 可以将快照复制到其它服务器从而创建具有相同数据的服务器副本。
- 如果系统发生故障,将会丢失最后一次创建快照之后的数据。
AOF 持久化(即时更新)
- 将写命令添加到 AOF 文件(Append Only File)的末尾。
- 使用 AOF 持久化需要设置同步选项,从而确保写命令同步到磁盘文件上的时机。这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。
面试题2:Redis为啥是单线程的?
正经回答:
上面是Redis官网给的解释(官方文档链接),翻译后简单说,因为Redis的瓶颈不是CPU的运行速度,而往往是网络带宽和机器的内存大小。再说了,单线程切换开销小,容易实现。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案,当然了,也是为了避免多线程存在的很多坑。对了,一个节点是一个单线程。
深入追问:
追问1:单线程只使用了单核CPU,太浪费,有什么办法发挥多核CPU的性能嘛?
我们可以通过在单机开多个Redis 实例,我们一直在强调的单线程,只是在处理我们的网络请求的时候只有一个线程来处理。实际上,一个正式的Redis Server运行的时候肯定是不止一个线程的,都是集群形式,多少多少个节点,所以实际环境中大家不用担心这种问题。
面试题3:聊一下对缓存穿透、缓存击穿、缓存雪崩的理解吧
正经回答:
- 缓存穿透:指缓存和数据库中都没有的数据,导致所有的请求都打到数据库上,然后数据库还查不到(如null),造成数据库短时间线程数被打满而导致其他服务阻塞,最终导致线上服务不可用,这种情况一般来自黑客同学。
- 缓存击穿:指缓存中没有但数据库中有的数据(一般是热点数据缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去查,引起数据库压力瞬间增大,线上系统卡住。
- 缓存雪崩:指缓存同一时间大面积的失效,缓存击穿升级版。
深入追问:
追问1:那你说一下针对缓存击穿的解决方法?
[图片上传失败...(image-331f61-1626851066095)]
- 根据实际业务情况,在Redis中维护一个热点数据表,批量设为永不过期(如top1000),并定时更新top1000数据。
- 加互斥锁(mutex key)
static Lock reenLock = new ReentrantLock();
public List<String> getData04() throws InterruptedException {
List<String> result = new ArrayList<String>();
// 从缓存读取数据
result = getDataFromCache();
if (result.isEmpty()) {
if (reenLock.tryLock()) {
try {
System.out.println("拿到锁了,从DB获取数据库后写入缓存");
// 从数据库查询数据
result = getDataFromDB();
// 将查询到的数据写入缓存
setDataToCache(result);
} finally {
reenLock.unlock();// 释放锁
}
} else {
result = getDataFromCache();// 先查一下缓存
if (result.isEmpty()) {
System.out.println("我没拿到锁,缓存也没数据,先小憩一下");
Thread.sleep(100);// 小憩一会儿
return getData04();// 重试
}
}
}
return result;
}
小结
今天我们复习了面试中常考的Redis三个问题,你做到心中有数了么?对了,如果你的朋友也在准备面试,请将这个系列扔给他,如果他认真对待,肯定会感谢你的!!