常见问题及解决思路:
目录
rabbitmq如果有100万消息堆积在MQ了,如何解决(消息堆积如何解决?)
HashMap是怎么解决哈希冲突的?
HashTable,HashMap,ConcurrentHashMap的区别?
HashMap的扩容机制?
讲讲HashMap的哈希函数?
ArrayList和HashMap的fail-fast机制是什么?
HashMap需要重写equals方法吗?
HashMap树化的条件是什么?为什么这样设计?
什么是浅拷贝?什么是深拷贝?ArrayList在扩容时是深拷贝还是浅拷贝?
创建HashMap时为什么推荐指定初始容量?已知要添加元素数量为98,初始化容量应该为多少?
CopyOnWriteArrayList是怎么解决线程安全问题的?
CopyOnWriteArrayList可以保证数据的实时一致性吗?
HashMap如何解决hash冲突?
使用链表进行存储时查找到相应数据的时间复杂度为O(n)

当链表达到什么条件链表转化为红黑树?时间复杂度为O(logn)

如果没有达到数组的64个,首先考虑进行扩容(数组长度乘以2 )相应的会导致链表长度减少。
位运算符!!!
ArrayList扩容机制:

List<Integer>list = new ArrayList<>(4);初始化容量为4 扩容还是乘以1.5倍
如果没有指定初始化的容量默认为10
1 redis的数据结构有哪些
2 String有哪些使用场景 Hash有哪些使用场景 String和Hash在使用时有什么区别
3 Redis的List底层数据结构链表,List底层优化
4 set的特性,set的使用场景
5 zset基础,和跳跃列表实现
6 布隆过滤器实现方式
7 缓存击穿解决办法
8 数据一致性问题
9 分布式锁如何实现,使用String和Hash的区别
10 mq如何保证消息消费正常,根据这个场景设计一个方案
11 Redis是单线程的,但是为什么还那么快?
12 I/O多路复用
简历书写:



redis面试题:


redis缓存雪崩问题解决方法:
redis使用哨兵模式或者是集群模式进行部署,防止redis的单机节点down机。
设置缓存过期时间+1~60秒内随机值,让redis缓存不会出现多个key值同时失效的情况。
服务降级限流,使用nginx或者spring cloud gateway:
如果缓存未命中或 Redis 不可用,直接返回一个默认值(如空列表、默认配置等),避免数据库查询。(返回旧数据,返回默认值,熔断机制)
-
预防:缓存过期时间随机化、缓存预热、多级缓存、Redis 高可用。
-
降级:返回默认值、旧数据、熔断机制。
-
限流:令牌桶、漏桶算法,控制请求速率。
使用多级缓存。
redis与mysql数据库数据同步问题

redis持久化机制:
RDB:

AOF:

redis数据删除策略:
惰性删除:

优缺点:

定期删除:
优缺点:

第三种两种配合使用:
定期删除+逻辑删除
redis数据淘汰策略:
如果缓存过多,而内存过少,内存被占满了怎么办?
redis的8中内存数据淘汰策略:


redis的分布式锁:
在单服务器上的话使用的是使用jvm自带的锁即可锁住;
但是在集群分布的场景中,nginx反向代理需要映射到多个地址,jvm锁只能锁单机,无法真正的锁住。
setnx 与 redission对比
setnx的获取锁与设置过期时间两次操作,没有原子性,易死锁
SETNX lock_key "value" # 设置锁
EXPIRE lock_key 30 # 单独设置过期时间(非原子操作)
setnx 原子性操作 但是程序的执行时间如果大于设置的锁的过期时间,将会收到影响
SET lock_key "client123" NX EX 30

在外等待的线程会进入while循环中在有限等待后仍没有获取锁,才表明获取锁失败。
在高并发的使用场景中能够提高分布式锁的使用性能。

redisson实现的分布式锁-可重入:
底层进行了线程判断,判断是否是同一个线程,如果是同一个线程则允许重入;


mysql面试题:
优化
定位慢查询,sql执行计划,索引,sql优化经验
MySQL优化一:
在mysql中,如何定位慢查询?
方案一:

方案二:
MySQL自带慢日志
在mysql中进行配置 当运行时长超过了配置时长产生日志
sql语句执行缓慢如何分析呢:?

possible_key:当前sql可能会使用到的索引。
key:当前sql实际命中的索引。
key_len:索引占用的大小。
判断一条sql语句的性能好坏?根据type 从左到右性能依次减弱,“Extra”代表的是优化的地方。
尽量在index之上!

mysql底层
了解B树特点:

一个B树中如果一个节点最多有m个分支,那么就是m阶树。
一个节点最少有m/2(向上取整)个分支,元素个数比分支个数少一个(如果该节点有4个元素,那么就有5个分支)
查找操作:

插入操作:
插入前判断大于等于最大阶数3需要分裂

插入后:以该节点内元素个数除以2向上取整,提取14位置到父节点

数据库底层采用的是B+树:
B树与B+树的区别:
B树: B+树:


B+树的特点:

了解过数据库中的索引吗?

索引的底层数据结构了解过吗?

什么是聚簇索引(聚集索引),什么是非聚簇索引(二级索引)?

注意:聚集索引必须有,而且只有一个!!!。


什么是回表查询?
通过二级索引找到对应的主键值,到聚簇索引中查找到整行数据,这个过程就是回表。
知道什么叫覆盖索引吗?
覆盖索引是指查询使用了索引,并且需要返回的列,在该索引中已经全部能够查找到。
以是否需要回表查询来判断是否是覆盖索引。

使用覆盖索引查询超分页情况:
如果查询前10页可能性能较好,如果查询900万页之后的数据,性能就比较差,非覆盖索引的缺点就暴露出来了。

这是可以使用覆盖索引+子查询进行查询:
虽然使用两次查询,但是都是聚集索引查询,没有涉及到回表查询,效率较高
节省4秒查询时间。

索引的创建原则有哪些?

注意:尽量减少字符串类型索引的创建,因为字符串作为索引所占内存比较大,对索引数据机构压力比较大。对于前缀索引:截取字符串首部几位字符作为字符串。
要控制索引的个数,索引个数不是越多越好,索引越多,底层维护的索引数据结构就越大,对于增删改的影响比较大。
比较重要的几点:
1,数据量较大,且查询比较频繁的表
2,常作为查询条件,排序,分组的字段
3,尽量使用联合索引
4,要控制索引的数量
联合索引失效情况:

数库表、语句设计的优化:
表设计的优化:
每个字符串数据设置合适的数据类型,例如性别使用char来定义,避免使用varchar
char定长效率更高,varchar长度不固定,效率较低。
对于每个数值型的数据设计合适的数据类型(tinyint ,int, bigint)
sql语句优化:
这里的避免在where子句中使用表达式操作的原因:
注意:where子句的条件查询索引是建立在原始值的基础上建立的,如果进行了表达式处理后,在根据索引查找过过程中,MySQL没办法直接通过索引进行快速查找,而是需要对每行数据进行计算判断是否满足条件,这种情况属于索引失效。
union 和union all 区别在于union要进行去重排序,如果不需要的话合并尽量使用union all
使用innerjoin的话会将小表排在外面,进行比如两层for循环一次循环是3次,一次循环是100次,使用该方式,仅需连接数据库3次,每次进行100次操作。

mysql数据库的主从复制工作原理:
-
主库 接收所有的写操作(INSERT、UPDATE、DELETE),并将这些操作记录到 二进制日志(Binary Log,简称 binlog) 中。
-
从库 通过一个称为 I/O 线程 的进程连接到主库,读取主库的 binlog,并将其复制到自己的 中继日志(Relay Log) 中。
-
从库 的另一个线程 SQL 线程 会读取中继日志中的事件,并在从库上重新执行这些操作,从而实现数据的同步。
sql优化:
建表,数据类型
避免select * 避免回表查询
创建索引(规则)
避免索引失效
主从复制,读写分离。
什么是事务?(什么是ACID)
原子性:事务是不可分割的最小单元,要么全部成功,要么全部失败
隔离性:每个事务的处理是相互隔离的,在独立的环境下运行。
一致性:事务完成时,所有数据保持一致性。
持久性:十五一旦完成,数据库中数据的改变是持久的
并发事务问题:
脏读,幻读,不可重复读
解决方案:

对事物级别进行隔离:
serializable串行化:虽然所有并发事务问题都能解决,但防止事务的并发执行,性能较低。

mysql的默认事务隔离级别是可重复读。
事务的undo log和redo log的区别:

redo log:持久性,原子性


undo log:(逻辑日志)
作用:提供事务回滚和MVCC(多版本并发控制)。

可以实现事务的一致性和原子性,隔离性。
两者区别:
redo log:记录数据页的物理变化,服务宕机可用来同步数据。
undo log:记录的是逻辑地址,当事务回滚时,通过逆操作恢复原来的数据。
redo log 保证了事务的持久性,undo log保证了事务的原子性,一致性。
事务的隔离性如何保证?
使用MVCC多版本并发控制
MVCC实现原理:

解释一下MVCC?
多版本并发控制
mvcc的实现主要依赖数据库记录中的隐藏字段,undo log日志,readview
隐藏字段:


undo log特点:

在mvcc中实现的时undo log版本链:
mysql的主从同步原理:

数据库分库分表:
优化单一表数据量过大而产生的性能问题
避免io争抢并减少锁表的几率。
框架篇
spring 中的单例bean是线程安全的吗?

Spring中事务失效的场景有哪些?
场景一:异常捕获处理

注意:如果没有try catch包围的话,出现异常会正常出现事务回滚,但是如果我们自己进行了异常处理就会出现事务无法正常回滚。
原因:事务通知只有捕获到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理调异常,事务通知无法知悉。并且使用try 包围的模块,如果中间出现异常会导致后续无法进行,因此事务完整执行。
解决方法(事务回滚触发的条件是事务接收到未被处理的异常!!!!!!!!):
![]()
场景二:抛出检查异常

原因:Spring默认指挥回滚非检查异常。
解决方案方案:

总结:事务回滚前提:异常要抛出,不能私自处理,不抛出无法触发回滚,抛出的异常还必须要是非检查异常!!!!!!!。
场景三:非public方法导致的事务失效

原因:Spring为方法创建代理,添加事务通知,前提条件都是该方法是public的。
解决办法:改为public方法。
bean的循环依赖问题:
场景:beanA中注入B的实例话对象,beanB中注入A的实例化对象

使用二级缓存解决循环依赖:

使用三级缓存解决增强bean的循环依赖:

如何解决循环依赖中的构造方法的循环依赖?
构造方法懒加载,啥时间使用构造方法,啥时间在注入。

springmvc的执行流程(图像版):

springmvc的执行流程(语言版):

MyBatis的延迟加载:

只需要设置相应的fetchType = "lazy"
也可以设置全局延迟加载配置

在本案例中通过id查询到的用户属性的订单集合是空的,因为设置了懒加载,只用当虚线下面要用到用户订单集合属性时,才执行查询用户订单操作
spring cloud微服务架构的5大组件:
网关,注册中心,远程调用,服务熔断,负载均
负载均衡的策略有哪些?
主要记住:轮询,加权选择(响应时间越长,权重越小),随机选择一个可用的服务器
如何自定义负载均衡策略?


微服务中什么是服务雪崩,怎么解决这个问题?

RabbitMQ问题:
rabbitmq如何保证发送消息不丢失?
简单概括为一下几点:
1,发布确认
2,接收确认
3,队列持久化
4,消息持久化
5,消费者失败重试机制,多次重试后将消费者投递到异常交换机,交由人工处理

注意:消息在任何阶段都有可能丢失!!!!!
解决方法:
交换机持久化,队列持久化,消息持久化,
队列持久化:
@Bean
public Queue orderDelayQueue() {
return QueueBuilder.durable(ORDER_DELAY_QUEUE)
.build();
}
//用QueueBuilder构建队列,durable就是持久化的
交换机持久化:
@Bean
public CustomExchange orderDelayExchange() {
Map<String, Object> args = new HashMap<>();
// 设置延迟类型为直连
args.put("x-delayed-type", "direct");
// 交换机类型指定为 x-delayed-message
//下面的true代表是否持久化,false代表当没有队列与其绑定时是否自动删除
return new CustomExchange(ORDER_DELAY_EXCHANGE, "x-delayed-message", true, false, args);
}
消息持久化:
在声明队列时进行:
/*
*生成一个队列
* 1,队列名称
* 2,队列里面的消息是否持久化(磁盘),默认情况消息存储在内存中
* 3,该队列是否只供一个消费者进行消费,是否进行共享消费,true可以多个消费者消费,false表示只能一个消费者消费
* 4,是否自动删除,最后一个消费者断开连接以后,该队列是否自动删除,true自动删除 false 不自动删除
* 5,其他参数
*/
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
RabbitMQ消息的重复消费问题如何解决的?
解决方案:
每条消息设置一个唯一的标识id
使用分布式锁,数据库锁
rabbitmq如果有100万消息堆积在MQ了,如何解决(消息堆积如何解决?)
1,扩大队列的容量,设置堆积上限
2,设置多个消费者,提高消费速度
3,在消费者内开启线程池加快消息的处理速度
集合:
集合框架:
单列集合(collection):
list(有序):vector 数组结构 线程安全
arraylist 数组,线程不安全
linkedlist 链表结构 线程不安全
set(无序):hashset:哈希表 线程不安全
treeset:红黑树 线程不安全
双列集合(map): hashmap哈希表 哈希表 线程安全 有linkedhashmap继承 哈希表和连表结构
currenthashmap 哈希表 线程安全
hashtable 哈希表结构 线程安全 properties
treemap 红黑树

arraylist与linkedlist的区别:
查找效率,删除效率
从内存方面,前者内存存储练习,并且占用内存相对较少。
从线程安全方面:两者都是线程不安全的
如何解决线程安全问题:
1,在方法内部使用,局部变量则是线程安全的
2,保证线程安全:

使用Collections.synchronizedList来进行封装。
二叉树:
满二叉树,完全二叉树,二叉搜索树,红黑树
二叉搜索树的插入,查找,删除时间复杂度都为logn
但是,如果二叉树的极端条件下,二叉树只有左子树的话,时间复杂度会退化为n
HashMap的jdk1.7和1.8有什么区别?
8之前使用的是链表加数组的形式
8之后使用的是链表加数组加红黑树形式
转变为红黑树的条件是,当链表的长度大于8且数组的长度大于64则会从链表转化为红黑树
hashMap底层默认数组长度为16,加载因子默认时0.75 。
新建hashmap时没有初始化,当put元素时,在进行初始话长度为16的数组。
多线程相关基础知识:
并发和并行的区别:
并发:是同一时间应对多件事情的能力
并行:是同一时间动手做多件事情的能力

线程创建的方式:
1,继承Thread类
2,实现runable接口
3,实现Callable接口
4,线程池创建线程(项目中使用的方式)
runable和callabel 的区别:
前者没有返回值,且不能抛出异常,只能内部try catch解决
callable又call()方法有返回值是个泛型,但是要结合Future 和FutrueTask配合来获取异步执行的结果
线程包含有哪些状态?
新建,可运行,阻塞,等待,时间等待,终止。
线程状态之间是如何变化的?
创建线程对象是新建状态
调用start方法转变为可执行状态
线程获取到了cpu的执行权,执行结束是终止状态
在可执行状态的过程中,如果没有获取到cpu的执行权,可能会转为别的状态。
如果没有获取到锁synchronized或者lock进入阻塞状态,获得锁后切换为可执行状态
如果调用了wait方法进入等待状态,其他线程调用notify(唤醒方法)唤醒后可切换为可执行状态
如果线程调用sleep方法,进入计时等待状态,到时间后可切换为可执行状态
新建线程T1,T2,T3三个线程,如何保证他们按顺序执行?
调用join方法 在线程T2中调用T1.join则要等到T1执行完,才能执行T2


notify和notifyAll有什么区别?
notifyAll唤醒所有wait的线程
notify只随即唤醒一个wait线程
Java中wait和sleep方法有什么不同?
相同点:
wait wait(long)sleep(long)的效果是一样的都是让当前线程暂时放弃CPU的使用权,进入阻塞状态
不同点:
方法归属不同:
sleep是Thread的静态方法
而wait,wait(long)都是Object的成员方法,每个对象都有
醒来时机不同:
wait(long)和sleep(long)都是过一段时间就自动醒了
其中wait(long)和wait还可以被notify唤醒
wait没有被notify唤醒,将永远沉睡
他们都可以被打断唤醒
锁的特性不同(重点):
wait方法调用必须先获取wai对象的锁(要和synchronized锁配合使用),而sleep则无此限制
wait方法执行后会释放锁,允许其他线程获取该对象锁(我放弃了cpu,但你们可以使用)
而sleep在synchronized代码中执行,并不会释放对象锁(我放弃了cpu但是,你们也用不了)
如何停止一个正在运行的线程?
有三种方式可以停止线程
1,使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
2,使用stop方法强行终止(不推荐,该方法已作废)
3,使用interrupt方法中断线程
1,打断阻塞的线程(sleep,wait,join)的线程,线程会抛出interruptedException异常。
2,打断正常的线程,可以根据打断的状态来标记是否退出线程。
使用interrupt方法打断线程:
打断阻塞的线程:
Thread t1 = new Thread(()->{
System.out.println("t1正在运行");
System.out.println("t1是否被中断:" + Thread.currentThread().isInterrupted());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
System.out.println("t1是否被中断:" + t1.isInterrupted());
打断正常的线程:
Thread t1 = new Thread(() -> {
while (true) {
System.out.println("t1是否被中断:" + Thread.currentThread().isInterrupted());
boolean b = Thread.currentThread().isInterrupted();
if (b) {
System.out.println("打断状态" + b);
break;
}
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
线程中并发安全:
synchronized关键字的底层原理:
public class Aaa {
static Object abc = new Object();
int ticketNum = 10;
public static void main(String[] args) throws InterruptedException {
Aaa aaa = new Aaa();
for (int i = 0; i < 20; i++) {
new Thread(aaa::BuyTicket).start();
}
}
private void BuyTicket() {
synchronized (abc){
if (ticketNum <= 0) {
return;
}
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticketNum + "张票");
ticketNum--;
}
}
}
Synchronized(对象锁)采用互斥的方式让同一时刻至多只有一个线程持有(对象锁),其他线程在想获取这个(对象锁)时就会阻塞住。
Synchronized默认底层是Monitor监视器
是由jvm提供,c++语言实现

Owner:存储当前获取锁的线程的,只能有一个线程可以获取。
EntryList:关联没有抢到锁的线程,处于Blocked状态的线程。
WaitSet:关联调用了wait方法的线程,处于Waiting状态的线程。

谈谈JMM(Java memory model Java内存模型):
不要跟Java 的JVM Java内存结构混淆
jmm定义了共享内存中多线程程序读写操作的行为规范,通过这些规划来规范对内存的读写
操作从而保证指令的正确性

回答技巧:定义,jmm内存划分,线程之间交互媒介

CAS你知道吗?

更多推荐



所有评论(0)