工作之余又参加了一次面试,对我来说比之前的面试难度都提了一个度,面试官从公司场景引申聊到高并发和Redis的很多问题。 可惜我太菜了回答不上来,只能回答基础的问题。面完就是凉凉的味道。。
Redis相关
Redis的String是怎么实现的?和其他String有什么优势?
Redis是用C语言写的,但它的字符串不是简单的char[]数组,而是简单动态字符串 Simple Dynamic Strings(SDS) ,它是一个二进制安全的字符串。
struct sdshdr{
unsigned int len ; //buf的长度
unsigned int free ; //标记buf中没使用的元素个数
char buf[] ; // 存放元素的数组
}
优势:
- 解决普通char数组的二进制安全问题: C语言的字符串是以\0终止的,二进制数据如果也存在\0字符, 会导致这个字符被截断。而SDS是以len来表示长度的,也就避免了这个问题。
- 缓存区溢出问题
- 方便进行字符串追加,预分配内存
(详细可以直接看下面连接的文章)
Redis String的扩容过程?
简单来讲也是空间预分配的策略,避免了每次字符串追加都要重新分配内存。
这个写的好详细了:https://juejin.cn/post/6894539895084154887#heading-2
Redis作为缓存的优势有哪些?
对比本地缓存HashMap,SpringCache:本地缓存的功能太单一,没有过期时间,淘汰机制,命中率设计等等。 并且 本地缓存不能实现多个服务数据共享。无法支持高并发。
对比其他分布式缓存如memcached:
Redis的数据结构更加丰富,支持list,set,hash等。并且操作具有原子性
Redis支持数据持久化,可以实现灾难恢复。
Redis支持cluster模式,可以分布式部署。
拓展:为啥Redis不采用多线程?
普通的kv操作瓶颈其实不在cpu,而在内存和I/O
Redis中有多种数据类型的操作,包括事务,采用多线程会因为上下切换,加锁解锁等增加复杂度。
Redis 6.0引入的多线程也主要处理网络I/O方面,对网络事件进行监听
Redis Cluster模式
Redis Cluster是服务器Sharding(分片)的技术
使用的场景:
Redis的主从模式和哨兵模式已经保障了数据高可用,实现读写分离的能力。 但是每台Redis服务器存储的都是相同的数据,对于海量数据存储压力还是很大,并且浪费内存,Redis Cluster实现了对数据分片,将不同的数据存储到不同的master节点上,从而解决了海量数据的问题。
Redis集群采用了去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点。
Redis的分布算法:哈希槽算法
对于这样的集群,一个重点问题就是如何将数据分配到不同的Redis服务器上。常见的算法有hash算法,一致性hash算法等等。但是Redis是引入了哈希槽的概念,并且设计了一套新的算法。
Redis-cluster中有16384个哈希槽,集群中的每个redis节点,分配到一部分槽,每个Key使用公式CRC16校验后,对16384取模来决定放置那个槽。通过查询集群的配置,就可以知道key对应的槽属于那个节点,然后再将请求打到该节点。
哈希槽分配到不同节点,可以很容易地向集群添加或者删除节点:
- 新节点加入到集群中,只需要将旧节点的槽移动到新节点
- 移除一个旧节点,只需要将该节点的槽移动到其他节点
槽在节点之间移动不会造成节点阻塞,所以无论是删除还是添加,redis集群都能保证槽的平滑移动,不会造成集群下线。
拓展:一致性哈希算法
一致性hash算法是一种特殊的哈希算法,它本质上也是一种取模算法,但不是按照服务器数量取模,而是对固定值2^32取模
步骤如下:
①、将整个哈希空间按照顺时针方向组织成一个虚拟的圆环,称为Hash环
②、将各个服务器使用Hash函数进行哈希,可以选择服务器IP或者主机名作为关键字进行哈希,来决定服务器在哈希环的位置(这也是为什么对2^32取模的理由,用ip地址进行哈希)
③、用算法定位数据访问到相应的服务器,将数据key使用相同的函数计算出hash值,沿着顺时针找到的第一台机器就是定位到的机器。
一致性Hash的优点是,当服务器数量变化时,只需要重定位一部分的数据,不至于发生缓存雪崩,具有较好的容错性和拓展性。
网络
一次Http请求包含哪些过程?
- 浏览器查找域名的IP地址 (查找过程: 浏览器缓存,本机缓存,发起DNS系统调用)
- 浏览器发起一个TCP连接请求 (IP协议,OPSF协议,ARP协议)
- 建立完TCP连接后,发起HTTP请求
- 服务器返回一个HTTP响应
- 浏览器解析HTML响应,并且展示HTML
HTTP的报文是怎么样的?
HTTP的请求报文和相应报文相似,包括请求行,请求头,请求体
HTTP的请求头有几种?
HTTP头字段是指在超文本传输协议HTTP的请求和响应消息的头部分
常见的请求字段有:
- Accept:表示浏览器能够接受的内容类型
- Accept-Language 浏览器申明自己接收的语言。
- Connection : Connection: keep-alive/close
- Host
- Cache-Control
- Cookie
HTTP3:了解吗?
HTTP3 不再使用TCP协议,而是使用基于UDP的QUIC传输协议。贴张图把:
Netty有什么优势?
易用性: 使用Jdk的NIO变成需要了解很多复杂的概念,比如channel,selectors,socket等等,Netty在NIO的基础上进行更高地封装,提供了统一的API
更低的资源消耗:
- 对象池复用技术,避免频繁创建和销毁对JVM GC带来的压力
- 零拷贝技术:除了操作系统级别的零拷贝技术外,Netty 提供了更多面向用户态的零拷贝技术,例如 Netty 在 I/O 读写时直接使用 DirectBuffer,从而避免了数据在堆内存和堆外内存之间的拷贝
其他杂七杂八
Zookeeper中有什么经典的算法?
Zookeeper的应用:
Zookeeper为分布式应用提供分布式锁,发布订阅和配置管理等服务,也提供类似于文件系统的树方式数据存储,但它主要解决分布式集群中的一致性问题,通过维护和监控存储数据的状态变化,达到基于数据的集群管理。
Zab协议:Zab协议是为了解决分布式一致性设计的协议,
Linux内核态和用户态的区别?
先要知道CPU指令是分权限的,不同CPU指令的权限不同,一些操作硬件的指令权限就很高,不是一般进程能使用的。
内核态和用户态其实就是CPU指令权限的区别。做出区别的目的是为了安全性,保护整个系统。
要从用户态切换到内核态,需要经过三种方式之一:系统调用、中断、异常
用户态切换到内核态再切换到用户态这个过程怎么优化?
重点应该放在减少切换操作,比如说IO操作都会需要系统调用让内核线程执行,这个时候mmap就很有用了。
mmap是一种内存映射文件的方法,将一个文件或者其他对象映射到内存,实现文件磁盘地址和进程虚拟地址空间的对应,这样进程就可以使用指针方法操作这一段内存,不必再调用read,write等系统调用函数了。并且操作系统会自动将页面写回到磁盘上。
(底层菜鸡,只能说点边角料的,如果有知道的大佬请在评论区指点一下我)
Spring的注解原理?