黑马面试篇

课程地址:新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题)_哔哩哔哩_bilibili

课程名称:新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题)

一、面试准备

技术栈细化;

二、Redis篇

Redis的面试占很大篇幅,很重要。

1. 布隆过滤器:

1. 自己写

2. Google 开发著名的 Guava 库(单机、内存)

3. Redisson实现(适用分布式)

2. 缓存击穿概念&解决方案

常见的解决方案有两种:

  • 互斥锁:给重建缓存逻辑加锁,避免多线程同时指向

  • 逻辑过期:热点key不要设置过期时间,在活动结束后手动删除。

缓存击穿实现代码:

3. 双写一致

总结:

  • 第一种方案:更新数据之后删除缓存,并为缓存设置过期时间。(延迟双删也有不一致风险,反而增加了业务复杂性:黑马微服务课程2-CSDN博客);
  • 第二种方案:采用Redisson提供的读写锁,强一致,性能低,只有要求强一致的场景采用;
  • 第三种方案:最终一致方案,使用MQ中间件。
  • 第四种方案:canal中间件,最终一致方案。

4. 持久化

1)RDB的执行原理?

bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。 fork采用的是copy-on-write技术: 当主进程执行读操作时,访问共享内存; 当主进程执行写操作时,则会拷贝一份数据,执行写操作。

2)AOF vs RDB

RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。

AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件

AOF默认是关闭的。AOF的命令记录的频率也可以通过redis.conf文件来配。

因为是记录命令,AOF文件会比RDB文件大的多,通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,进行文件压缩。Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:

5. 数据过期策略

假如redis的key过期之后,会立即删除吗?

  • 惰性删除:访问key的时候判断是否过期,如果过期,则删除;
  • 定期删除:定期检查一定量的key是否过期( SLOW模式+ FAST模式)
  • Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用

6. 数据淘汰策略

假如缓存过多,内存是有限的,内存被占满了怎么办?

  • Redis提供了8种不同的数据淘汰策略,默认是noeviction不删除任何数据,内存不足直接报错
  • LRU:最少最近使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高
  • LFU:最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高
  • 平时开发过程中用的比较多的就是allkeys-lru(结合自己的业务场景)

数据淘汰策略-使用建议

  • 优先使用 allkeys-lru 策略。充分利用 LRU 算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
  • 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用 allkeys-random,随机选择淘汰。
  • 如果业务中有置顶的需求,可以使用 volatile-lru 策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
  • 如果业务中有短时高频访问的数据,可以使用 allkeys-lfu 或 volatile-lfu 策略。

关于数据淘汰策略其他的面试问题

1)数据库有1000万数据 ,Redis只能缓存20w数据, 如何保证Redis中的数据都是热点数据 ?

  • 使用allkeys-lru(挑选最近最少使用的数据淘汰)淘汰策略,留下来的都是经常访问的热点数据。

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

  • 主要看数据淘汰策略是什么?如果是默认的配置( noeviction ),会直接报错。

7. 分布式锁

redis分布式锁,是如何实现的?

需要结合项目中的业务进行回答,通常情况下,分布式锁使用的场景: 集群情况下的定时任务、抢单、幂等性场景。

1)抢券:加synchronized可以吗?

如果是单体项目,并且只启动了一台服务,synchronized锁是没问题的。但如果集群部署,也就是将同一份代码部署在多台服务器上,synchronized就不行了。synchronized属于本地锁,JVM锁,只能解决同一JVM下线程的互斥,无法解决多个JVM下线程的互斥,所以在集群下,就不能使用本地锁了,只能使用外部锁解决(分布式锁)。

2)Redis分布式锁setnx

  • 死锁问题 -> 设置失效时间;
  • 锁的失效时长不好控制 -> 自动续期(看门狗线程);
  • 释放别人的锁 -> 设置锁key对应的UUID的value值;

3)Redisson分布式锁

redisson实现的分布式锁-执行流程

  • 每隔releaseTime时间做一次续期(releaseTime表示锁的过期时间,默认10秒);
  • 其他线程非阻塞方式获取锁(while循环,不断尝试获取锁,有一定阈值);
  • 加锁、设置过期时间都是基于lua脚本完成。

redisson实现的分布式锁:

  • 可重入;(可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用的hash结构,来存储线程信息和重入的次数);
  • 主从不一致(解决方案是RedLock红锁,但实现复杂、性能差,实际很少用,还是使用Redisson,因为Master宕机导致主从不一致概率低);
  • redis本身就是支持高可用的,做到强一致性,就非常影响性能。所以如果有强一致性要求高的业务,建议使用zookeeper实现的分布式锁。

8. Redis集群

1)Redis集群有哪些方案?★★★

  • 主从复制
  • 哨兵模式
  • 分片集群

2)主从集群

问题一:redis主从数据同步的流程是什么?

单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。 一般都是一主多从,主节点负责写数据,从节点负责读数据。

全量同步:

  1. 从节点请求主节点同步数据(replication id、 offset )
  2. 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(replication id和offset)
  3. 主节点执行bgsave,生成rdb文件后,发送给从节点去执行
  4. 在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
  5. 把生成之后的命令日志文件发送给从节点进行同步

增量同步:

  1. 从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值
  2. 主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步

3)哨兵模式

Redis集群哨兵模式也会产生脑裂的问题,导致数据丢失。

问题一:Redis集群脑裂,该怎么解决呢?

集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新master同步数据,就会导致数据丢失

解决:我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失。

redis中有两个配置参数:

  • min-replicas-to-write 1   表示最少的salve节点为1个
  • min-replicas-max-lag 5  表示数据复制和同步的延迟不能超过5秒
问题二:怎么保证redis的高并发高可用?

首先可以搭建主从集群,再加上使用redis中的哨兵模式,哨兵模式可以实现主从集群的自动故障恢复,里面就包含了对主从服务的监控、自动故障恢复、通知;如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;同时Sentinel也充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端,所以一般项目都会采用哨兵的模式来保证redis的高并发高可用。

问题三:你们使用redis是单点还是集群,哪种集群?

我们当时使用的是主从(1主1从)加哨兵。一般单节点不超过10G内存,如果Redis内存不足则可以给不同服务分配独立的Redis主从节点。尽量不做分片集群。因为集群维护起来比较麻烦,并且集群之间的心跳检测和数据通信会消耗大量的网络带宽,也没有办法使用lua脚本和事务。

4)分片集群

1. 分片集群结构

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:

  • 海量数据存储问题
  • 高并发写的问题

使用分片集群可以解决上述问题,分片集群特征:

  • 集群中有多个master,每个master保存不同数据
  • 每个master都可以有多个slave节点
  • master之间通过ping监测彼此健康状态
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点

分片集群:不再需要Sentinel哨兵了,已经具有了哨兵所有的功能。

2. redis的分片集群有什么作用
  • 集群中有多个master,每个master保存不同数据
  • 每个master都可以有多个slave节点
  • master之间通过ping监测彼此健康状态
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点
3. Redis分片集群中数据是怎么存储和读取的?
  • Redis 分片集群引入了哈希槽的概念,Redis 集群有 16384 个哈希槽
  • 将16384个插槽分配到不同的实例
  • 读写数据:根据key的有效部分计算哈希值,对16384取余(有效部分,如果key前面有大括号,大括号的内容就是有效部分,如果没有,则以key本身做为有效部分)余数做为插槽,寻找插槽所在的实例。

9. Redis其他面试题

1)Redis是单线程的,但是为什么还那么快?

2)能解释一下I/O多路复用模型?

Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度, I/O多路复用模型主要就是实现了高效的网络请求。

能解释一下I/O多路复用模型?

I/O多路复用

是指利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。

Redis网络模型

就是使用I/O多路复用结合事件的处理器来应对多个Socket请求

  • 连接应答处理器
  • 命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件;
  • 命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程。

三、MySQL篇

3.1 如何定位慢查询?

方案一:使用开源工具,如:调试工具Arthas, 运维工具Prometheus和Skywalking

方案二:开启MySQL自带慢日志功能,slow_query_log设置为1表示开启,long_query_time通常设置为2秒,具体如下图所示。开启之后,对于执行时间超过2秒的sql,就会被写入慢sql的日志文件。不过,通常用于调试阶段,生产上开启这个功能会损耗一定的性能。

3.2 SQL语句执行很慢, 如何分析呢?

聚合查询 -> 可以新增临时表

多表查询 -> 可以试着优化sql语句的结构

表数据量过大查询 -> 可以添加索引

上面这三种,都可以SQL执行计划(找到慢的原因);须掌握possible_key、key、key_len、Extra、type这几个字段。

可以采用MySQL自带的分析工具 EXPLAIN

  • 通过key和key_len检查是否命中了索引(索引本身存在是否有失效的情况)
  • 通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描或全盘扫描
  • 通过extra建议判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复 

深度分页查询(后面分析)

3.3 MYSQL支持的存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。

3.4 索引

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

MySQL的InnoDB引擎采用的B+树的数据结构来存储索引

  • 阶数更多,路径更短
  • 磁盘读写代价B+树更低,非叶子节点只存储指针,叶子阶段存储数据
  • B+树便于扫库和区间查询,叶子节点是一个双向链表

2)B树与B+树对比:

①:磁盘读写代价B+树更低;②:查询效率B+树更加稳定;③:B+树便于扫库和区间查询

3)什么是聚集索引,什么是非聚集索引?

  • 聚簇索引(聚集索引):数据与索引放到一块,B+树的叶子节点保存了整行数据,有且只有一个
  • 非聚簇索引(二级索引):数据与索引分开存储,B+树的叶子节点保存对应的主键,可以有多个

4)什么是回表查询?

先通过二级索引去找到对应的主键值,再通过主键值到聚集索引中找到整行的(或select后面的除主键以外的)数据。

5)什么叫覆盖索引? 

覆盖索引是指查询使用了索引,返回的列,必须在索引中全部能够找到

  • 使用id查询,直接走聚集索引查询,一次索引扫描,直接返回数据,性能高。
  • 如果返回的列中没有创建索引,有可能会触发回表查询,尽量避免使用select *

6)MYSQL超大分页怎么处理 ?

7)索引创建原则有哪些?

  • 针对于数据量较大,且查询比较频繁的表建立索引。
  • 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
  • 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
  • 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
  • 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
  • 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
  • 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

8)什么情况下索引会失效 ?

  • 违反最左前缀法则;
  • 范围查询右边的列,不能使用索引;(不包含自身)
  • 不要在索引列上进行运算操作, 索引将失效;
  • 字符串不加单引号,造成索引失效;(类型转换)
  • 以%开头的Like模糊查询,索引失效。

3.5 SQL优化经验

  • 表的设计优化(参考阿里开发手册《嵩山版》)

①设置合适的数值(tinyint   int   bigint),要根据实际情况选择

②设置合适的字符串类型(char和varchar)char定长效率高,varchar可变长度,效率稍低

  • SQL语句优化

①SELECT语句务必指明字段名称(避免直接使用select * )

②SQL语句要避免造成索引失效的写法

③尽量用union all代替union,union会多一次过滤,效率低

④避免在where子句中对字段进行表达式操作

⑤Join优化 能用innerjoin 就不用left join right join,如必须使用 一定要以小表为驱动,

内连接会对两个表进行优化,优先把小表放到外边,把大表放到里边。left join 或 right join,不会重新调整顺序

  • 主从复制、读写分离

①如果数据库的使用场景读的操作比较多的时候,为了避免写的操作所造成的性能影响 可以采用读写分离的架构。 读写分离解决的是,数据库的写入,影响了查询的效率。

3.6 事务相关★★★

  • 事务特性(ACID);
  • 隔离级别;(MySQL有四种隔离级别)
  • MVCC

并发事务带来哪些问题?怎么解决这些问题呢?MySQL的默认隔离级别是?

  • 脏读   -> 读已提交来解决;
  • 不可重复读   -> 可重复读来解决;
  • 幻读   -> 串行化来解决。
  • MySQL默认的隔离级别是可重复读。

1)undo log和redo log的区别

总结:undo log和redo log的区别

  • redo log: 记录的是数据页的物理变化,服务宕机可用来同步数据
  • undo log :记录的是逻辑日志,当事务回滚时,通过逆操作恢复原来的数据
  • redo log保证了事务的持久性,undo log保证了事务的原子性和一致性

2)事务中的隔离性是如何保证的呢?

  • 锁:排他锁(如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁。平时使用的update、delete、insert自动会获取排他锁。)
  • mvcc : 多版本并发控制

3)解释一下MVCC(讲的非常好)★★★★★

MVCC,全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。

MVCC的具体实现,主要依赖于数据库记录中的隐式字段、undo log日志、readView

MVCC总结:

3.7 MySQL主从同步原理 

3.8 项目用过分库分表吗?

1)什么时候需要分库分表

2)四种拆分策略

垂直拆分(垂直分库、垂直分表)

水平拆分(水平分库、水平分表)

水平分库:将一个库的数据拆分到多个库中;

水平分表:将一个表的数据拆分到多个库(或者同一个库)的表中。

3)分库分表中间件

分库之后会面临一些问题:

  • 分布式事务一致性问题
  • 跨节点关联查询
  • 跨节点分页、排序函数
  • 主键避重

于是,引入了分库分表中间件,可以很好的解决这些问题。常用的有:

  • sharding-sphere
  • mycat

四、框架篇

4.1 Spring部分

1. Spring框架中的单例bean是线程安全的吗?

不是线程安全的

Spring框架中有一个@Scope注解,默认的值就是singleton,单例的。

因为一般在spring的bean的中都是注入无状态的对象,没有线程安全问题,如果在bean中定义了可修改的成员变量,是要考虑线程安全问题的,可以使用多例或者加锁来解决。

2. 什么是AOP,你们项目中有没有使用到AOP?

相关推荐

  1. 黑马点评面试问题

    2024-04-28 23:46:02       11 阅读
  2. 黑马程序员】Python面向对象

    2024-04-28 23:46:02       22 阅读
  3. TypeScript学习(面试

    2024-04-28 23:46:02       27 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-28 23:46:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-28 23:46:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-28 23:46:02       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-28 23:46:02       20 阅读

热门阅读

  1. 先出发再思考怎么解决问题

    2024-04-28 23:46:02       38 阅读
  2. IDEA那些牛X的插件

    2024-04-28 23:46:02       16 阅读
  3. 安全运营之通行字管理

    2024-04-28 23:46:02       24 阅读
  4. vue项目使用tsx

    2024-04-28 23:46:02       15 阅读
  5. C++ 模板和泛型编程详解

    2024-04-28 23:46:02       10 阅读
  6. Android ANR 日志导出及分析

    2024-04-28 23:46:02       14 阅读