1.Java 中操作字符串都有哪些类?它们之间有什么区别?
String,StringBuffer,StringBuilder
String每次操作都会生成String对象,StringBuffer,StringBuilder是在原对象基础上进行操作
StringBuffer线程是安全,StringBuilder性能高
效率:
StringBulider(无同步锁)> StringBuffer(有同步锁) > String
StringBuffer和StringBuffer线程安全主要差在哪里?
StringBuffer的很多方法都被关键字synchronized 修饰,允许多线程进行字符操作
2.请你说一下Error 和 Exception 区别是什么?
Error 和 Exception 都是 Throwable 的子类,Throwable是所有异常的父类
Error类包括一些严重的程序不能处理的系统能够错误类,如内存溢出,虚拟机错误,栈溢出,一般与硬件有关,与程序本身无关,程序本身无法捕获和处理
Exception分为可检查异常(编译时异常)和不可检查异常(运行时异常)
3.== 和 equals() 的区别是什么?
==判断两个对象的地址是不是相等
equals()判断两个对象是否相等(1类没有覆盖equals() 方法,等价于调用了Object类的equals() 方法,也就是通过“==”比较这两个对象 2类覆盖了 equals() 方法,就是对内容对比)
不重写equals() 方法,会怎么样?
没有重写equals()方法调用的是Object默认的equals()方法就是对对象的地址值进行判断,等价于‘==’
重写equals()的同时,为什么需要重写hashCode()?
两个对象equals相同时,也要确保hashCode相同,因为调用equals()时也会对hashCode进行判断
4.为什么要用 Redis ?业务在哪块儿用到的?
目前比较热门缓解高并发,提升高可用的手段
1 缓存 2 限流 3 购物车 4 分布式锁
Redis里有哪些数据类型?
String,hash,list,set,zset
Redis怎样防止异常数据不丢失的?如何持久化?
RDB持久化(快照):将某个时间点的所有数据生成快照,存放到硬盘上
AOF持久化(即使更新):将写命令添加到 AOF文件的末尾
? ??
5.Redis为啥是单线程的?
Redis的瓶颈不是CPU的运行速度,而是网络带宽和机器的内存大小 ? ?
单线程只使用了单核CPU,太浪费,有什么办法发挥多核CPU的性能嘛?
单机开多个Redis 实例,搭集群,多节点
6.聊一下对缓存穿透、缓存击穿、缓存雪崩的理解吧
缓存穿透:缓存和数据库中都没有的数据,占着数据库的线程导致其他服务阻塞(采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中)
缓存击穿:缓存中没有数据但数据库中有的数据,并发量高给数据库造成压力(在原有的失效时间基础上增加一个随机值)
缓存雪崩:缓存同一时间大面积的失效,缓存击穿升级版(1使用互斥锁2提前使用互斥锁3永远不过期4资源保护
7.对比 Vector、ArrayList、LinkedList 有何区别?适合在什么场景下使用?
Vector:线程安全的动态数组,同步操作,当数组已满,开始扩容时,会先创建新的扩容后数组,并拷贝原有数组数据,最后删除原数组
ArrayList:线程不安全,查询和更新性能好,因为根据角标index直接锁定位置,扩容和Vector一样,不过扩容数量是50%,插入和删除数据时效率低,因为增删操作影响数组内的其他数据的下标
LinkedList:线程不安全,插入和删除性能好,因为插入数据时只需要记录当前项的前后项即可,增删时也只需修改链表指向即可,数组结果是双向链表,不需要扩容,查询和更新性能比较差,因为需要移动指针从前往后依次查找
多线程场景下就不能使用ArrayList么?
ArrayList线程是不安全的,需要通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用
8.List 和 Set 有哪些区别?
是继承自Collection 接口,
list方法可以允许重复的对象,而set方法不允许重复对象
list有序,插入顺序就是输出顺序;Set无序,无法保证每个元素的存储顺序;
Set 和 List 效率上对比怎么样呢?
Set:删除和插入效率高,插入和删除不会引起元素位置改变
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变
说一下 HashSet 的实现原理?
HashSet 底层是基于 HashMap 实现的,能够继承 HashMap 的所有特性,构也是数组+链表+红黑树
HashSet是如何保证Key不重复的?
HashSet 底层是基于 HashMap 实现的,HashMap 针对 hashCode 相同且 equals 比较值相同的时候执行的是更新操作,所以Hashmap中的key是唯一的,也决定了hashset元素值也是唯一的。
9.Array 和 ArrayList 有何区别?
Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。
Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
10.你对数据库优化有哪些了解呀?
最大利用索引;尽可能避免全表扫描;减少无效数据的查询;
那你对SQL优化方面有哪些技巧呢?
1.避免出现select *;
2.避免出现不确定结果的函数;
3.多表关联查询时,小表在前,大表在后;
4.使用表的别名
5.用where字句替换HAVING字句
6.调整Where字句中的连接顺序(将过滤数据多的条件往前放)
说一下为什么不建议用SELECT * 呢?
增加查询分析器解析成本。
增减字段容易与 resultMap 配置不一致。
无用字段增加网络 消耗,尤其是 text 类型的字段。
11.你对分库分表是怎么看的呀?
分库:由单个数据库实例拆分成多个数据库实例,将数据分布到多个数据库实例中。目的是减轻单台MySQL实例存储压力及可扩展性
分表:由单张表拆分成多张表,将数据划分到多张表内。目的是解决单张表数据过大以后查询的瓶颈问题
常用策略包括:
垂直分表:可以把一个宽表的字段按访问频次、是否是大字段的原则拆分为多个表,这样既能使业务清晰,还能提升部分性能。拆分后,尽量从业务角度避免联查,否则性能方面将得不偿失。
垂直分库:可以把多个表按业务耦合松紧归类,分别存放在不同的库,这些库可以分布在不同服务器,从而使访问压力被多服务器负载,大大提升性能,同时能提高整体架构的业务清晰度,不同的业务库可根据自身情况定制优化方案。但是它需要解决跨库带来的所有复杂问题。
水平分库:可以把一个表的数据(按数据行)分到多个不同的库,每个库只有这个表的部分数据,这些库可以分布在不同服务器,从而使访问压力被多服务器负载,大大提升性能。它不仅需要解决跨库带来的所有复杂问题,还要解决数据路由的问题。
水平分表:可以把一个表的数据(按数据行)分到多个同一个数据库的多张表中,每个表只有这个表的部分数据,这样做能小幅提升性能,它仅仅作为水平分库的一个补充优化。
一般来说,在系统设计阶段就应该根据业务耦合松紧来确定垂直分库,垂直分表方案,在数据量及访问压力不是特别大的情况,首先考虑缓存、读写分离、索引技术等方案。若数据量极大,且持续增长,再考虑水平分库水平分表方案。
12.MySQL删除数据的方式都有哪些?
elete、truncate、drop 关键字进行删除
说一下 delete、truncate、drop的区别吧
执行速度drop > truncate >> DELETE
DELETE:属于数据库DML操作语言,只删除数据不删除表的结构,会走事务
truncate:属于数据库DDL定义语言,不走事务(一般完全清理点某些数据用)
drop:属于数据库DDL定义语言,不走事务(一般删库用)
13.说一下抽象类和接口有哪些区别?
一个类可以有多个接口,只能有继承一个父类;
抽象类可以有构造方法,接口中不能有构造方法;
抽象类中可以有普通成员变量,接口中没有普通成员变量;
接口里边全部方法都必须是abstract的;抽象类的可以有实现了的方法;
抽象类中可以包含静态方法,接口中不能包含静态方法;
在接口和抽象类的选择上,必须遵守这样一个原则:
行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能
说一说你对抽象类的理解吧,他到底是干啥用的?
面向对象的核心思想是:先抽象,后具体。
抽象类是含有抽象方法的类,不能被实例化,抽象类常用作当做模板类使用
抽象类在代码实现可以实现代码的重用
用抽象类实现一个接口,和普通类实现接口会有什么不同么?
使用普通类来实现接口,这个普通类就必须实现接口中所有的方法,可能会需要实现多余的方法,造成代码冗余;
使用抽象类来实现接口,就可以只实现接口中的部分方法,并且当其他类继承这个抽象类时,仍然可以实现接口中有但抽象类并未实现的方法。
抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承
14.final 在 Java 中有什么作用?
修饰类:类不能被其他类所继承
修饰方法:把方法锁定,以防止继承类对其进行更改或重写
修饰变量:该基本数据类型的值一旦在初始化后便不能发生变化
能分别说一下final、finally、finalize的区别么?
final:可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值
finally:一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中
finalize:属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,判断一个对象是否能回收
15.你对Java序列化了解么?
保存对象在内存中的状态。
把一个Java对象变成二进制内容,实质上就是一个byte[]数组,因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程(IO)
反序列化:把一个二进制内容(也就是byte[]数组)变回Java对象。
Java序列化是如何工作的?
对象的类实现java.io.Serializable接口时,该对象才有资格进行序列化;
Serializable方法只是标记的作用,接口没有方法,只是告诉JVM虚拟机标记的类要序列化,默认情况下JVM通过writeObject将可序列化的对象写入输出流,readObject从输入流读取,构造并返回一个对象。
16.说一下TCP连接的三次握手和四次挥手吧?
三次握手:
建立一个TCP连接时,需要客户端和服务器总共发送3个包,通过这三个请求包,来确认双方的接收能力和发送能力是否正常,同时,指定自己的初始化序列号为后面的可靠性传送做准备。(
第一次握手:客户端将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给服务器端,客户端进入SYN_SENT状态,等待服务器端确认。(客户发送连接请求)
第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。(服务端接收到请求,确认了客户端发送功能和服务端接收功能正常)
第三次握手:客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,
? ? ? ?完成三次握手,随后客户端与服务器端之间可以开始传输数据了(客户端接收到请求,确认了客户端接收功能和服务端发送功能正常)
案例:
第一次握手:我把信(第一封信)绑在鸽子腿上发给女朋友,如果女朋友收到了,就确定了我的发件能力和她的收件能力没问题;
第二次握手:然后女朋友给我回信(第二封信),我如果收到了,说明我的收件能力和她的发件能力没问题;
第三次握手:然而此时女朋友还不知道她的发件能力和我的收件能力是否正常;因此我还要给他发(第三封信)
四次挥手:
四次挥手即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开
第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
第二次挥手:服务器端收到FIN后,先发送ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2 状态,继续等待服务器端的FIN报文。
第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
第四次挥手:客户端收到FIN=N报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=N+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手
为什么连接的时候是三次握手,关闭的时候却是四次握手?
因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP设置了一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次,若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接
17.常见的HTTP状态码有哪些?
200:OK,表示从客户端发来的请求在服务器端被正确处理
301:永久性重定向,表示资源已被分配了新的 URL
304:表示服务器允许访问资源,但请求未满足条件的情况
400:请求报文存在语法错误
403:表示对请求资源的访问被服务器拒绝
404:表示在服务器上没有找到请求的资源
500:表示服务器端在执行请求时发生了错误
503:表明服务器暂时处于超负载或正在停机维护,无法处理请求
18.先说说GET和POST请求有哪些区别吧?
GET请求在URL中传送的参数是有长度限制的,而POST没有
GET比POST更不安全,GET数直接暴露在URL,不能用来传递敏感信息,POST数据不会显示在URL中
参数的数据类型,GET只接受ASCII字符,而POST没有限制
GET请求会被浏览器主动缓存,而POST不会,除非手动设置
GET在浏览器回退时是无害的,而POST会再次提交请求
那Get请求有Request body么?如果有的话参数可以像Post请求一样放在里面么?
GET和POST在本质上没有区别,都是HTTP协议中的两种发送请求的方法。
那么你知道Get、Post请求发送的数据包有什么不同吗?
GET请求时产生一个TCP数据包;POST请求时产生两个TCP数据包;
GET:浏览器会把http header和data一并发送出去,服务器响应200(返回数据)
POST:浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 OK(返回数据)
19.简述一下Springboot相对SSM做了哪些提升?
SpringBoot是采用“约定大于配置”的理念,让你的项目快速运行起来,简化了大量的XML配置
1.Spring 项目独立运行(SpringBoot 可以以jar 包的形式独立运行,以前的需要通过tomcat)
2.内嵌Servlet 容器(可选择内嵌Tomcat、Jetty 或者Undertow,无须以war 包形式部署项目)
3.简化Maven 配置(提供了一系列的starter pom 来简化Maven 的依赖加载)
4.自动配置Spring(根据在类路径中的jar 包、类,为jar 包里的类自动配置Bean)
5.准生产的应用监控(提供基于http、ssh、telnet 对运行时的项目进行监控)
6.无代码生成和xml 配置(通过条件注解和配置组合来实现Spring 的所有配置)
说说你在使用SpringBoot时比较有印象的有哪些注解
@SpringBootApplication ?核心的注解,标识这是一个 Spring Boot 应用,用来开启 Spring Boot 的各项能力
@EnableAutoConfiguration ?自动配置注解,根据当前类路径下的包或者类来配置 Spring Bean
@RestController 用于标注控制层组件,包含@Controller和@ResponseBody
@ResponseBody 表示该方法的返回结果直接写入HTTP response body中
@Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
@ComponentScan 组件扫描,如果扫描到有@Component @Controller @Service等这些注解的类,则把这些类注册为bean
@Bean 放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理
@AutoWired 把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@RequestMapping 一个用来处理请求地址映射的注解,可用于类或方法上
20.SpringBoot 打成的 jar 包和普通的 jar 包有什么区别?
Spring Boot 中默认打包成的 jar 叫做可执行 jar,可以通过可以通过命令(java -jar xxx.jar)来运行的,但这种jar包不能被其他项目所依赖,因为它和普通 jar 的结构不同,即使被依赖了也不能直接使用其中的类;
普通的jar包,解压后直接就是包名,包里就是我们的代码(Spring Boot 打包成的可执行 jar 解压后,在 \BOOT-INF\classes 目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置)
如何让SpringBoot打的jar包可依赖?
在pom文件中增加配置项目,打包会生成一个可依赖和可执行的两个包
21.CORS跨域问题是怎么引起的呢?
跨域只存在于浏览器端
跨域请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了
之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致
处理过Springboot的CORS跨域问题么?怎么解决的?
方法一 使用注解@CrossOrigin(Controller层在需要跨域的类或者方法上加上该注解即可)
方法二 写一个类使用@Configuration注解成为配置类并继承WebMvcConfigurerAdapter或者实现WebMvcConfigurer接口,项目启动时,会自动读取配置
方法三 采用过滤器的方式,写一个过滤类使用@Configuration注解成为配置类,实现Filter接口,设置过滤条件,项目启动时,会自动读取配置?
22.先说一下什么是MySQL事务吧?
事务就是一组原子性的SQL执行单元;要么全部执行成功(commit),要么全部执行失败
说一下你对ACID四大特性的理解
原子性:事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性:事务前后数据的完整性必须保持一致
隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
可以从原理上聊一下ACID具体是怎么实现的么?
原子性:通过回滚日志(undolog)来实现
持久性:通过binlog、redolog来实现
隔离性:通过(读写锁+MVCC)来实现
一致性:MySQL通过原子性,持久性,隔离性最终实现(或者说定义)数据一致性
23.并发场景下事务会存在哪些数据问题?
脏读、幻读、不可重复读问题
脏读:某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个回滚了操作,则后一个事务所读取的数据就会是不正确的
幻读:在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的
不可重复读:在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新了原有的数据
那Innodb是如何解决幻读问题的呢?
通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作;2.给涉及到的行两端加间隙锁(Gap Lock)防止新增行写入;从而解决了幻读问题
24.说一下MySQL中你都知道哪些锁?
粒度从大到小:表锁,页锁和行锁;特殊场景下使用的全局锁
按锁级别分类:共享(读)锁、排他(写)锁、意向共享(读)锁、意向排他(写)锁
面向编程的两种锁思想:悲观锁、乐观锁
那你来谈一谈你对表锁、行锁的理解吧。
表锁:存储引擎中最大颗粒度的锁定机制,实现逻辑非常简单,带来的系统负面影响最小;获取锁和释放锁的速度很快,由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题,但并发度低;主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎(大量按索引条件并发更新数据的情况,同时又有并发查询的应用场景)
行锁:存储引擎中最小颗粒度的锁定机制,给予应用程序尽可能大的并发处理能力从而提高系统的整体性能;容易发生死锁,主要是InnoDB存储引擎(表级锁更适合于以查询为主)
页锁:颗粒度介于行级锁定与表级锁之间,会发生死锁。主要是BerkeleyDB存储引擎
那全局锁是什么时候用的呢?
全局锁是对整个数据库实例加锁,使用场景一般在全库逻辑备份时;MySQL全局读锁的命令:Flush tables with read lock,可以使整个库处于只读状态
那你再说一下按锁级别划分的那几种锁的使用场景和理解吧?
锁级别又分为:共享(读)锁、排他(写)锁、意向共享(读)锁、意向排他(写)锁
共享(读)锁:读取操作(SELECT)时创建的锁,查询事务结束前,任何事务都不能对数据进行修改
排他(写)锁:又称独占锁,如果事务A对数据B加上写锁后,则其他事务不能再对数据B加任何类型的锁
25.介绍一下你对Redis哨兵模式的了解吧
主从复制模式:节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,故障处理方式不人性化
哨兵模式:用于监控redis集群中主节点(Master)主服务器工作的状态;在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用;
? ? 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机,
? ? 一个哨兵进程对Redis服务器进行监控,可能会出现问题,可以使用多个哨兵进行监控,各个哨兵之间还会进行监控,形成了多哨兵模式
介绍一下Redis故障自动切换过程?
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行切换过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。
当其他的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行切换操作。
切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
那你说一下主观下线以及客观下线的区别吧?
主观下线:单个哨兵实例对服务器做出的下线判断
客观下线:多个哨兵实例在对同一个服务器做出主观下线判断, 并且通过命令互相交流之后, 得出的服务器下线判断
说一下哨兵进程的工作方式吧?
每个哨兵以每秒一次的频率向整个集群的主服务器,从服务器,其他哨兵发送一个 PING 命令,如果某个实例距离最后一次有效回复 PING 命令的时间超过选项所指定的值,
则这个实例会被哨兵标记为主观下线,则正在监视这个Master主服务器的所有哨兵要以每秒一次的频率确认Master主服务器的确进入了主观下线状态,当有足够数量(大于等于配置文件指定的值)
的哨兵在指定的时间范围内确认Master主服务器进入了主观下线状态, 则Master主服务器会被标记为客观下线。
哨兵模式至少要配置3个以上实例增加哨兵对故障判断的准确性,因为领导者选举需要至少一半加1个节点,奇数个节点可以在满足该条件的基础上节省一个节点
26.redis 的过期策略和内存淘汰策略是一个东西么?
redis 过期策略
在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除
定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;
惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源
redis内存淘汰机制
Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据,如何淘汰旧数据给新数据腾出内存空间
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
简单介绍一下LRU淘汰机制吧?
淘汰最近最少使用的;算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”
算法思路:新数据插入到链表头部,每当缓存命中(即缓存数据被访问),则将数据移到链表头部,当链表满的时候,将链表尾部的数据丢弃。
Redis的内存用完了会发生什么实际问题?
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
Redis如何做内存优化?
利用Hash,list,sorted set,set等集合类型数据,尽可能使用散列表(hashes),散列表使用的内存非常小,比如把一个用户数据的所以数据装进一个散列表,不要单独用一个Key存一个信息。
27.在实际生产环境中的 redis 你是怎么部署的?说一服务器情况吧?
我们线上是用了 5 台服务器(32G 内存+ 8 核 CPU + 1T 磁盘),每台服务器划了两台虚拟机共10台,5 台机器部署了 redis 主实例,另外 5 台机器部署了 redis 的从实例,?
每个主实例挂了一个从实例,5 个节点对外提供读写服务,每个节点的读写高峰 qps 可能可以达到每秒 5 万,5 台机器最多是 25 万读写请求/s。
分配给 redis 进程的内存是 10g,一般线上生产环境,redis 的内存尽量不要超过 10g,超过 10g 可能会有问题,5 台机器对外提供读写,一共有 50g 内存。
因为每个主实例都挂了一个从实例,所以是高可用的,任何一个主实例宕机,都会自动故障迁移,redis 从实例会自动变成主实例继续提供读写服务。
我们主要存的是热点商品数据,每条数据是 10kb。100 条数据是 1mb,10 万条数据是 1g。常驻内存的是 200 万条商品数据,占用内存是 20g,仅仅不到总内存的 50%。
目前高峰期每秒就是 3500 左右的请求量。
28.简单说下你对线程和进程的理解?
进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程
线程:一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据
那进程和线程有哪些区别呢?
1.一个进程可以包含多个线程
2.不同进程间数据很难共享,同一进程下不同线程间数据很易共享
3.进程要比线程消耗更多的计算机资源
4.进程可以拓展到多机,线程最多扩展到多核CPU,而不能扩展到多机
29.守护线程和用户线程的区别?
用户线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
守护线程:运行在后台,为其他前台线程服务。当所有用户线程都结束运行时,守护线程会随 JVM 一起结束工作(典型的守护线程如垃圾回收线程)
30.什么是线程死锁?
死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
形成死锁的四个必要条件是什么?
1.互斥: 某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2.占有且等待: 一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3.不可抢占: 别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4.循环等待: 存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,相反,而只要上述条件之一不满足,就不会发生死锁。
我们该如何避免死锁?
1.打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。
2.打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
3.打破占有且等待条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
4.打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。
死锁避免和死锁预防有啥不同?
死锁预防:法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现
死锁避免:系统运行过程中注意避免死锁的最终发生,因为即使死锁的必要条件存在,也不一定发生死锁
31.你怎么理解ORM框架,常见的ORM框架都有哪些?
对象关系映射简称ORM,主要实现程序对象到关系数据库数据的映射;常见的有Mybatis,Hibernate,iBatis,EJB
优点:使开发更加对象化;可移植性强;可以很方便地引入数据缓存之类的附加功能
缺点:自动化进行关系数据库的映射需要消耗少量系统性能;多表联查语法会变得复杂
大家都在用Mybatis,Mybatis都有哪些优势?
1.入门简单
2.支持注解,面对接口开发,效率高
3.高度封装,使得程序员可专注与业务层,开发效率高
32.相比较Hibernate与Mybatis,你有哪些看法?
都支持JDBC和JTA事务处理;
MyBatis可以进行更为细致的SQL优化,可以减少查询字段;
MyBatis容易掌握,而Hibernate门槛较高;
Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射;
Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便;
Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL;
JDBC(手动)>Mybatis(半自动)>Hibernate(自动)
Hibernate与Mybatis 的缓存机制都有哪些区别?
Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓存方案,创建适配器来完全覆盖缓存行为;
Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是那种缓存
MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制
33.Mybatis中的#{}和${}有哪些区别
#{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号;
${}将传入的数据直接显示生成在sql中;
#方式能够很大程度防止sql注入,$方式无法防止Sql注入($方式一般用于传入数据库对象,例如传入表名);
什么是sql注入?
一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中
mybatis是如何做到防止sql注入的?
1.mybatis可以自己手动编写sql,可以加上对输入参数的判断
2.mybatis对会sql进行预编译,会把sql的#{}替换成?号,用户输入的参数如果不符合标准就会报出异常
34.说一下HashMap的实现原理?
HashMap在JDK1.7及以前是数组 + 链表数据结构
HashMap在JDK1.8是数组+链表+红黑树数据结构
我们常把数组中的每一个节点称为一个桶。当向桶中添加一个键值对时,首先计算键值对中key的hash值,以此确定插入数组中的位置,
但是可能存在同一hash值的元素已经被放在数组同一位置了,这种现象称为碰撞,这时按照尾插法的方式添加key-value到同一hash值的元素的最后面,链表就这样形成了。
当链表长度超过8(阈值)时,链表就自行转为红黑树
如何实现HashMap的有序?
使用LinkedHashMap 或 TreeMap.
LinkedHashMap内部维护了一个单链表,有头尾节点;可以实现按插入的顺序或访问顺序排序;
TreeMap是按照Key的自然顺序或者Comprator的顺序进行排序,内部是通过红黑树来实现;
put方法原理是怎么实现的?
判断数组是否为空,为空进行初始化,计算输入值得hash值,通过hash计算应当存放在数组中的下标 index,
查看数组index是否存在数据,没有数据就构造一个节点存放,存在数据,说明发生了hash冲突(index相同),
判断key是否相等,相等,用新的value替换原数据,如果不相等,判断当前节点类型是不是树型节点,如果是
树型节点,创造树型节点插入红黑树中,如果不是树型节点,创建普通Node加入链表中,判断链表长度是否大
于8并且数组长度大于64,大于的话链表转换为红黑树,插入完成之后判断当前节点数是否大于阈值,如果大于
开始扩容为原数组的二倍
HashMap扩容机制原理?
容量,默认16
加载因子,默认是0.75
阈值=容量*加载因子
当元素数量超过阈值时便会触发扩容,每次扩容的容量都是之前容量的2倍;?
实例化的HashMap默认内部数组是null,即没有实例化。第一次调用put方法时,则会开始第一次初始化扩容,长度为16,
如果不是第一次扩容,则容量变为原来的2倍,阈值也变为原来的2倍
HashMap在JDK1.8都做了哪些优化?
数组+链表改成了数组+链表或红黑树:防止发生hash冲突,链表长度过长,将时间复杂度由O(n)降为O(logn)
插入数据方式由头插法改成尾插法:1.7将新元素放到数组中,新节点插入到链表头部,原始节点后移;而JDK1.8会遍历链表,将元素放置到链表的最后
???????????????头插法可能会导致链表发生反转,多线程环境下会产生死循环
链表红黑树如何互相转换?阈值多少?
链表转红黑树的阈值为:8
红黑树转链表的阈值为:6
35.HashMap是线程安全的吗?
不是线程安全
JDK1.7:会产生死循环、数据丢失、数据覆盖的问题
JDK1.8:中会有数据覆盖的问题
以1.8为例,当A线程判断index位置为空后正好挂起,B线程开始往index位置写入数据时,这时A线程恢复,执行写入操作,这样A或B数据就被覆盖了。
你是如何解决这个线程不安全问题的?
HashTable、SynchronizedMap、ConcurrentHashMap这三种是实现线程安全的Map
HashTable:直接在操作方法上加synchronized关键字,锁住整个数组,粒度比较大;
SynchronizedMap:是使用Collections集合工具的内部类,通过传入Map封装出一个SynchronizedMap对象,内部定义了一个对象锁,方法内通过对象锁实现;
ConcurrentHashMap:使用分段锁(CAS + synchronized相结合),降低了锁粒度,大大提高并发度。
36.HTTP 与 HTTPS 有什么不同?
HTTP:超文本传输协议,被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密
HTTPS:安全套接字层超文本传输协议,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密
HTTPS的工作原理是什么样的?
客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤:
1.客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
2.Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
3.客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
4.客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
5.Web服务器利用自己的私钥解密出会话密钥。
6.Web服务器利用会话密钥加密与客户端之间的通信
37.你了解对称加密和非对称加密么?说一下工作原理
对称加密:加密与解密用的是同样的密钥(加密与解密速度快)
非对称加密:加密与解密用的是不同样的密钥;非对称加密有两把钥匙,公钥和私钥,公钥和私钥是成对的存在,如果对原文使用公钥加密,则只能使用对应的私钥才能解密;
???????私钥经过一系列算法是可以推导出公钥,但是无法通过公钥反向推倒出私钥(加密与解密速度慢)
38.说一下你对Session、Cookie的理解吧
会话跟踪是Web程序中用来跟踪用户的整个会话;
Cookie:客户端记录信息确定用户身份(Cookie实际上是一小段的文本信息,客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie,当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器,服务器以此来辨认用户状态)
Session:服务器端记录信息确定用户身份(客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了)
请介绍一下Session的生命周期吧
Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里;
Session在用户第一次访问服务器的时候自动创建;
Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session;
你知道Cookie中要哪些属性么?都是干什么用的?
name:该Cookie的名称,Cookie一旦创建,名称便不可更改
value:该Cookie的值
maxAge:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效
Session、Cookie都有哪些区别?
1.cookie数据存放在客户的浏览器上,session数据放在服务器上
2.ookie并不安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session
39.MySQL有哪些数据类型?
大致可以分为三类:数值、日期/时间,字符串类型;
char 和 varchar 的区别是什么?
varchar :存储可变长度字符串,字符串数据类型,比固定长度类型更节省空间
char:存储固定长度字符串,根据定义的字符串长度分配足够的空间
存储方式?
varchar需要使用1或2个额外字节记录字符串的长度
CHAR适合存储很短或长度近似的字符串
存储容量?
char最多只能存放的字符个数为255字节
varchar默认最大65535字节
既然VARCHAR长度可变,那我要不要定到最大?
更长的列会消耗更多的内存,因为MySQL通常会分配固定大小的内存块来保存内部值,
在SQL中需要注意的点?
char类型末尾的空格会被截断,VARCHAR字段存储相同的值时,末尾的空格会被保留
varchar(50)、char(50)中50的涵义是什么?
CHAR和VARCHAR类型声明的长度表示保存的最大字符数
对于MyISAM表,推荐CHAR类型;对于InnoDB表,推荐VARCHAR类型
varchar列中的值为可变长字符串。长度可以指定为0到65535之间的值
那int(10)中10的涵义呢?int(1)和int(20)有什么不同?
int(10):能显示的宽度能显示10位,当输入的数据不足10位时,会自动帮你补全位数
40.MySQL 的内连接、左连接、右连接有什么区别?
内连接:连接表的公共部分
左连接:连接变时保全左表数据,对应连接的表没数据时用null代替
右连接:连接变时保全右表数据,对应连接的表没数据时用null代替
例:ab两表关联,a表数据有,b表关联数据没有,a表的就不要显示或显示null的问题,
???????左链接取A集合,右链接取B集合,full join取并集,inner join 取交集。
41.MySQL的隐式转换问题遇到过么?说说你的理解。
1.两个参数至少有一个是 NULL 时,比较的结果也是 NULL
2.两个参数都是字符串,会按照字符串来比较,不做类型转换
3.两个参数都是整数,按照整数来比较,不做类型转换
4.十六进制的值和非数字做比较时,会被当做二进制串
5.有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
6.有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
7.所有其他情况下,两个参数都会被转换为浮点数再进行比较。(这里所说的浮点数一般默认为double类型)
SQL语句中隐式转换的坑?
黑客同学喜欢用隐式转换进行SQL注入攻击?
索引中隐式转换的坑?
1、索引失效 无法使用到索引查询,因为mysql会在引擎层进行类型隐式转换,会先把username隐式转换成浮点数,然后再做对比,转换成浮点数当于没有索引
2、查询结果不准确 在隐式转换时的时候,部分数字符号会被省略掉,导致数值不准确
42.能简单说一下你对Spring框架的理解么?
种轻量级框架,用于提高开发人员的开发效率和可维护性,集成了很多模块供我们直接使用,包括:
核心容器、数据访问/集成、响应式 web 编程、AOP(面向切面编程)、工具、消息和测试模块
Spring Core:Core封装包是框架的最基础部分,提供IOC和依赖注入特性;
Spring AOP:AOP模块是Spring的AOP库,提供了AOP(拦截器)机制,并提供常用的拦截器,供用户自定义和配置
Spring JDBC:负责Java数据库连接。
Spring JMS:负责Java消息服务。
Spring ORM:用于支持常用的Hibernate,Mybatis等框架,Spring本身并不对ORM进行实现,仅对常见的ORM框架进行封装,并对其进行管理
Spring Web:WEB模块提供对常见框架如Struts X,SpringMVC,JSF的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器
Spring Test:提供了对JUnit和TestNG测试的支持
常见的Core组件有哪些?
IoC Container,控制反转容器
Events,事件编程
Resources,资源加载
i18n,国际化
Validation,校验
Data Binding,数据绑定
Type Conversion,类型转换
SpEL,Spring 表达式
AOP,面向切面编程
43.谈谈对Spring IOC的理解
IOC(控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理;
IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象
当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的
Spring中的bean的作用域有哪些?
singleton:唯一bean实例,Spring中的bean默认都是单例的。
prototype:每次请求都会创建一个新的bean实例。
request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义
Spring中的bean生命周期?
Spring 中的 bean 是线程安全的吗?
容器本身并没有提供Bean的线程安全策略,需要结合作用域才能体现安全性:
对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题;
对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的;
有状态Bean(Stateful Bean) :就是有实例变量的对象,可以保存数据,是非线程安全的。
无状态Bean(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的
44.说一下 SpringMVC 运行流程?
1.客户端(浏览器)发送请求,直接请求到DispatcherServlet。
2.DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
3.解析到对应的Handler(也就是我们平常说的Controller控制器)。
4.HandlerAdapter会根据Handler来调用真正的处理器(对应的接口)来处理请求和执行相对应的业务逻辑。
5.处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是逻辑上的View。
6.ViewResolver(视图解析器)会根据逻辑View去查找实际的View。
7.DispatcherServlet把返回的Model传给View(视图渲染)。
8.把View返回给请求者(浏览器)
能介绍一下SpringMVC各组件的作用么?
DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性,系统扩展性提高。由框架实现
HandlerMapping:处理器映射器。HandlerMapping负责根据用户请求的url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,根据一定的规则去查找,例如:xml配置方式,实现接口方式,注解方式等。由框架实现
Handler:处理器。Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
HandlAdapter:处理器适配器。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。由框架实现。
ModelAndView:是springmvc的封装对象,将model和view封装在一起。
ViewResolver:视图解析器。ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
View:是springmvc的封装对象,是一个接口, springmvc框架提供了很多的View视图类型,包括:jspview,pdfview,jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面
45.聊一下你对AOP的理解吧?
AOP,面向切面思想;降低模块的耦合度、使系统容易扩展、提高代码复用性;重复的代码抽离出来,写成公共方法;
如日志收集、事务管理、安全检查(权限校验)、缓存、对象池管理等
AOP的一些概念:
切入点:即织入切面。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
通知:我们也叫它处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
切面:即Pointcut(切入点)和Advice(通知)。
连接点:是程序执行的一个点。
织入:就是通过动态代理,在目标对象方法中执行处理内容的过程。
目标对象:被一个或者多个切面所通知的对象。
AOP代理: 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理
Advice通知的类型有哪几种?
前置通知:在目标方法被调用之前调用通知功能;
后置通知:在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知:在目标方法成功执行之后调用通知;
异常通知:在目标方法抛出异常后调用通知;
环绕通知:通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
46.AspectJ AOP 和 Spring AOP 有什么区别?
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理
静态代理的代表为AspectJ;原始的AOP实现技术,提供了完整的AOP解决方案,相对于Spring AOP也显得更为复杂,性能也比Spring AOP好
动态代理则以Spring AOP为代表;通过Spring IoC提供一个简单的AOP实现,以解决编码人员面临的最常出现的问题。这并不是完整的AOP解决方案,它只能用于Spring容器管理的beans;
了解JDK动态代理和CGLIB动态代理的原理么?他俩有哪些区别?
JDK动态代理:是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理,他有一个限制,就是它只能为接口创建代理实例,那么对于没有通过接口定义业务方法的类,就要用CGLIB动态代理了。
CGLIB动态代理:是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
两者对比: JDK动态代理是面向接口的;CGLib动态代理是通过字节码底层继承要代理类来实现(对指定的类生成一个子类,覆盖其中的方法)
47.什么是基于Java的Spring注解配置? 给一些注解的例子
基于Java的配置,允许你在少量的Java注解的帮助下进行大部分Spring配置,而非通过XML文件;
以@Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个是通过@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文;
48.MySQL中你用过的INSERT插入方式都有哪几种?
1、普通插入语句
INSERT INTO table (`a`, `b`, `c`, ……) VALUES ('a', 'b', 'c', ……);
2、插入或更新
如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就更新该记录,此时,可以使用"INSERT INTO … ON DUPLICATE KEY UPDATE …"语句
情景示例:这张表存了用户历史充值金额,如果第一次充值就新增一条数据,如果该用户充值过就累加历史充值金额,需要保证单个用户数据不重复录入
?? ?INSERT INTO total_transaction (t_transId,username,total_amount,last_transTime,last_remark)?
? ??? ?VALUES (null, 'chenhaha', 30, '2020-06-11 20:00:20', '充会员')?
? ??? ?ON DUPLICATE KEY UPDATE ?total_amount=total_amount + 30, last_transTime='2020-06-11 20:00:20', last_remark ='充会员'
3、插入或替换
如果我们想插入一条新记录(INSERT),但如果记录已经存在,就先删除原记录,再插入新记录;此时,可以使用"REPLACE INTO"语句,这样就不必先查询,再决定是否先删除再插入
情景示例:这张表存的每个客户最近一次交易订单信息,要求保证单个用户数据不重复录入,且执行效率最高,与数据库交互最少,支撑数据库的高可用
?? ?REPLACE INTO last_transaction (transId,username,amount,trans_time,remark)?
? ??? ?VALUES (null, 'chenhaha', 30, '2020-06-11 20:00:20', '会员充值')?? ?
4、插入或忽略
如果我们希望插入一条新记录(INSERT),但如果记录已经存在,就啥事也不干直接忽略,此时,可以使用INSERT IGNORE INTO …
实例:INSERT IGNORE INTO users_info (id, username, sex, age ,balance, create_time)?
? ?VALUES (null, 'chenhaha', '男', 26, 0, '2020-06-11 20:00:20');
49.见过大量数据同时插入的场景么?有哪些处理方式?给你会怎么设计?
1、单条循环插入
逐条插入优化成本太高,每一条都要访问一次数据库,数据库访问压力大;可以用批量插入的方法;
2、修改SQL语句批量插入
批量插入会大大提升数据插入速度,当有较大数据插入操作是用批量插入优化;可以先把数据存到内存中(集合),再用数据层集合插入的API更快
3、分批量多次循环插入
上面的批量插入需要修改数据库配置,如果不方便的修改的时候可以后端代码控制,分批批量插入
如果插入速度依旧很慢,还有没有其他的优化手段?
方案一:通过show processlist;命令,查询是否有其他长进程或大量短进程抢占线程池资源,看能否通过把部分进程分配到备库从而减轻主库压力,或者,先把没用的进程杀掉一些
方案二:大批量导数据,也可以先关闭索引,数据导入完后再打开索引
方案三:拖过中间件缓存慢慢添加到数据库中(MQ,Redis)
50:你对建表字段是否该使用not null这个问题怎么看?
尽量把数据库列设置为not null,因为NULL值很容易导致我们在统计、查询表数据时出错,业务层面容易报空指针异常
51.你在日常开发中,发现bug后的解决思路一般是怎么样的?
1.知道预期的行为是什么,知道bug和预期行为的差别;
2.稳定复现这个bug;
3.猜一下到底什么导致这个bug,改一点试一试;
4.看一看这个bug是否被修复了,如果不是,回到3,如果是,继续到5;
5.多运行几次,确认这个bug真的稳定不会出现了,如果依然出现,回到3,否则继续到6;
6.找人来做code review,确保没有自己没有考虑到的方面。
7.记录这个bug
52.先说一下大家为什么要选择ConcurrentHashMap?
在并发编程中使用HashMap可能导致程序死循环。而使用线程安全的HashTable效率又非常低下,基于以上两个原因,便有了ConcurrentHashMap;
ConcurrentHashMap所使用的分段锁技术,首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问
53.ConcurrentHashMap在JDK1.7、1.8中都有哪些优化?
JDK1.7:ReentrantLock+Segment+HashEntry
JDK1.8:Synchronized+CAS+Node(HashEntry)+红黑树
从JDK1.7版本到JDK1.8版本抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性;
同时,也把之前的HashEntry改成了Node,作用不变,当Node链表的节点数大于8时Node会自动转化为TreeNode,会转换成红黑树的结构;
JDK1.8为什么使用Synchronized来代替ReentrantLock?
1 因为粒度降低了,在相对而言的低粒度加锁方式,synchronized并不比ReentrantLock差,在粗粒度加锁中ReentrantLock可能通过Condition来控制各个低粒度的边界,更加的灵活,而在低粒度中,Condition的优势就没有了
2 在大量的数据操作下,对于JVM的内存压力,基于API的ReentrantLock会开销更多的内存,虽然不是瓶颈,但是也是一个原因之一
讲讲ConcurrentHashMap的 get put 过程?
JDK1.7 —— put操作
JDK1.7 —— get操作
JDK1.8 —— put操作
JDK1.8 —— get操作
ConcurrentHashMap 的 get 方法是否要加锁,为什么?
get 方法不需要加锁。因为 Node 的元素 value 和指针 next 是用 volatile 修饰的(可见性),在多线程环境下线程A修改节点的 value 或者新增节点的时候是对线程B可见的
54:说说你对Mybatis的理解?
Mybatis是一个持久层的框架,内部封装了jdbc,开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程;
mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回;
MyBatis 支持定制化 SQL、存储过程以及高级映射;
说一下MyBatis的工作原理和流程吧?
1.读取 MyBatis 配置文件:mybatis-config.xml为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息
2.加载映射文件:映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载
3.构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4.创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法
5.Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护
6.MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息
7.输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。
8.输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型
列举几个MyBatis的核心组件,说说分别干啥用?
SqlSession:用于和数据库交互的会话,完成必要数据库增删改查功能
Configuration:MyBatis所有的配置信息都维持在Configuration对象之中
SqlSource:负责根据用户传递的parameterObject,动态生成SQL语句,将信息封装到BoundSql对象中,并返回
..
55:Mybatis动态sql是做什么的?都有哪些动态sql?
动态sql是指在进行sql操作的时候,传入的参数对象或者参数值,根据匹配的条件,有可能需要动态的去判断是否为空、循环、拼接等情况,用于辅助开发者更方便的进行半自动化的SQL开发;
Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim、where、set、foreach、if、choose、when、otherwise、bind
Xml映射文件中,除了常见的select,insert,updae,delete标签之外,你还常用哪些标签?
用于Mybatis的Mapper文件中,有很多常见标签如:
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
1.实体类和数据表中的列名是否一一对应,如果对应上就可以直接返回。
2.使用sql列的别名功能,将列的别名书写为对象属性名,强行与实体类保持一致,但不方便维护。
3.使用resultMap标签,逐一定义数据库列名和对象属性名之间的映射关系,处理起来就比较清晰
MyBatis中接口绑定你都用过哪几种方式?
1.注解绑定:在接口的方法上面加上 @Select、@Update等注解里面包含Sql语句来绑定,Sql语句比较简单的时候
2.Mapper标签绑定:通过xml里面写SQL来绑定, 指定xml映射文件里面的namespace必须为接口类的全路径名,select标签中的id来定义接口名称,须一一对应
我们知道insert 方法总是返回一个int值 ,这个值代表的是插入的行数。那我如何获取自动生成的主键(id)值?
如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。
有两个XML文件和这个Dao建立关系,如何避免冲突?
不管有几个XML和Dao建立关系,只要保证namespace+id唯一即可
56:用过Mybatis的一级、二级缓存么?用过的话说一下原理。
将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能
一级缓存?
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。
与Redis同理,用户发起查询请求,查找某条数据,sqlSession先去缓存中查找,是否有该数据,如果有,直接返回;如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用
但sqlSession执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读
二级缓存?
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大
MyBatis对二级缓存的支持粒度很细,它会指定某一条查询语句是否使用二级缓存
一级缓存和二级缓存的使用顺序?
二级缓存> 一级缓存 > 数据库
57:你一般怎么修改Linux目录、文件权限?
修改文件、目录一般会使用chmod,利用chmod 可以控制文件如何被他人所调用。
另外,当确定了一个文件的访问权限后,用户可以利用Linux系统提供的chmod 命令来重新设定不同的访问权限。也可以利用chown 命令来更改某个文件或目录的所有者。
利用chgrp 命令来更改某个文件或目录的用户组。
Linux/Unix 的文件调用权限分为三级 :
文件所有者(Owner)
用户组(Group)
其它用户(Other Users)
58:kill -9 和kill的区别?
都可以用来杀死进程;
kill命令默认的信号就是15,也就是 kill -15 ,被称为优雅的退出;发出该指令后系统系统有三种执行方式:1.立即停止程序2.释放响应资源后停止程序3.忽略该信号,继续执行程序
因为kill -15信号只是通知对应的进程要进行"安全、干净的退出"
kill -9在执行时,应用程序是没有时间进行准备工作的,立即杀掉程序,所以这通常会带来一些副作用,如数据丢失或者终端无法恢复到正常状态等
59.说一下你对ReentrantLock的理解?
ReentrantLock:可重入锁,它拥有与synchronized相同的并发性和内存语义,并提供了超出synchonized的其他高级功能(例如,中断锁等候、条件变量等)
?????????????主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁
????????????CAS:即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。
?????????否则,处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。
????????????AQS:AQS的本质上是一个同步器/阻塞锁的基础框架,其作用主要是提供加锁、释放锁,并在内部维护一个FIFO等待队列,用于存储由于锁竞争而阻塞的线程
你认为 ReentrantLock 相比 synchronized 都有哪些区别?
共同点:
1.都是用来协调多线程对共享对象、变量的访问
2.都是可重入锁,同一线程可以多次获得同一个锁
3.都保证了可见性和互斥性
不共同点:
1.ReentrantLock 显示获得释放锁,synchronized 隐式获得释放锁;
2.ReentrantLock 是API 级别的,synchronized 是 JVM 级别的;
3.ReentrantLock 是同步非阻塞,采用的是乐观并发策略;synchronized 是同步阻塞,使用的是悲观并发策略
60.解释一下公平锁和非公平锁?
公平锁就是先等待的线程先获锁;ReenTrantLock可以指定是公平锁还是非公平锁,而synchronized只能是非公平锁
61:你说一下常用的排序算法都有哪些?
插入排序,冒泡排序,快速排序,选择排序等
谈一谈你对快速排序的理解吧?
快速排序是对冒泡排序的一种改进,从排序数组中找出一个数,可以随机取,也可以取固定位置,一般是取第一个或最后一个,称为基准数。然后将比基准小的排在左边,比基准大的放到右边
说一下快排的算法原理?
1.选定一个基准数作为中心点(Pivot)
2.将大于Pivot的数字放到Pivot的左边
3.将小于Pivot的数字放到Pivot的右边
4.第一次排序结束后,分别对左右子序列继续递归重复前三步操作(取第二个基准数重复操作),直到左右子序列长度只有单个元素为止
62.说一下构成递归的前提条件有啥?
函数内部调用的自身函数的编程技巧称为递归;
构成递归的条件:
1.子问题须与原始问题为同样的事,且更为简单;
2.不能无限制地调用本身,须有个出口,化简为非递归状况处理
递归都有哪些优缺点?
优点:
递归的实现明显要比循环简单得多
缺点:
1.递归由于是函数调用自身,而函数调用是有时间和空间的消耗的
2.调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间
63. 10亿个数中找出最大的100000个数?
方法一:局部淘汰法,该方法与排序方法类似,用一个容器保存前100000个数,然后将剩余的所有数字——与容器内的最小数字相比,如果所有后续的元素都比容器内的100000个数还小,那么容器内这个100000个数就是最大100000个数。如果某一后续元素比容器内最小数字大,则删掉容器内最小元素,并将该元素插入容器,最后遍历完这1亿个数,得到的结果容器中保存的数即为最终结果了
方法二:分治法,将10亿个数据分成1000份,每份100万个数据,找到每份数据中最大的100000个,最后在剩下的1000 * 100000个数据里面找出最大的100000个
方法二:如果这1亿个书里面有很多重复的数,先通过Hash法,把这10亿个数字去重复,这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间,然后通过分治法或最小堆法查找最大的100000个数
64:说说什么分布式事务?解释一下什么是CAP?
分布式事务,是分布式环境下,对数据一系列操作的集合,通过以下三个特征是否实现来表述
一致性 : 客户端任何时候看到各节点的数据都是一致的
可用性: 任何时刻都可以提供读写
分区容错性:网络故障、某些节点不能通信的时候系统仍能继续工作
AP(高可用&&分区容错):允许至少一个节点更新状态会导致数据不一致,即丧失了C性质(一致性)。会导致全局的数据不一致。
CP(一致&&分区容错):为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质(可用性)。分区同步会导致同步时间无限延长(也就是等数据同步完成之后才能正常访问)
CA(一致&&高可用):两个节点可以互相通信,才能既保证C(一致性)又保证A(可用性),这又会导致丧失P性质(分区容错性)。这样的话就分布式节点受阻,无法部署子节点,放弃了分布式系统的可扩展性。因为分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,节点间的分区故障是必然发生的,所以在分布式系统中分区容错性是必须要考虑的
CAP理论告诉我们在分布式存储系统中,最多只能实现上面的两点;
怎么理解强一致性、弱一致性和最终一致性?
强一致性:当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。
弱一致性:系统并不保证后续进程或者线程的访问都会返回最新的更新过的值。
最终一致性:弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。
65:了解BASE理论么?
在分布式系统中,我们往往追求的是可用性,它的重要程序比一致性要高,BASE理论,它是用来对CAP定理进行进一步扩充的;
BASE理论指的是:基本可用,软状态,最终一致性;
基于BASE理论,举几个实际的例子?
以12306订票系统为例
1.流量削峰 可以在不同的时间,出售不同区域的票,将访问请求错开,削弱请求峰值。比如,在春运期间,深圳出发的火车票在 8 点开售,北京出发的火车票在 9 点开售。
2 延迟响应 在春运期间,自己提交的购票请求,往往会在队列中排队等待处理,可能几分钟或十几分钟后,系统才开始处理,然后响应处理结果。
3 体验降级 比如用小图片来替代原始图片,通过降低图片的清晰度和 大小,提升系统的处理能力
4 过载保护 比如把接收到的请求放在指定的队列中排队处理,如果请求等 待时间超时了(假设是 100ms),这个时候直接拒绝超时请求;再比如队列满了之后,就 清除队列中一定数量的排队请求,保护系统不过载,实现系统的基本可用
66:实现分布式事务一致性的方法有哪些?
二阶段提交协议、三阶段提交协议和Paxos算法
67.你遇到过哪些OOM情况,什么原因造成的?怎么解决的?
1、Java heap space(堆内存)
当堆内存没有足够空间存放新创建的对象时,就会抛出OOM(内存不足 内存溢出)
原因:
1.请求创建一个超大对象,通常是一个大数组
2.超出预期的访问量/数据量,通常是上游系统请求流量飙升,常见于各类促销/秒杀活动,可以结合业务流量指标排查是否有尖状峰值
3.内存泄漏,大量对象引用没有释放,JVM 无法对其自动回收,常见于使用了 File 等资源没有回收
解决方案
针对大部分情况,通常只需要通过-Xmx 参数调高 JVM 堆内存空间即可
如果仍没有解决,可以:
1.如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全部结果,而没有做结果数限制。
2.如果是业务峰值压力,可以考虑添加机器资源,或者做限流降级。
3.如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接
2、GC overhead limit exceeded
应用程序已经基本耗尽了所有可用内存, GC 也无法回收
解决方式和Java heap space类似
3、Permgen space
该错误表示永久代(Permanent Generation)已用满,通常是因为加载的 class 数目太多或体积太大
原因:
永久代存储对象主要包括以下几类:
1.加载/缓存到内存中的 class 定义,包括类的名称,字段,方法和字节码;
2.常量池;
3.对象数组/类型数组所关联的 class;
4.JIT 编译器优化后的 class 信息。
解决方案
1.程序启动报错,修改 -XX:MaxPermSize 启动参数,调大永久代空间。
2.应用重新部署时报错,很可能是没有应用没有重启,导致加载了多份 class 信息,只需重启 JVM 即可解决。
3.运行时报错,应用程序可能会动态创建大量 class,而这些 class 的生命周期很短暂,但是 JVM 默认不会卸载 class,可以设置 -XX:+CMSClassUnloadingEnabled 和 -XX:+UseConcMarkSweepGC这两个参数允许 JVM 卸载 class。
4、Unable to create new native thread
每个 Java 线程都需要占用一定的内存空间,当JVM 向底层操作系统请求创建一个新的 native 线程时,如果没有足够的资源分配就会报此类错误
原因分析
1.线程数超过操作系统最大线程数 ulimit 限制;
2.线程数超过 kernel.pid_max(只能重启);
3.native 内存不足;
解决方案
1.升级配置,为机器提供更多的内存;
2.限制线程池大小;
3.使用 -Xss 参数减少线程栈的大小;
4.修复应用程序的线程泄漏问题;
68.说说JVM的内存结构?
线程私有:程序计数器、JVM栈、本地栈
线程共享:堆、方法区(永久代或元空间、代码缓存)
程序计数器:当前线程所执行的字节码的行号指示器
JVM栈:Java方法执行的内存模型
本地方法栈:与JVM栈所发挥的作用是非常相似的,本地方法栈则是为虚拟机使用到的Native(本机)方法服务
堆内存:被所有线程共享的一块内存区域,在虚拟机启动时创建;存放对象实例,几乎所有的对象实例都在这里分配内存;
方法区:各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
69.说一下new一个对象的过程是什么样的?
当虚拟机遇见new关键字时候,实现判断当前类是否已经加载,如果类没有加载,首先执行类的加载机制,加载完成后再为对象分配空间、初始化等;
首先校验当前类是否被加载,如果没有加载,执行类加载机制
1加载:就是从字节码加载成二进制流的过程
2.验证:当加载完成之后,当然需要校验Class文件是否符合虚拟机规范,跟我们接口请求一样,第一件事情当然是先做个参数校验了
3.准备:为静态变量、常量赋默认值
4.解析:把常量池中符号引用(以符号描述引用的目标)替换为直接引用(指向目标的指针或者句柄等)的过程
5.初始化:执行static代码块(cinit)进行初始化,如果存在父类,先对父类进行初始化
当类加载完成之后,紧接着就是对象分配内存空间和初始化的过程
1.首先为对象分配合适大小的内存空间
2.接着为实例变量赋默认值
3.设置对象的头信息,对象hash码、GC分代年龄、元数据信息等
4.执行构造函数(init)初始化
70:Bean 的加载过程是怎样的?
Spring 的工作流主要包括以下两个环节:
解析:读 xml 配置,扫描类文件,从配置或者注解中获取 Bean 的定义信息,注册一些扩展功能。
加载:通过解析完的定义信息获取 Bean 实例
一个 Bean 加载主要会经历这么几个阶段:
1.获取 BeanName,对传入的 name 进行解析,转化为可以从 Map 中获取到 BeanDefinition 的 bean name
2.合并 Bean 定义,对父类的定义进行合并和覆盖,如果父类还有父类,会进行递归合并,以获取完整的 Bean 定义信息。
3.实例化,使用构造或者工厂方法创建 Bean 实例。
4.属性填充,寻找并且注入依赖,依赖的 Bean 还会递归调用 getBean 方法获取。
5.初始化,调用自定义的初始化方法。
6.获取最终的 Bean,如果是 FactoryBean 需要调用 getObject 方法,如果需要类型转换调用 TypeConverter 进行转化
总结一下大致步骤:
1.加载XML文件,封装成Resource对象;
2.调用Reader对象方法读取XML文件内容,并将相关属性放到BeanDefinition实例;
3.将BeanDefinition对象放到BeanFactory对象,用于调用;
什么是循环依赖?
举个例子,这里有三个类 A、B、C,然后 A 关联 B,B 关联 C,C 又关联 A,这就形成了一个循环依赖。
循环依赖得解决思路是什么样的?
Spring解决循环依赖,主要的思路就是依据三级缓存(解链)
在实例化A时调用doGetBean,发现A依赖的B的实例,此时调用doGetBean去实例B,实例化的B的时候发现又依赖A,如果不解决这个循环依赖的话此时的doGetBean将会无限循环下去,导致内存溢出,程序奔溃;
如果Spring引用一个早期对象,并且把这个"早期引用"并将其注入到容器中,让B先完成实例化,此时A就获取B的引用,完成实例化。
71:@Resource和@Autowired有什么区别?
@Autowired: 根据类型注入
@Resource: 默认根据名字注入,其次按照类型搜索
@Autowired: @Qualifie("userService") 两个结合起来可以根据名字和类型注入,等同于@Resource
@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上
@Autowired默认按byType自动装配,而@Resource默认byName自动装配
@Autowired只包含一个参数:required,表示是否开启自动注入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。
@Autowired能够用在:构造器、方法、参数、成员变量和注解上,而@Resource能用在:类、成员变量和方法上
@Autowired是spring定义的注解,而@Resource是JSR-250定义的注解
72:Spring 的事务传播行为有哪些,都有什么作用?
事务的传播特性:当系统中存在两个事务方法时(我们暂称为方法A和方法B),如果方法B在方法A中被调用,那么将采用的事务形式就是事务的传播的传播特性
1、PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
2、PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
3、PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作
73.谈一下你对 Nginx 的理解?
高性能的 HTTP 服务器和反向代理服务器;Nginx 可以作为一个 HTTP 服务器进行网站的发布处理,另外 Nginx 也可以作为反向代理进行负载均衡的实现;
支持数以百万级别的TCP连接
高度的模块化和自由软件许可证允许第三方软件插入
跨平台服务器
正向代理和反向代理区别在哪?
代理就是一个代表、一个渠道;
正向代理和反向代理的关键不同点在于是否处于同一个网络环境下;
正向代理:代理的是客户端,代客户端发出请求,是一个位于客户端和目标服务器间的服务器,为了从目标服务器取得内容,客户端向代理发送一个请求并指定目标(内网服务器),然后代理向目标服务器转交请求并将获得的内容返回给客户端
??????用途:????访问原来无法访问的资源,如外网、办公内网;
????可以做缓存,加速访问资源;
????对客户端访问授权,上网进行认证;
????代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息;
反向代理:它代理的是服务端,代服务端接收请求,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息
??????用途:保证内网的安全,通常将反向代理服务器配置为公网访问地址,代理的Web服务器是内网IP
????负载均衡,通过反向代理服务器来优化每个单机服务实例的负载
????
74:常用的Nginx做负载均衡的策略有哪些?
轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除
权重 weight:指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的?
ip_hash( IP绑定):每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题
fair(第三方插件):根据服务器的响应时间来分配请求,响应时间短的优先分配,即负载压力小的优先会分配。
url_hash(第三方插件):按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
75:说几个你常用的 nginx 命令吧?
nginx -s reopen????#重启Nginx
nginx -s reload????#重新加载Nginx配置文件,热部署
nginx -s stop????#强制停止Nginx服务,类似kill -9 pid
nginx -s quit????#优雅地停止Nginx服务(即处理完所有请求后再停止服务),类似kill pid
nginx -V????????#显示版本和配置选项信息,然后退出
nginx -t????????#检测配置文件是否有语法错误,然后退出
76:说一下你对聚集索引与非聚集索引的理解,以及他们的区别?
索引像一个汉语字典,聚集索引是根据拼音查询,而非聚集索引是根据偏旁部首查询;
聚集索引:索引中键值的逻辑顺序决定了表中相应行的物理顺序,我们叶子结点直接对应的实际数据,当索引值唯一时,使用聚集索引查找特定的行效率很高
????当某列满足两个条件时,我们可以创建聚集索引:1 数据存储有序(如自增)2 key值应当唯一
非聚集索引:非聚集索引的数据存储在一个位置,索引存储在另一位置。由于数据和非聚集索引是分开存储的,因此在一个表中可以有多个非聚集索引
区别:
????????1.单表中只能有一个聚集索引,而非聚集索引单表可以存在多个。????
????????2.聚簇索引的叶节点就是数据节点,非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块
????????3.聚集索引:物理存储按照索引排序;非聚集索引:物理存储不按照索引排序;
聚集索引一定比非聚集索引性能优么?
并不是。既然只输出两列,我们可以在学分以及学生姓名上创建联合非聚集索引,此时的索引就形成了覆盖索引,即索引所存储的内容就是最终输出的数据,这种索引当然比以学分为聚集索引做查询性能好
77:说一说你对 B树 和 B+树 的理解吧?
B树:一种多路自平衡搜索树,它类似普通的二叉树,但是B书允许每个节点有更多的子节点。
特点:
1.所有键值分布在整个树中
2.任何关键字出现且只出现在一个节点中
3.搜索有可能在非叶子节点结束
4.在关键字全集内做一次查找,性能逼近二分查找算法
B+树:B+树是B树的变体,也是一种多路搜索树
优点:1. B+树的磁盘读写代价更低
??????????2. B+树的查询效率更加稳定
B+树与B树的不同在于:
1.所有关键字存储在叶子节点,非叶子节点不存储真正的data,从而可以快速定位到叶子结点
2.所有叶子节点增加了一个链指针,意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同,很适合查找范围数据。说明支持范围查询和天然排序
78:说一下你对最左前缀原则的理解吧?
在建立联合索引的时候,会让我们选择索引的顺序,比如我们想在a,b,c三个字段上建立一个联合索引,我们可以选择自己想要的优先级,(a、b、c),或是 (b、a、c) 或者是(c、a、b) 等顺序;
在走索引的时候要符合最左前缀原则;
违背最左原则导致索引失效的情况:1、查询条件中,缺失优先级最高的索引 “a” 2、查询条件中,缺失优先级居中的索引 “b”
79:说说你对RPC框架的理解?
RPC即远程过程调用,是分布式系统常见的一种通信方法。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。
(RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果)
RPC框架实现原理是什么样的?
在RPC框架中主要有三个角色:提供者、消费者和注册中心;
提供者: 暴露服务的服务提供方。
调用者: 调用远程服务的服务消费方。
注册中心: 服务注册与发现的注册中心。
提供者--(注册服务)--注册中心--(获取提供者地址)--调用者--(调用接口)--提供者
RPC整个调用过程:
1.建立通信 :主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
2.服务寻址 :A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么;常用的服务发现可用redis或者zookeeper来注册服务
??????1.从服务提供者的角度看:当提供者服务启动时,需要自动向注册中心注册服务;
??????2.当提供者服务停止时,需要向注册中心注销服务;
??????3.提供者需要定时向注册中心发送心跳,一段时间未收到来自提供者的心跳后,认为提供者已经停止服务,从注册中心上摘取掉对应的服务。
??????4.从调用者的角度看:调用者启动时订阅注册中心的消息并从注册中心获取提供者的地址;
??????5.当有提供者上线或者下线时,注册中心会告知到调用者;
??????6.调用者下线时,取消订阅
3.网络传输:序列化: 由于网络协议是基于二进制的,所有我们传输的参数数据都需要先进行序列化(Serialize)或者编组(marshal)成二进制的形式才能在网络中进行传输。
??????反序列化:接收到其他机器的应用发来的请求之后,又需要对接收到的参数等信息进行反序列化操作(序列化的逆操作),即将二进制信息恢复为内存中的表达方式,然后再找到对应的方法(寻址的一部分)进行本地调用,之后得到调用的返回值。
4.服务调用:B机器进行本地调用之后得到了返回值,此时还需要再把返回值发送回A机器,同样也需要经过序列化操作,然后再经过网络传输将二进制数据发送回A机器,而当A机器接收到这些返回值之后,则再次进行反序列化操作,恢复为内存中的表达方式,最后再交给A机器上的应用进行相关处理
80:常见的RPC框架有哪些?
Dubbo:Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。?
Spring Cloud:Spring Cloud由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案
81:说说RPC和SOA、SOAP、REST的区别吧?
REST:可以看着是HTTP协议的一种直接应用,默认基于JSON作为传输格式,使用简单,学习成本低效率高,但是安全性较低
SOAP:SOAP是一种数据交换协议规范,是一种轻量的、简单的、基于XML的协议的规范
SOA:面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。SOA是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。
82:面向对象程序设计(OOP)的六大原则分别有哪几个?
开闭原则????对扩展开放,对修改关闭。
单一职责原则:即一个类只负责相应领域的职责,即不要存在多于一个导致类变更的原因。
里氏代换原则:子类型必须能够替换它们的父类型。一个软件实体如果使用的是一个父类,那么当把这个父类替换成继承该父类的子类,程序的行为不会发生任何变化。软件实体察觉不出父类对象和子类对象的区别。
依赖倒置原则:要依赖于抽象,不要依赖于具体。客户端依赖于抽象耦合。抽象不应当依赖于细节;细节应当依赖于抽象;要针对接口编程,不针对实现编程。
接口隔离原则:客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
最少知识原则:对象与对象之间应该使用尽可能少的方法来关联,避免千丝万缕的关系
83:你说一下什么是设计模式?
设计模式是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性。
我们使用设计模式最终的目的是实现代码的 高内聚 和低耦合
那你怎么理解高内聚和低耦合?
耦合:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差
内聚:表示内部间聚集、关联的程度,那么高内聚就是指要高度的聚集和关联。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。
84:设计模式有哪几种?
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
工厂方法模式(Factory Pattern)????定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法模式是一个类的实例化延迟到子类。
抽象工厂模式(Abstract Factory Pattern)????提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
单例模式(Singleton Pattern)????确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
建造者模式(Builder Pattern)????将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
原型模式(Prototype Pattern)????用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
适配器模式(Adapter Pattern)????将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作
桥接模式(Bridge Pattern)????将抽象部分与它的实现部分分离,使它们都可以独立地变化。
组合模式(Composite Pattern)????组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。使得用户对单个对象和组合对象的使用具有一致性。
装饰器模式(Decorator Pattern)????动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。
外观模式(Facade Pattern)????为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
享元模式(Flyweight Pattern)????运用共享技术有效地支持大量细粒度对象的复用。
代理模式(Proxy Pattern)????为其他对象提供一种代理以控制对这个对象的访问。
责任链模式(Chain of Responsibility Pattern)????使多个对象都有机会处理请求,从而避免请求发送者与接收者耦合在一起。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
命令模式(Command Pattern)????将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
解释器模式(Interpreter Pattern)????给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
迭代器模式(Iterator Pattern)????提供一种方法来访问聚合对象中的各个元素,而不用暴露这个对象的内部表示。
中介者模式(Mediator Pattern)????用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
备忘录模式(Memento Pattern)????在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
观察者模式(Observer Pattern)????定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
状态模式(State Pattern)????允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
策略模式(Strategy Pattern)????定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。
模板方法模式(Template Pattern)????定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
访问者模式(Visitor Pattern)????表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
85:说说你对消息队列的理解,消息队列为了解决什么问题?
消息队列应用的场景大致分为三类:解耦、异步、削峰
解耦:生产者生成和发送消息到消息队列,消费者从消息队列中取走消息进行处理,称为消费,使用消息队列将“生产者”和“消费者”之间的操作关联解耦,易于扩展
? ? ? ? ? ?示例:系统A为支付系统,一开始用户支付完调用日志记录系统B记录就完了,后来内容越来越多,支付完成要调用加积分系统C、短信通知系统D、优惠券系统E等,
? ? ? ? ? ?? ? ? ? 这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,如果A系统挂了会牵连到很多其他业务,引入 MQ,其他系统监听消息列队中的消息即可
? ? ? ? ? ?问题:数据一致性;A 系统把消息放进消息列队后,如果后面系统出现错误,数据处理不了就会导致一致性问题,解决方式是把所有的服务都放到一个事务里,一起成功或者失败
异步:可以把不重要业务或者数据放在消息列队里面慢慢处理;
削峰:比如代售抢票业务,平时每天QPS也就50左右,系统没有异常,到了春运每秒并发请求数量突然会暴增到10000以上,导致系统崩溃,但是高峰期一过就又没人了,QPS回到50;
? ? ? ? ? 如果这里使用 MQ,每秒 1w 个请求写入 MQ,然后系统在MQ消费系统的QPS的最大值,就不会导致系统崩溃又可以最大量处理业务了
消息队列有什么优缺点?
1.系统可用性降低 ?:系统引入的外部依赖越多,越容易挂掉
2.系统复杂度提高 ?:怎么保证消息一定被消费?如何避免消息重复投递或重复消费?数据丢失怎么办?怎么保证消息传递的顺序性?
3.一致性问题 ? ? ? ? :多系统间业务消费后异常问题
86:对于消息中间机,你们是怎么做技术选型的?
息队列中间件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ
Kafka:吞吐量10万级别,延迟ms级,消息可靠性有较低概率丢失
RabbitMQ:吞吐量万级别,延迟微秒级(最低),消息可靠性基本不丢
ActiveMQ:吞吐量万级别,延迟ms级,经过配置优化可以做到0丢失
RocketMQ:吞吐量10万级别,延迟ms级,经过配置优化可以做到0丢失
87:如何确保消息正确地发送至 RabbitMQ?如何确保消息接收方消费了消息?
发送方确认模式:
将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID,一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID),
如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条Nack(not acknowledged,未确认)消息,发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息
接收方确认机制:
消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。
这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性
如何保证MQ消息的可靠传输?
消息不可靠的情况可能是消息丢失,劫持等原因;
丢失又分为:生产者丢失消息、消息队列丢失消息、消费者丢失消息;
生产者丢失消息:从生产者弄丢数据这个角度来看,RabbitMQ提供消息确认机制(Confirm模式)模式来确保生产者不丢消息
消息队列丢数据:消息持久化
关消费者丢失消息:闭autoAck(消息应答),处理完之后发送ack(确认字符)给rabbitMq
88:JDK1.8的新特性有哪些?
1.接口的默认和静态方法:接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法
2.Lambda 表达式:例如:(x, y) -> { return x + y; };表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块
3.方法与构造函数引用:使用 :: 关键字来传递方法或者构造函数引用,如converter = something::startsWith
4.函数式接口:所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法
5.Annotation 注解:支持多重注解
6.Stream的使用:支持连续、并行聚集操作的元素,像linux的管道、或者链式编程,代码写起来简洁明了
7.Base64编码:Base64编码成为了Java类库的标准。Base64类同时还提供了对URL、MIME友好的编码器与解码器。
8.扩展注解的支持:注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解
9.编译器优化:方法的参数名加入了字节码中,这样在运行时通过反射就能获取到参数名,只需要在编译时使用-parameters参数
89:什么是内部类?内部类的作用?
将一个类定义在另一个类或者另一个方法里面,这样的类称着内部类;内部类能够访问外部类的所有成员,包括private修饰的
作用:
1.内部类可以很好的实现隐藏:非内部类是不可以使用 private和 protected修饰的,但是内部类却可以,从而达到隐藏的作用。同时也可以将一定逻辑关系的类组织在一起,增强可读性
2.间接的实现多继承:每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响
90:内部类有哪几种?分别介绍一下
?成员内部类:位于外部类成员位置的类(1.内部类可以访问外部类的所有成员就想访问自己的成员一样没有限制2.内部类中的 this 指的是内部类的实例对象本身,如果要用外部类的实例对象就可以用类名 .this 的方式获得3.内部类对象中不能有静态成员)
?局部内部类:定义在一个方法或者一个作用域里面的类(1.方法中的内部类没有访问修饰符, 即方法内部类对包围它的方法之外的任何东西都不可见2.方法内部类只能够访问该方法中的局部变量,所以也叫局部内部类)
静态内部类:可以用static修饰,这种用static修饰的内部类我们称作静态内部类,也称作嵌套内部类;特点:不能使用外部类的非static成员变量和成员方法
匿名内部类:一个没有名字的类,是内部类的简化写法,其实是继承该类或者实现接口的子类匿名对象(1.一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的事先或是覆盖2.只是为了获得一个对象实例,不需要知道其实际类型3.类名没有意义,也就是不需要使用到)
91:查找数组中重复元素和重复元素的个数?
方案一:两层循环比较方式+
int[] arr = [1,1,3,4,5,6,3,1,.........]
for (int x = 0; x < 10; x++) {
int sumber1 = 0;
for (int j = 0; j < 20; j++) {
//数组中的数循环比较
if (x == arr[j]) {
sumber1++;
} else {
continue;
}
}
方案二:转成Map集合处理方式
int[] arr = [1,1,3,4,5,6,3,1,.........]
//将数据转成集合
Map
for (int a = 0; a < arr.length; a++) {
if (map.get(arr[a])!=null){
map.put(arr[a],map.get(arr[a])+1);
}
else {
map.put(arr[a],1);
}
}
92:写个二分查找demo吧?
二分查找的条件是对一组有序数组的查找,所以要先确定查找的数组是否有序,在使用二分查找的时候先要对数组进行排序;
二分算法步骤:
1.首先确定整个查找区间的中间位置mid = ( left + right )/ 2
2.用待查关键字值与中间位置的关键字值进行比较;
★ 若相等,则查找成功
★ 若大于,则在后(右)半个区域继续进行折半查找
★ 若小于,则在前(左)半个区域继续进行折半查找
3.对确定的缩小区域再按折半公式,重复上述步骤,使用递归或普通while循环
93:把两个有序数组合并成一个有序数组?
对于两个有序数组arrayM,arrayN,数组长度分别为m和n
思路:
定义一个新数组newArray,长度为m+n;
定义两个index(int),如:int start=0,end=0,用来记录数组arrayM、arrayN的下标;
通过分别遍历数组arrayM和arrayN的方式来比较每个值的大小,并将值存放到newArray中;
判断start和end是否分别小于m和n,如果小于则继续执行,否则则表示有一个array遍历结束,然后将另一个array剩余部分追加到newArray中即可;
94:我们知道MQ有可能发生重复消费,啥导致的?
网络延迟、网络抖动;MQ默认允许消息重复发送。
如何保证消息不被重复消费?如何实现幂等性?
先根据主键查一下,如果这数据都有了,update就行;ES的插入采用了插入并更新的策略(发现相同的数据就直接更新);
如果是写 Redis,每次都是set,天然幂等性;
生产者发送每条数据的时候,里面加一个全局唯一的 id,每次消费先根据这个 id 去查一下,如果没有消费过,你就处理
数据库的唯一键来保证重复数据不会重复插入多条(唯一键约束了,重复数据插入只会报错)
95:RabbitMQ如何保证消息的顺序性?
核心思路就是根据业务数据关键值划分成多个消息集合,而且每个消息集合中的消息数据都是有序的,每个消息集合有自己独立的一个consumer。
多个消息集合的存在保证了消息消费的效率,每个有序的消息集合对应单个的consumer也保证了消息消费时的有序性。也就是保证了生产者 - MQServer - 消费者是一对一对一的关系
96:消息队列满了以后该怎么处理?比如现在大量消息在MQ里长时间积压,你会如何解决?
1.先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉;
2.kafka的话,比如新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量;
3.写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue里去;
4.接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据;
5.这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据;
6.等快速消费完积压数据之后,恢复成原来部署
MQ消息过期失效怎么办?
等服务器访问量比较低的时候,把过期的数据找回来并重新放到MQ中,让他重新消费一遍
如果mq经常写满你会怎么办?
程序优化不了的话只能扩容