> 文档中心 > 实习面试复盘(二)

实习面试复盘(二)

文章目录

  • 技术问题集合
  • 部分重点知识点
    • SpringBoot与SpringCloud
    • JVM
    • Nginx
    • MySQL
    • Redis
    • RabbitMQ

技术问题集合

  1. 你能说说SpringCloud和SpringBoot和SpringMVC吗(讲得越多越好)
  2. 我看你简历上写了了解JVM,你能简单说说吗?
  3. 你能说说Nginx的架构和底层的算法吗?
  4. MySQL熟悉吗(多说说)
  5. 我看你用过Redis,其中有哪些常用的数据结构呢?
  6. 你的Redis自己搭建过集群吗,有没有更多的了解?
  7. 你项目中的RabbitMQ是怎么应用的?
  8. 你对js和css掌握多少,css中的选择器举几个例子
  9. VUE的使用你会吗?能不能快速上手?

部分重点知识点

SpringBoot与SpringCloud

1 SpringBoot是一个快速开发框架,通过用MAVEN依赖的继承方式,帮助我们快速整合第三方常用框架,完全采用注解化(使用注解方式启动SpringMVC),简化XML配置,内置HTTP服务器(Tomcat、Jetty),最终以Java应用程序进行执行。
SpringCloud:是一套目前完整的微服务框架,它是一系列框架的有序集合。它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简易易懂、易部署和易维护的分布式系统开发工具包。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。

区别:SpringBoot只是一个快速开发框架,使用注解简化了xml配置,内置了Servlet容器,以Java应用程序进行执行。SpringCloud是一系列框架的集合,可以包含SpringBoot。

JVM

2 我先说说JVM的内存结构吧,JVM是由虚拟机栈和本地方法栈、程序计数器以及方法区和堆组成的,其中永久代在JDK8之后变成元空间。
程序计数器和栈都是线程私有的,程序计数器内存空间小,字节码解释器工作就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成。如果线程正在执行一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是Native方法,这个计数器的值则为(Underfined)。此内存区域是唯一一个在Java虚拟机规范中没有任何OutOfMemoryError的区域。
Java虚拟机栈,线程私有,生命周期和线程一致。描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
局部变量表:存放了编译期的各种基本类型(byte、short、int、long、float、double、char、boolean)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)

StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError:如果虚拟机可以动态扩展,而扩展时无法申请到足够的内存。

​ 本地方法栈:区别于Java虚拟机栈的是,Java虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。也会有StackOverflowError和OutOfMemoryError异常。
​ Java堆:对于绝大多数应用来说,这块区域是JVM所管理的内存中最大的一块。线程共享,主要是存放实例和数组。内部会划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。

OutOfMemoryError:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出该异常。

​ 方法区:属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

img

​ 运行时常量池:属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用。编译器和运行期(String的intern())都可以将常量放入池中。内存有限,无法申请时抛出OutOfMemoryError。

​ 直接内存:非虚拟机运行时数据区的部分

在JDK1.4中新加入NIO(New Input/OutPut)类,引入了一种基于通道(Channel)和缓存(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。可以避免在Java堆和Native堆中来回的数据耗时操作。
OutOfMemoryError:会受到本机内存限制,如果内存区域总和大于物理内存限制从而导致动态扩展时出现该异常。

HotSpot虚拟机对象探秘(比较复杂,建议看书)

垃圾回收器与内存分配策略

概述:程序计数器、虚拟机栈、本地方法栈3个区域随线程生灭(因为是线程私有),栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期才知道哪些对象会创建,这部分内存的分配和回收都是动态的,垃圾回收器所关注的就是这部分内存。

垃圾回收之前先要判断对象的死活

  1. 引用计数法:给对象添加一个引用计数器,但是难以解决循环引用问题 img从该图中可以看出,如果不小心直接把Obj1-reference和Obj2-reference置null,则在Java堆当中的两块内存依然保持着互相引用无法回收。

  2. 可达性分析法:通过一系列GC Roots的对象作为起始点,从这些起始点出发所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连的时候说明对象不可用。 img可作为GC Roots的对象:

    虚拟机栈(栈帧中的本地变量表)中引用的对象
    方法区中类静态属性引用的对象
    方法区中常量引用的对象
    本地方法栈中JNI(即一般说的Native方法)引用的对象

四种引用

  1. 强引用:类似于Object obj = new Object(); 创建的,只要强引用在就不回收。
  2. 软引用:SoftReference类实现软引用。在系统要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行二次回收。
  3. 弱引用:WeakReference类实现弱引用。对象只能生存到下一次垃圾收集之前。在垃圾收集器工作时,无论内存是否足够都会回收掉只被弱引用关联的对象。
  4. 虚引用:PhantomReference类实现虚引用。无法通过虚引用获取一个对象的实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

垃圾回收算法

  1. 标记——清除算法:直接标记清除就可。两个不足:效率不高,空间会产生大量碎片
  2. 复制算法:把空间分成两块,每次只对其中一块进行GC。当这块内存使用完时,就将还存活的对象复制到另一块上面。
    1. 解决前一种方法的不足,但是会造成空间利用率低下。因为大多数新生代对象都不会熬过第一次GC。所以没必要1 : 1划分空间。可以分一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor上,最后清理Eden和Survivor空间。大小比例一般是8 : 1 : 1,每次浪费10%的Survivor空间。但是这里有一个问题就是如果存活的大于10%怎么办?这里采用一种分配担保策略:多出来的对象直接进入老年代。
  3. 标记整理法:不同于针对新生代的复制算法,针对老年代的特点,创建该算法。主要是把存活对象移到内存的一端
  4. 分代回收:根据存活对象划分几块内存区,一般是分为新生代和老年代。然后根据各个年代的特点制定相应的回收算法。

新生代:每次垃圾回收都有大量对象死去,只有少量存活,选用复制算法比较合理。

老年代:老年代中对象存活率较高、没有额外的空间分配对它进行担保。所以必须使用标记——清除或者标记——整理算法回收

Nginx

Nginx是高性能的HTTP和反向代理的web服务器,处理高并发能力是十分强大的,能经受高负载的考验,有报告表明能支持高达50000个并发连接数。

其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度京东新浪网易腾讯淘宝等。

Nginx可以作为静态页面的web服务器,同时还支持CGI协议的动态语言,比如perl、php等。但是不支持java。Java程序员只能通过tomcat配合完成。Nginx专为性能优化而开发,性能是其最重要的考量,是线上非常注重效率,能经受高负载的考验。

正向代理

​ Nginx不仅可以做反向代理,实现负载均衡。还能用作正向代理来进行上网等功能。正向代理:如果把局域网外的Internet想象成一个巨大的资源库,则局域网中的客户端要访问Internet,则需要通过代理服务器来访问,这种代理服务就称为正向代理。
​ 简单一点:通过代理服务器来访问服务器的过程,就叫正向代理。需要在客户端配置代理服务器 进行指定网站访问

反向代理

​ 反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问
我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。

负载均衡

  1. 增加服务器的数量,然后将请求分发到各服务器上,将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同的服务器,也就是我们所说的负载均衡。
  2. 客户端发送多个请求到服务器,服务器处理请求,有一些可能要与数据库进行交互,服务器处理完毕后,再将结果返回给客户端。

动静分离

为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度。降低原来单个服务器的压力。

Nginx的组成部分

  1. 全局块,从配置文件开始到events块之间的内容,主要会设置一些影响nginx服务器整体运行的配置指令,主要包括配置运行Nginx服务器的用户(组)、允许生成的worker process数,进程PID存放路径、日志存放路径和类型以及配置文件的引入等。

    worker_processes 1;
    这是Nginx服务器并发处理的关键配置,worker_processes值越大,可以支持的并发处理量也越多,但是会受到硬件、软件等设备的制约

  2. events块,events块涉及的指令主要影响Nginx服务器与用户的网络连接,常用的设置包括是否开启对多 work process 下的网络连接进行序列化,是否允许同时结束多个网络连接,选取哪种事件驱动模型来处理连接请求,每个 work progress 可以同时支持的最大连接数等

    events {

    ​ worker_connections 1024;

    } 这部分的配置对Nginx的性能影响比较大,在实际中应该灵活配置。

  3. 第三部分,这算是Nginx服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。
    这里需要注意的是:http块也可以包括http全局块、server块

    1. http全局块:配置的指令包括文件引入、MIME-TYPE定义、日志自定义、连接超时事件、单链接请求数上限等。

    2. server块:这块和虚拟主机有密切关系,虚拟主机从用户角度看,和一台独立的硬件主机是完全一样的,该技术的产生是为了节省互联网服务器硬件成本
      每个http块可以包括多个server块,而每个server块就相当于一个虚拟主机。
      而每个server块也分为全局server块,以及可以同时包含多个location块。

      1. 全局server块
        最常见的配置是本虚拟机主机的监听配置和本虚拟机主机的名称或IP设置。

      2. location块
        一个server块可以配置多个location块
        这块的主要作用是基于Nginx服务器接收到的请求字符串(例如server_name/uri-string),对虚拟主机名称(也可以是IP别名)之外的字符串(例如前面的。/uri-string)进行匹配,对特定的请求进行处理。地址定向、数据缓存和应答控制等功能,还有许多第三方模块的配置也在这里进行。

        http {    includemime.types;    default_type  application/octet-stream;    sendfile on;    keepalive_timeout  65;    server { listen80; server_name  localhost; location / {     root   html;     index  index.html index.htm; } error_page   500 502 503 504  /50x.html; location = /50x.html {     root   html; }    }}

    Nginx原理

    1. master和worker

      1. nginx启动后,是由两个进程组成的。master(管理者)和worker(工作者)。

      2. 一个nginx只有一个master。但可以有多个worker 实习面试复盘(二)

      3. 过来的请求由master管理,worker进行争抢的方式去获取请求。 在这里插入图片描述 在这里插入图片描述

    2. master-workers的机制的好处

      1. 首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。
      2. 可以使用nginx - s reload 热部署,利用nginx进行热部署操作(热部署就是指你在修改项目bug的时候对jsp或者java类进行了修改在不重启WEB服务器前提下能修改生效。但是对配置文件的修改除外!)
      3. 其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其他进程还在工作,服务不会中断,master进程则会很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。
    3. 设置多少个worker

      1. Nginx同redis类似都采用io多路复用机制,每个worker都是一个独立的进程,但每个进程里只有一个主线程,通过异步非阻塞的方式来处理请求,即使是成千上万个请求也不在话下。每个worker的线程可以把一个cpu的性能发挥到机制。所以worker数和服务器的cpu数相等是最为适宜的。设置少了会浪费cpu,设置多了会造成cpu频繁切换上下文带来的损耗。
    4. 连接数worker_connection

      1. 第一个:发送请求,占用了worker的几个连接数——2或者4个
      2. nginx有一个master,有四个worker,每个worker支持的最大连接数1024,支持的最大并发数是多少?
      3. 普通的静态最大并发数是:worker_connections*worker_progress/2
    5. 而如果是HTTP作为反向代理来说,最大并发数量应该是worker_connections * worker_progress/4
      这个值是表示每个 worker 进程所能建立连接的最大值,所以,一个 nginx 能建立的最大连接 数,应该是 worker_connections * worker_processes。当然,这里说的是最大连接数,对于 HTTP 请 求 本 地 资 源 来 说 , 能 够 支 持 的 最 大 并 发 数 量 是 worker_connections * worker_processes,如果是支持 http1.1 的浏览器每次访问要占两个连接,所以普通的静态访 问最大并发数是: worker_connections * worker_processes /2,而如果是 HTTP 作 为反向代 理来说,最大并发数量应该是 worker_connections * worker_processes/4。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服 务的连接,会占用两个连接。 在这里插入图片描述

    Nginx的六种负载均衡算法

    1. round robin轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动删除
    2. weight(加权轮询):指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况
    3. ip_hash:每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session共享的问题
    4. fair(第三方):可以依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配,Nginx本身默认是不支持fair的,如果需要使用这种调度算法,必须下载Nginx的upstream_fair模块
    5. url_hash(第三方):按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率,Nginx默认是不支持url_hash的,如果需要这种高度算法,必须安装Nginx的hash软件包
    6. least_conn:根据后端服务器的连接情况进行分配客户请求,连接最少的服务器被有限分配客户端请求

MySQL

InnoDB特点

  1. 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB
  2. InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于告诉缓冲数据和索引
  3. 免费的方案可以是拷贝数据文件、备份binlog,或者用mysqldump,在数据量达到几十G的时候就相对痛苦了
  4. 数据和索引是集中存储的,文件格式是.idb。记录存储按主键大小有序插入
  5. 支持外键,支持事务(默认可重复读,不可以防止幻读),支持MVCC,支持行锁、表锁、锁粒度小并发能力高,
  6. 使用B+树实现索引,InnoDB是索引组织表,主键索引的叶子节点存储的是行数据地址,因此主键索引非常高效;非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效

(MySQL太重要重点也太多了 篇幅有限,就不一一写出了,大家可以找找相关文章看看)

Redis

Redis可以存储键和五种不同类型的值之间的的映射。键的类型只能为字符串,值支持五种数据类型:string、list、map、set、zset

与传统数据库不同的是Redis的数据是存在内存中的,所以读写速度非常快,因此redis被广泛应用于缓存方向,每秒可以处理超过10万次读写操作,是已知性能最快的Key-Value DB。另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA脚本、LRU驱动事件、多种集群方案。

优点:

  1. 读写性能优异,Redis能读的速度是110000次/s,写的速度是81000次/s
  2. 支持数据持久化,支持AOF和RDB两种持久化方式
  3. 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行
  4. 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构
  5. 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离

缺点:

  1. 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上
  2. Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复
  3. 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性
  4. Redis比较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

为什么要用 Redis 而不用 map/guava 做缓存?

缓存分为本地缓存和分布式缓存。以Java为例,使用自带的map或者guava实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着jvm的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。 使用Redis或memcached之类的称为分布式缓存,在多实例公用一份缓存数据,缓存具有一致性。缺点就是需要保持redis或memcached服务的高可用,整个程序架构上较为复杂。

Redis为什么这么快

  1. 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap、HashMap的优势就是查找和操作的时间复杂度都是O(1)
  2. 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的
  3. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
  4. 使用多路I/O复用模型。非阻塞IO
  5. 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信协议不一样,Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

什么是Redis持久化?

​ 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis的持久化机制是什么?各自的优缺点?

Redis提供两种持久化机制RDB(默认)和AOF(机制):

RDB:是Redis DataBase的缩写快照。RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。

优点:

  1. 只有一个文件dump.rdb,方便持久化。
  2. 容灾性好,一个文件可以保存到安全的磁盘
  3. 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。使用单独子进程来进行持久化,主进程不会进行IO操作,保证了redis的高性能
  4. 相对于数据集大时,比AOF的启动效率更高

缺点:

数据安全性低。RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候。-

AOF持久化:AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。

当两种方式同时开启时,数据恢复会Redis会优先选择AOF恢复

优点:

  1. 数据安全,aof持久化可以配置appendfsync属性,有always,每进行一次命令操作就记录到aof文件中一次
  2. 通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题
  3. AOF机制的 rewrite 模式。AOF文件没被rewrite之前(文件过大时会对命令 进行合并重写,可以删除其中的某些命令(比如误操作的flushall))

缺点:

  1. AOF文件比RDB文件大,且恢复速度慢
  2. 数据集大的时候,比rdb启动效率低

优缺点是什么?

  1. AOF文件比RDB更新频率高,优先使用AOF还原数据
  2. AOF比RDB更安全也更大
  3. RDB性能比AOF好
  4. 如果两个都配了优先加载AOF

如何选择合适的持久化方式

  1. 一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
  2. 如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
  3. 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
  4. 如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。

Redis持久化数据和缓存怎么做扩容?

  1. 如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容
  2. 如果Redis被当作持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到

Redis的内存淘汰策略有哪些

Redis内存数据集大小上升到时候,就会施行数据淘汰策略。

全局的键空间选择性移除

  1. noeviction:当内存不足以容纳新写入数据时,新写入操作会报错
  2. allkeys-lru:当内存不足以容纳新写入的数据时,在键空间中,移除最近最少使用的key。(这个时最常用的)
  3. allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key

设置过期时间的键空间选择性移除

  1. volatile-lru:当内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,移除最少使用的key
  2. volatile-random:当内存不足以容纳新写入的数据时,在设置了过期时间的键空间中,随机移除某个key
  3. volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

总结:Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。

Redis的内存用完了会发生什么?

如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回),或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

集群方案

哨兵模式

img

哨兵的介绍:sentinel,中文名是哨兵。哨兵是redis集群机构中非常重要的一个组件,主要有以下功能:

  1. 集群监控:负责监控redis master和slaver进程是否正常工作
  2. 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  3. 故障转移:如果master node挂掉了,会自动转移到slave node上
  4. 配置中心:如果故障转移发生了,通知client客户端新的master地址

哨兵用于实现redis集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

  1. 故障转移时,判断一个master node是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
  2. 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。

哨兵的核心知识

  1. 哨兵至少需要3个实例,来保证自己的健壮性
  2. 哨兵+redis主从的部署架构,是不保证数据零丢失的,只能保证redis集群的高可用性
  3. 对于哨兵+redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练

什么是RedLock?

Redis官方提出了一种权威的基于Redis实现分布式的锁的方式名叫RedLock,此种方式比原先的单节点的方法更安全。它可以保证以下特性:

  1. 安全特性:互斥访问,即永远只有一个client能拿到锁
  2. 避免死锁:最终client都能拿到锁,不会出现死锁的情况,即使原本锁住某资源的client crash了或者出现了网络分区
  3. 容错性:只要大部分Redis节点存活就可以正常提供服务

缓存异常

缓存雪崩:是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
  2. 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队
  3. 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存

缓存穿透:是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
  3. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

**附加:**对于空间的利用到达了一种极致,那就是Bitmap和布隆过滤器(Bloom Filter)
Bitmap:典型的就是哈希表
缺点是,Bitmap对于每个元素只能记录1bit信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的时间、空间来完成了。

布隆过滤器(推荐)
就是引入了k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。
它的优点就是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。Bloom-Filter算法的核心思想就是利用多个不同的Hash函数来解决“冲突”。
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个的URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到而查数据库

解决方案

  1. 设置热点数据永远不过期
  2. 加互斥锁

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决方案

  1. 直接写个缓存刷新页面,上线时手工操作以下
  2. 数据量不大,可以在项目启动的时候自动进行加载
  3. 定时刷新缓存

缓存降级

当访问量剧增,服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可用根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)
在进行降级之前要对系统进行梳理,看看系统是不是可用丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级
  2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警
  3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阈值,此时可以根据情况自动降级或者人工降级
  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的作法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户

RabbitMQ

MQ全称Message Queue消息队列,是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。其中可能有A系统充当生产者生产消息通过MQ充当中间件分发消息给消费者。

MQ的优势:

  1. 应用解耦,MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。系统的耦合度越高,容错性就越低,可维护性就越低,比如库存系统出了问题,用户调用订单系统的时候也会出现问题,并且对其他与订单系统调用的系统会有影响,有了MQ的话就会解耦,如果是库存系统出问题了,不影响订单系统与其他系统的相互调用,提升容错性和可维护性
  2. 任务异步处理,将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行处理。提高了应用程序的响应时间。比如订单分别调用库存、支付、物流系统时,都是比较耗时的操作。如果有MQ,订单系统发送消息给MQ,最后给这三个系统异步消费。
  3. 削峰填谷,如订单系统,在下单的时候就会往数据库写数据。但是数据库只能支撑每秒1000左右的并发写入,并发量再高就容易宕机。有了MQ之后,消息就被MQ保存起来,然后系统就可以按照自己的消费能力来消费,比如每秒1000个消息,这样慢慢写入数据库,这样就不会卡死数据库了。但是这样一来,高峰期产生的数据势必会被挤压在MQ中,高峰就被"削"掉了,但是会维持一段时间消息的消费,直到消费完积压的消息,这就叫"填谷"

MQ的劣势:MQ的加入,使系统可用性降低,系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。系统复杂度提高MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用

实习面试复盘(二)
AMQP(Advanced Message Queuing Protocol 高级消息队列协议):是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,遵循此协议,不受客户端和中间件产品和开发件产品和开发语言限制。实习面试复盘(二)
RabbitMQ实现了AMQP实习面试复盘(二)

  1. Broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker
  2. Virtual host:出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/ queue等
  3. Connection:publisher/consumer 和 broker 之间的TCP连接
  4. Channel:如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销是巨大的,效率也较低。Channel是在connection内部建立逻辑连接,如果程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method 包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的 Connection 极大减少了操作系统建立TCP connection 的开销
  5. Exchange:message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中去。常用的类型有:direct(point-to-point),topic(publish-subscribe) and fanout(multicast)
  6. Queue:消息最终被送到这里等待consumer取走
  7. Binding:exchange和queue之间的虚拟连接,binding中可以包含routing key,Binding信息被保存到exchange中的查询表中,用于message的分发依据

RabbitMQ提供了6种模式:简单模式、work模式、Publish/Subscribe发布与订阅模式,Routing路由模式,Topic主题模式,RPC远程调用模式(远程调用、不太算MQ)
实习面试复盘(二)

中评网简体版