下面是针对一系列模拟面试后的情况进行面试题的总结和梳理,希望对大家有所帮助:
@SpringBootApplication{@SpringBootConfiguration(标识配置类)、@EnableAutoConfiguration(自动配置基于@import)、@ComponentScan (扫描路径设置)}
启动流程:
第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器,第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建上下文环境模块,第三部分是自动化配置模块,该模块作为springboot自动配置核心
<span style="background-color:#f8f8f8"><span style="color:#333333"> @SpringBootApplication public class SpringTestApplication { public static void main(String[] args) SpringApplication.run(SpringTestApplication.class, args); } }</span></span>
SpringTestApplication类执行main方法,main方法调用SpringApplication的run方法。
run方法干了两件事:
创建SpringApplication对象
利用创建好的SpringApplication对象调用run方法
B+树;
#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入
默认值 16;当我们不断的向HashMap中添加元素时,它会判断HashMap当前的容量值(当前元素的个数)是否超过了它的临界值(在没有指定其初始化大小时,默认16*0.75=12),如果添加的元素个数超过了临界值,它就会开始进行扩容。
继承Thread类创建线程、实现Runnable接口创建线程、实现Callable接口通过FutureTask包装器来创建Thread线程
1)每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。
2)如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被当前 Sentinel 标记为主观下线。
3)如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
4)当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。
5)当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 (在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 )。
6)若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会变成主观下线。若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。
7)sentinel节点会与其他sentinel节点进行“沟通”,投票选举一个sentinel节点进行故障处理,在从节点中选取一个主节点,其他从节点挂载到新的主节点上自动复制新主节点的数据。
AOF和RDB;
AOF:记录每次写请求的命令,以追加的方式在文件尾部追加,直接在尾部追加,效率比较高。 对于操作系统来说,不是每次写都直接写到磁盘,操作系统自己会有一层cache,redis写磁盘的数据会先缓存在os cache里,redis每隔1秒调用一次操作系统的fsync操作,强制将os cache中的数据刷入AOF文件中。
当redis重启的时候,就把AOF中记录的命令重新执行一遍就可以了,但是如果文件很大的话,执行会耗费较多的时间,对于数据恢复来说耗时会多一点。
RDB:是快照文件,每隔一定时间将redis内存中的数据生成一份完整的RDB快照文件,当redis重启的时候直接加载数据即可,同样的数据比AOF恢复的要快。
RDB的优点:第一点就是他会生成多个数据文件,每个数据文件都代表了某一时刻redis中的数据,非常适合做冷备。 第二点,RDB持久化机制对redis对外提供的读写服务影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可。 第三点,相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复redis进程,更加快速。
AOF,存放的指令日志,做数据恢复的时候,其实是要回放和执行所有的指令日志,来恢复出来内存中的所有数据的。
RDB,就是一份数据文件,恢复的时候,直接加载到内存中即可。
RBD的缺点:
1)故障时可能数据丢失的比AOF要多。 一般来说,RDB数据快照文件,都是每隔5分钟或者更长时间生成一次,这个时候一旦redis进程宕机,那么会丢失最近5分钟的数据。
这个问题,也是rdb最大的缺点,就是不适合做第一优先的恢复方案,如果你依赖RDB做第一优先恢复方案,会导致数据丢失的比较多
2)RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。
所以一般不要让RDB的间隔太长,否则每次生成的RDB文件太大了,对redis本身的性能可能会有影响的。
AOF的优点:
1)AOF可以更好的保护数据不丢失 一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
每隔1秒,就执行一次fsync操作,保证os cache中的数据写入磁盘中。 redis进程挂了,最多丢掉1秒钟的数据.
2)AOF持久化性能高 AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。
3)AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。 因为在rewrite log的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。
4)AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。
比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据。
AOF的缺点:
(1)对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
(2)AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的。
如果你要保证一条数据都不丢,也是可以的,AOF的fsync设置成没写入一条数据,fsync一次,但是那样导致redis的QPS大幅度下降。
(3)以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。
所以说,类似AOF这种较为复杂的基于命令日志/merge/回放的方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
(4)唯一的比较大的缺点,其实就是做数据恢复的时候,会比较慢,做冷备不太合适。
其实两个都可以做,只不过RDB更适合。
RDB可以做冷备,是因为它会生成多个文件,每个文件都代表了某一个时刻的完整的数据快照,我们可以将这种完整的数据文件发送到一些远程的安全存储上去,比如可以是阿里云的ODPS分布式存储上,以预定好的备份策略来定期备份redis中的数据。
AOF也可以做冷备,只不过它只有一个文件,但是我们可以去自己写程序,每隔一定时间,去copy一份这个文件出来。
RDB做冷备,优势在于由redis去控制固定时长生成快照文件的事情,比较方便,而 AOF,还需要我们自己写一些脚本去做这个事情,各种定时,比较麻烦。
RDB数据做冷备,在最坏的情况下,提供数据恢复的时候,速度比AOF快。
(1)前端控制器 DispatcherServlet(不需要程序员开发)
作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
(2)处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求的URL来查找Handler
(3)处理器适配器HandlerAdapter
注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。
(4)处理器Handler(需要程序员开发)
(5)视图解析器 ViewResolver(不需要程序员开发)
作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view)
(6)视图View(需要程序员开发jsp)
View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)
三大类 :创建型模式、结构型模式、行为型模式;
创建型模式:共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式:共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式:共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
(1)用户发送请求至前端控制器DispatcherServlet; (2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle; (3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet; (4)DispatcherServlet 调用 HandlerAdapter处理器适配器; (5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器); (6)Handler执行完成返回ModelAndView; (7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet; (8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析; (9)ViewResolver解析后返回具体View; (10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) (11)DispatcherServlet响应用户
主要从4个方面
设计:存储引擎,字段类型,范式与逆范式
功能:索引,缓存,分库分表。
架构:主从复制,读写分离,负载均衡。
合理SQL:测试,经验
创建工程环境。
配置数据库连接信息。这里使用yml方式。spring: datasource: username: root url: …
编写实体类。
编写Mapper接口类
启动redis 、配置redis的数据库连接、编写redis操作工具类、测试
非关系型数据库,C编写的, key,value键值对数据库 数据存储在内存中 读写快。
优点:读写快,支持数据持久化,数据结构丰富,
缺点:容易受到物理内存的限制,不能作为海量数据的高性能读写 ,不具备容错和恢复功能。
适应场景在较小的数据量的高性能操作和运算上。
实例化 Instantiation、属性赋值 Populate、初始化 Initialization、销毁 Destruction
一级缓存默认开启,查询时先从缓存中查询,缓存中没有对应数据时再进行数据库查询。
二级缓存即查询缓存,他的作用于是mapper和namespace,二级缓存可以跨SqlSession
@RestController表示该方法的返回结果直接写入HTTP response body中 @RequestMapping 注解 处理请求地址映射
@EnableAutoConfiguration 注解允许 Spring Boot 自动配置注解
@Configuration用于定义配置类
@SpringBootApplication用在 Spring Boot的主类上,标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力。 @ComponentScan 组件扫描。让spring Boot扫描到Configuration类并把它加入到程序上下文。
@ResponseBody
@Component
@AutoWired
@RequestParam用在方法的参数前面:
@PathVariable 路径变量。参数与大括号里的名字一样要相同 @ResponseBody表示该方法的返回结果直接写入HTTP response body 中
@Repository 用于标注数据访问组件,即DAO组件
ACID 原子性、一致性、隔离性、持久性
原子性(要么全部成功要么全部失败,不会结束在中间某个环节)
一致性(执行前和执行后数据库保持在一致性状态)
隔离性(数据所处的状态要么是更改之前的状态要么是更改之后的状态 不会看到中间的状态数据)
持久性(只要事务成功结束,那么数据库的数据就会永久保存下来,即使数据库崩溃 重新启动后数据库还能恢复到事务成功结束的状态)
不考虑事务的隔离性会出现几种:脏读(在一个事务里读取到领另一个未提交事务的数据)、不可重复读(在一个事务范围内多次查询返回的数据不同的数据, 发生在修改)、幻读( 前后两次读取的数据在第二次查询的时候能查到都第一次看不到的行数据,发生在插入和删除 )
事务的ACID是通过InnoDB的日志和锁来保证的,事务的隔离性通
过数据锁的机制实现的
22、RESTful是一种设计风格而不是规范,比如接口的命名不以动词命名(如findById deleteById等),不同的请求方式代表了相应的动作 比如 /user接口 crud操作只需要更换不同的请求方式即可 一个接口即可搞定 而不需要定义多个接口
InnoDB、MyISAM、MEMORY、ARCHIVE
MyISAM索引与InnoDB索引的区别?
InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。
23.1、InnoDB引擎的4大特性
插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(ahi)、预读(read ahead)
23.2、引擎的选择?
如果要提供提交、回滚、崩溃恢复能力的事物安全(ACID兼容)能力,并要求实现并发控制,InnoDB是一个好的选择。
如果数据表主要用来插入和查询记录,则MyISAM引擎能提供较高的处理效率。
如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果。
如果只有INSERT和SELECT操作,可以选择Archive,Archive支持高并发的插入操作,但是本身不是事务安全的。Archive非常适合存储归档数据,如记录日志信息可以使 Archive。
@ResponseBody注解表示该方法的返回的结果直接写入 HTTP 响应正文中,一般在异步获取数据时使用。通常是在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入HTTP 响应正文中。
@RequestBody是作用在形参列表上,用于将前台发送过来固定格式的数据(xml 格式或者 json等)封装为对应的 JavaBean 对象,封装时使用到的一个对象是系统默认配置的 HttpMessageConverter进行解析,然后封装到形参上。
在GET请求中,不能使用@RequestBody。在POST请求,可以使用@RequestBody和@RequestParam,但是如果使用@RequestBody,对于参数转化的配置必须统一。
1.存取的速度快,数据永久保存
2.第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
25.1、数据库中为什么要用索引?这么多优点怎么不在每一个表中的列都使用索引呢 ?
1、可以大大加快数据的检索速度,使用索引,提高系统的性能。
2、时间上:在每一列上使用索引耗费时间,在使用时会降低执行效率,
空间上:会占用物理空间
25.2、你在开发的时候用到索引了吗 应用场景是?
数据量的时候查询字段较多的时候
当数据多且字段值有相同的值得时候用普通索引,当字段多且字段值没有重复的时候用唯一索引,当有多个字段名都经常被查询的话用复合索引。
好处:异步处理、流量削峰、消息通讯、应用解耦;
缺点:可用性降低、复杂度提高、一致性问题
Rabbit :支持高并发高吞吐、支持集群化、功能较为完善
使用场景:顺序消费、服务间异步通信、定时任务、请求削峰
三种 :单机部署,集群部署,伪集群部署。
数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁、分布式队列
在开发阶段写下的这些判断仅为了调试的语句,在开发完成时需要查找并移除。部署运行后,尤其是在一些企业应用系统中,还经常需要进一步调试,这时就遇到了更大的麻烦。所以,我们需要一套完备的、灵活的、可配置的日志工具log4J就是优秀的选择。
28.1、Log4j主要由哪三部分组成?每部分的主要作用是什么?
Log4j 由 logger、appender 和 layout 三个组件组成。可以通过同名的 Java 类访问 Log4j 的这三个组件。
Logger 在执行应用程序时,接收日志语句生成的日志请求。它是一种重要的日志处理组件, 可以通过 log4j API 的 logger 类对其进行访问。它的方法有:debug、info、warn、error、fatal 和 log。这些方法用于记录消息。 Appender - 管理日志语句的输出结果。执行日志语句时,Logger 对象将接收来自日志语句的记录请求。此请求是通过 logger 发送至 appender 的。然后,Appender 将输出结果写入到用户选择的目的地。对于不同的日志目的地,提供不同的 appender 类型。这些 appender 包括:用于文件的 file appender、用于数据库的 JDBC appender 和用于 SMTP 服务器的 SMTP appender。 Layout - 用于指定 appender 将日志语句写入日志目的地所采用的格式。appender 可以用来格式化输出结果的各种布局包括:简单布局、模式布局和 HTML 布局。
线程池作用就是限制系统中执行线程的数量。
1、提高效率 创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
2、方便管理 可以编写线程池管理代码对池中的线程同一进行管理
3、降低资源消耗
29.1、线程池都有哪几种工作队列
1、ArrayBlockingQueue
是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
2、LinkedBlockingQueue
一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
3、SynchronousQueue
一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
4、PriorityBlockingQueue
一个具有优先级的无限阻塞队列。
Nginx是一个轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载平衡
30.1、为什么要用Nginx?
跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,
而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。
使用Nginx的话还能:
节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
稳定性高:宕机的概率非常小
接收用户请求是异步的
30.2、Nginx的优缺点?
优点:
占内存小,可实现高并发连接,处理响应快
可实现http服务器、虚拟主机、方向代理、负载均衡
Nginx配置简单
可以不暴露正式的服务器IP地址
缺点: 动态处理差:nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,现在一般前端用nginx作为反向代理抗住压力,
30.3、Nginx应用场景?
http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。
30.4、Nginx 是如何实现高并发的?
异步,非阻塞,使用了epoll 和大量的底层代码优化。
如果一个server采用一个进程负责一个request的方式,那么进程数就是并发数。正常情况下,会有很多进程一直在等待中。
而nginx采用一个master进程,多个woker进程的模式。
master进程主要负责收集、分发请求。每当一个请求过来时,master就拉起一个worker进程负责处理这个请求。
同时master进程也负责监控woker的状态,保证高可靠性
woker进程一般设置为跟cpu核心数一致。nginx的woker进程在同一时间可以处理的请求数只受内存限制,可以处理多个请求。
Nginx 的异步非阻塞工作方式正把当中的等待时间利用起来了。在需要等待的时候,这些进程就空闲出来待命了,因此表现为少数几个进程就解决了大量的并发问题。
https://yq.aliyun.com/articles/659726
https://developer.aliyun.com/article/711990
https://www.jianshu.com/p/a66b83c450b2
https://www.cnblogs.com/zhangyfr/p/10459346.html
https://www.jianshu.com/p/a66b83c450b2
33.1、Redis的队列如何异步使用?
Redis 的 list 结构可以作为队列使用,rpush 生产消息,lpop 消费消息,lpop 没有取到消息时,可以让线程休眠一会再获取消息
blpop 指令,在队列没有消息时,会阻塞线程直到消息被生产,获取消息