我的最新文章:BAT 老兵的经验之谈,成长路上这个道理越早知道越好
前言
最近囧辉发现自己的 Java 学习交流群 里有不少同学已经“悄悄”的入职了携程、美团、阿里菜鸟等大厂。
有不少同学也在积极准备大厂面试中,从聊天中可以看得出来大家都信心满满。
其中有一个同学我印象特别深刻,因为我经常晚上发文章,然后隔天早上起来就看到他的点赞了,看得出来是有认真在看的同学。
最近他已经入职阿里菜鸟了,可以说是“悄悄的努力,惊艳所有人”的典范。
希望大家都能像他一样,早日拿下大厂 Offer。
回归本文,对于 Redis 在面试中的重要程度,一句话描述就是:在当前 Java 后端面试中,所有框架/中间件中被问到频率最高的。
本文按 BAT 面试标准给出解析,有些题你阅读起来可能会很吃力,建议收藏反复学习。
希望在这金三银四的日子里,祝你一臂之力,拿下大厂 Offer。
面试系列
我自己前前后后加起来总共应该参加了不下四五十次的面试,拿到过几乎所有一线大厂的 offer:阿里、字节、美团、快手、拼多多等等。
每次面试后我都会将面试的题目进行记录,并整理成自己的题库,最近我将这些题目整理出来,并按大厂的标准给出自己的解析,希望在这金三银四的季节里,能助你一臂之力。
面试文章持续更新中...
内容 | 链接地址 |
---|---|
面试经验分享 | 921天,从小厂到入职阿里 |
两年Java开发工作经验面试总结 | |
4 年 Java 经验,阿里网易拼多多面试总结、心得体会 | |
5 年 Java 经验,字节、美团、快手核心部门面试总结(真题解析) | |
复习2个月拿下美团offer,我都做了些啥 | |
如何准备好一场大厂面试 | |
简历 | 如何写一份让 HR 眼前一亮的简历(附模板) |
Offer 选择 | 跳槽,如何选择一家公司 |
Java 基础 | Java 基础高频面试题(2021年最新版) |
一道有意思的“初始化”面试题 | |
集合(HashMap) | Java 集合框架高频面试题(2021年最新版) |
面试阿里,HashMap 这一篇就够了 | |
并发编程 | 面试必问的线程池,你懂了吗? |
面试必问的CAS,你懂了吗? | |
MySQL | 面试必问的 MySQL,你懂了吗? |
MySQL 8.0 MVCC 核心原理解析(核心源码) | |
Spring | 面试必问的 Spring,你懂了吗? |
Mybatis | 面试题:mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立关系的? |
Redis | 本文 |
JVM | Java虚拟机面试题精选(二) |
Java虚拟机面试题精选(一) | |
分布式 | 面试必问的分布式锁,你懂了吗? |
算法 | 位图法:判断一个数是否在40亿个整数中? |
正文
1、Redis 是单线程还是多线程?
这个问题应该已经看到过无数次了,最近 redis 6 出来之后又被翻出来了。
redis 4.0 之前,redis 是完全单线程的。
redis 4.0 时,redis 引入了多线程,但是额外的线程只是用于后台处理,例如:删除对象,核心流程还是完全单线程的。这也是为什么有些人说 4.0 是单线程的,因为他们指的是核心流程是单线程的。
这边的核心流程指的是 redis 正常处理客户端请求的流程,通常包括:接收命令、解析命令、执行命令、返回结果等。
而在最近,redis 6.0 版本又一次引入了多线程概念,与 4.0 不同的是,这次的多线程会涉及到上述的核心流程。
redis 6.0 中,多线程主要用于网络 I/O 阶段,也就是接收命令和写回结果阶段,而在执行命令阶段,还是由单线程串行执行。由于执行时还是串行,因此无需考虑并发安全问题。
值得注意的时,redis 中的多线程组不会同时存在“读”和“写”,这个多线程组只会同时“读”或者同时“写”。
redis 6.0 加入多线程 I/O 之后,处理命令的核心流程如下:
1、当有读事件到来时,主线程将该客户端连接放到全局等待读队列
2、读取数据:1)主线程将等待读队列的客户端连接通过轮询调度算法分配给 I/O 线程处理;2)同时主线程也会自己负责处理一个客户端连接的读事件;3)当主线程处理完该连接的读事件后,会自旋等待所有 I/O 线程处理完毕
3、命令执行:主线程按照事件被加入全局等待读队列的顺序(这边保证了执行顺序是正确的),串行执行客户端命令,然后将客户端连接放到全局等待写队列
4、写回结果:跟等待读队列处理类似,主线程将等待写队列的客户端连接使用轮询调度算法分配给 I/O 线程处理,同时自己也会处理一个,当主线程处理完毕后,会自旋等待所有 I/O 线程处理完毕,最后清空队列。
大致流程图如下:
2、为什么 Redis 是单线程?
在 redis 6.0 之前,redis 的核心操作是单线程的。
因为 redis 是完全基于内存操作的,通常情况下CPU不会是redis的瓶颈,redis 的瓶颈最有可能是机器内存的大小或者网络带宽。
既然CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了,因为如果使用多线程的话会更复杂,同时需要引入上下文切换、加锁等等,会带来额外的性能消耗。
而随着近些年互联网的不断发展,大家对于缓存的性能要求也越来越高了,因此 redis 也开始在逐渐往多线程方向发展。
最近的 6.0 版本就对核心流程引入了多线程,主要用于解决 redis 在网络 I/O 上的性能瓶颈。而对于核心的命令执行阶段,目前还是单线程的。
3、Redis 为什么使用单进程、单线程也很快
主要有以下几点:
1、基于内存的操作
2、使用了 I/O 多路复用模型,select、epoll 等,基于 reactor 模式开发了自己的网络事件处理器
3、对数据结构进行了优化,简单动态字符串、压缩列表等。
4、单线程可以避免不必要的上下文切换和竞争条件,减少了这方面的性能消耗。
如果题目换成:“Redis 为什么这么快?”,则第4点需要去掉,单线程不是 Redis 快的原因,更多是早期方便实现,后续 Redis 6 的多线程版本性能是更快的。
4、Redis 在项目中的使用场景
缓存(核心)、分布式锁(set + lua 脚本)、排行榜(zset)、计数(incrby)、消息队列(stream)、地理位置(geo)、访客统计(hyperloglog)等。
5、Redis 常见的数据结构
基础的5种:
-
String:字符串,最基础的数据类型。
-
List:列表。
-
Hash:哈希对象。
-
Set:集合。
-
Sorted Set:有序集合,Set 的基础上加了个分值。
高级的4种:
-
HyperLogLog:通常用于基数统计。使用少量固定大小的内存,来统计集合中唯一元素的数量。统计结果不是精确值,而是一个带有0.81%标准差(standard error)的近似值。所以,HyperLogLog适用于一些对于统计结果精确度要求不是特别高的场景,例如网站的UV统计。
-
Geo:redis 3.2 版本的新特性。可以将用户给定的地理位置信息储存起来, 并对这些信息进行操作:获取2个位置的距离、根据给定地理位置坐标获取指定范围内的地理位置集合。
-
Bitmap:位图。
-
Stream:主要用于消息队列,类似于 kafka,可以认为是 pub/sub 的改进版。提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。