目录

HashMap如何解决hash冲突?

ArrayList扩容机制:

简历书写:

redis面试题:

redis缓存雪崩问题解决方法:

redis与mysql数据库数据同步问题

redis持久化机制:

RDB:

AOF:

 redis数据删除策略:

惰性删除:

优缺点:

定期删除:

​编辑 优缺点:

第三种两种配合使用:

redis数据淘汰策略:

 redis的分布式锁:

redisson实现的分布式锁-可重入: 

mysql面试题:

优化

MySQL优化一:

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

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

什么是回表查询?

知道什么叫覆盖索引吗?

索引的创建原则有哪些?

联合索引失效情况:

数库表、语句设计的优化:

什么是事务?(什么是ACID)

事务的undo log和redo log的区别:

解释一下MVCC?

框架篇

spring 中的单例bean是线程安全的吗?

Spring中事务失效的场景有哪些?

场景一:异常捕获处理

场景二:抛出检查异常

场景三:非public方法导致的事务失效

bean的循环依赖问题:

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

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

MyBatis的延迟加载:

spring cloud微服务架构的5大组件:

负载均衡的策略有哪些?

如何自定义负载均衡策略?

RabbitMQ问题:

rabbitmq如何保证发送消息不丢失?

RabbitMQ消息的重复消费问题如何解决的?

rabbitmq如果有100万消息堆积在MQ了,如何解决(消息堆积如何解决?)

集合:

arraylist与linkedlist的区别:

二叉树:

HashMap的jdk1.7和1.8有什么区别?


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你知道吗?

 

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐