java面试(九)
1、Redis 的五种数据结构
String、Hash、List、Set、Zset(SortSet)
2、Redis 为什么快
纯内存、单线程、IO多路复用(一般会继续问IO多路复用相关问题)
IO多路复用相关
由于Redis是单线程的,如果是传统的阻塞式IO,那么各个IO操作都将是顺序执行,如果当前的IO不可读或者不可写,整个Redis都将变成阻塞状态,而不会去询问是否其它IO流可读可写。
Redis是I/O多路复用的,采用了select,poll,epoll方法。前两个都是线程不安全的。select的官方文档说:
一个Sock(I/O Stream)插入到select中,如果另一个线程发现当前这个Sock不可用,要收回这个sock,Select是不支持的。你硬关掉了这个sock,最终对于这个流的操作结果可能会有多种。“If a file descriptor being monitored by select() is closed in another thread, the result is unspecified”
epoll是:
(1)线程安全的,
(2)不仅会通知sock有数据,还会具体告知那个sock里有数据。在select中只会告诉你sock里有数据,而不会返回具体的sock,需要你自己去一个一个的找,如果有上万个sock,找起来那是相当的费劲。
(3)只支持Linux平台
IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题。
如上图所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续进行。
从流程上看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后的最大优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断的调用select读取被激活的socket,即可以达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
3、Redis 持久化
RDB:在指定的时间间隔能对你的数据进行快照存储。
AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。
在Redis中RDB持久化的触发分为两种:自己手动触发与Redis定时触发。
针对RDB方式的持久化,手动触发可以使用:
- save:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。
- bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。
而自动触发的场景主要是有以下几点:
- 根据我们的
save m n
配置规则自动触发; - 从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会触发
bgsave
; - 执行
debug reload
时; - 执行
shutdown
时,如果没有开启aof,也会触发 - 由于
save
基本不会被使用到,我们重点看看bgsave
这个命令是如何完成RDB的持久化的。
这里注意的是 fork
操作会阻塞,导致Redis读写性能下降。我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的事件消耗。以及上面提到的自动触发的频率减少fork次数,或者使用手动触发,根据自己的机制来完成持久化。
AOF的整个流程大体来看可以分为两步,一步是命令的实时写入(如果是 appendfsync everysec
配置,会有1s损耗),第二步是对aof文件的重写。
对于增量追加到文件这一步主要的流程是:命令写入=》追加到aof_buf =》同步到aof磁盘。那么这里为什么要先写入buf在同步到磁盘呢?如果实时写入磁盘会带来非常高的磁盘IO,影响整体性能。
aof重写是为了减少aof文件的大小,可以手动或者自动触发,关于自动触发的规则请看上面配置部分。fork的操作也是发生在重写这一步,也是这里会对主进程产生阻塞。
手动触发: bgrewriteaof
,自动触发 就是根据配置规则来触发,当然自动触发的整体时间还跟Redis的定时任务频率有关系。
下面来看看重写的一个流程图:
对于上图有四个关键点补充一下:
- 在重写期间,由于主进程依然在响应命令,为了保证最终备份的完整性;因此它依然会写入旧的AOF file中,如果重写失败,能够保证数据不丢失。
- 为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个buf,防止新写的file丢失数据。
- 重写是直接把当前内存的数据生成对应命令,并不需要读取老的AOF文件进行分析、命令合并。
- AOF文件直接采用的文本协议,主要是兼容性好、追加方便、可读性高可认为修改修复。
不管是RDB还是AOF都是先写入一个临时文件,然后通过 rename
完成文件的替换工作。
4、Redis 高可用
哨兵机制、Redis Cluster、持久化
哨兵机制
哨兵是一个独立的进程,哨兵的作用就是监控Redis系统的运行状况。
它的功能包括两个:
监控master和slave是否正常运行。
master出现故障时自动将slave数据库升级为master。
在解决master选举问题同时,又引出了一个单点问题,也就是哨兵的可用性如何解决?在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳定。此时哨兵不仅会监控master和slave,同时还会互相监控,这种方式称为哨兵集群,哨兵集群需要解决故障发现、和master决策的协商机制问题。
Redis Cluster
即使是使用哨兵,此时的Redis集群的每个数据库依然存有集群中的所有数据,从而导致集群的总数据存储量受限于可用存储内存最小的节点,形成了木桶效应。而因为Redis是基于内存存储的,所以这一个问题在redis中就显得尤为突出了。
在redis3.0之前,我们是通过在客户端去做的分片,通过hash环的方式对key进行分片存储。分片虽然能够解决各个节点的存储压力,但是导致维护成本高、增加、移除节点比较繁琐。因此在redis3.0以后的版本最大的一个好处就是支持集群功能,集群的特点在于拥有和单机实例一样的性能,同时在网络分区以后能够提供一定的可访问性以及对主数据库故障恢复的支持。
哨兵和集群是两个独立的功能,当不需要对数据分片时使用哨兵就够了,如果要进行水平扩容,集群是一个较好的方式。
5、点击一个URL到页面返回,发生了什么
客户端传给服务端时经过 TCP/IP 四层模型,中途依次加入HTTP报文、TCP报文、IP报文、MAC报文,然后逆序依次拆包。
6、OSI 七层模型 & TCP/IP 四层模型
OSI 七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
TCP/IP 四层模型: 数据链路层、网络层、传输层、应用层
7、cookie 和 session 的区别
会话,指用户登录网站后的一系列动作,比如浏览商品添加到购物车并购买。会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。
总结
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
- 可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中。
应用场景
登录网站,今输入用户名密码登录了,第二天再打开很多情况下就直接打开了。这个时候用到的一个机制就是cookie。
session一个场景是购物车,添加了商品之后客户端处可以知道添加了哪些商品,而服务器端如何判别呢,所以也需要存储一些信息就用到了session。
求职面试题&解析