声明:
- 背景:本人为24届双非硕校招生,已经完整经历了一次秋招,拿到了三个offer。
- 本专题旨在分享自己的一些Java开发岗面试经验(主要是校招),包括我自己总结的八股文、算法、项目介绍、HR面和面试技巧等等,如有建议,可以友好指出,感谢,我也会不断完善。
- 想了解我个人情况的,可以关注我的B站账号:东瓜Lee
B树:
是一种多叉路平衡查找树,相对于二叉树,B树每个节点可以有多个分支,即多叉。
以一颗最大度数(max-degree) 为5(5阶)的b-tree为例, 那这个B树每个节点最多存储4个key
B+树:
B+Tree是在BTree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+ Tree实现其索引结构。
非叶子结点上不存储数据,只存储指针,叶子结点上存储数据(叶子结点之间是用双向指针进行连接的)
B树和B+树的对比:
①:磁盘读写代价B+树更低,效率更高;
②:查询效率B+树更加稳定;
③: B+树便于扫库和区间查询;
面试题:
执行一条 SQL 查询语句,期间发生了什么?
通过连接器,建立连接、管理连接、校验用户身份;
查询缓存:要查询的内容如果在缓存中命中,则直接返回,否则继续往下执行。MySQL 8.0 已删除缓存模块(因此限制比较多,比如数据表更新,缓存就要更新,如果更新操作多,那就很影响效率);
解析 SQL,通过解析器对 SQL 查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型;
执行 SQL:执行 SQL 共有三个阶段:
预处理阶段:检查表或字段是否存在;将 select *
中的 *
符号扩展为表上的所有列。
优化阶段:基于查询成本的考虑, 选择查询成本最小的执行计划;
执行阶段:根据执行计划执行 SQL 查询语句,从存储引擎读取记录,返回给客户端;
在MySQL中,如何定位慢查询?
慢查询是指执行速度比较慢的dql操作,比如聚合查询、多表查询、表的数据量过大、深度的分页查询都会导致慢查询,表现形式就是页面加载很慢or对某个接口的压测响应时间过长
使用一些开源工具,比如Arthas(阿尔萨斯)
使用MySQL自带的慢查询日志
慢查询日志记录了所有执行时间超过指定参数的所有SQL语句的日志清空,如果要开启慢查询日志,需要在MySQL的配置文件(/etc/my.cnf) 中配置如下信息:
那这个SQL语句执行很慢,如何优化呢?
可以采用MySQL自带的分析工具EXPLAIN
了解过索引吗?
在数据之外,数据库系统还维护着满足特定查找算法的数据结构(B+树) ,这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
索引(index) 是帮助数据库高效查询数据 的一种数据结构。
作用:
索引的底层数据结构了解过嘛?
其他问题:
B+树的查询时间跟树的高度有关,是log(n),如果用hash存储,那么查询时间是O(1),既然hash比B+树更快,为什么mysql用B+树来存储索引呢?
如果只选择一个数据那确实是hash更快,但是数据库中经常会选中多条数据,这时候由于B+树索引是有序的,并且又有链表相连,它的查询效率比hash就快很多了
为什么不用红黑树?
树的查询时间跟树的层高有关,层数多一层,磁盘IO就多一次,数据量相同的情况下,红黑树是二叉树,树的层数高,B+树是一棵多叉树,每一层的数据多一些,层数小一些。
B树和B+树的对比
什么是聚簇索引什么是非聚簇索引(二级索引)?
什么是回表查询?
通过二级索引找到对应的主键值,到聚集索索引中查找整行数据,这个过程就是回表查询。
什么是覆盖索引?
覆盖索引是指查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到(如果不能全部找到,就还要回表查询,就是非覆盖索引)。
索引创建的原则有哪些?
聚集索引(主键索引、唯一索引)、非聚集索引(根据业务创建的索引—复合索引)
原则:
什么情况下索引会失效?
索引就是用来高效查找数据的,如果失效了,那当然就失去使用它的意义的,所以要尽可能的避免索引失效的情况出现,平时定位索引失效可以使用mysql自带的工具EXPLAIN,可以通过看key和key_len字段看是否命中了索引。
一般来说可能的情况有:
单个索引和联合索引的设计
单个索引:就是给某一个字段设置索引
联合索引:就是将多个字段共同作为索引,比如给某个字段A设置为了非聚集索引,如果要找另一个字段B,就要先找到主键id,再去回表查询找到那个字段B,如果同时把这两个字段AB设置为一个索引,那就不用回表了,效率更高。
要满足一个最左匹配原则(查找从索引的最左的列开始,并且不能跳过索引中的列)
谈一谈你对SQL优化的经验
表的设计可以优化
索引优化
要按照创建索引的几大原则
SQL语句优化
主从复制、读写分离
如果数据库的使用场景 读的操作比较多的时候,为了避免写的操作所造成的性能影响,可以采用读写分离的架构。
分库分表,可能的场景:
事务的特性是什么?可以详细说一下吗?
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体 一起向系统提交或撤销,也就是说这些操作 同时成功、同时失败。
事务的四大特性ACID:(可以用转账案例来说)
MySQL事务的底层实现原理
事务一组操作的集合,是一个不可分割的工作单位,事务会把所有的操作作为一个整体 一起向系统提交或撤销,也就是说这些操作 同时成功、同时失败。
事务满足四大特性ACID:原子性、一致性、隔离性、持久性
事务的实现原理实际上就是如何保证这四大特性:
并发的事务会带来哪些问题?
如何解决并发事务带来的问题?
使用事务的隔离级别
读未提交:脏读、不可重复读、幻读一个也解决不了
读已提交:只能解决脏读(不可重复读,幻读解决不了)
可重复读:只能解决脏读、不可重复读(幻读解决不了)
串行化:脏读、不可重复读、幻读都可以解决
事务的执行是串行的,效率很低,实际开发中肯定要使用并发事务
MySQL的默认隔离级别是?
undo log和redo log的区别
事务中的隔离性是如何保证的呢?
MVCC是什么?
MVCC多版本并发控制,可以维护一个数据的多个版本,使得并发事务对数据的读写操作没有冲突。
具体实现主要依赖于数据库记录中的隐式字段、undo log、readView
读写分离是什么?
面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。
MySQL主从复制的原理
MySQL数据库自带主从复制的功能,只需要配置即可。
原理:
MySQL 有哪些锁?
根据粒度大小分为三种:
lock tables 表名 ... read/write
,显式加锁根据性质,可以分为:
说一下innodb 存储引擎下(MySQL)的死锁问题
表级锁的死锁
用户A锁住了表1,然后又去访问表2,此时用户B锁住了表2,准备去访问表1,两个用户都是去请求共享资源,然后又不释放现有资源,就形成了一个循环等待的现象,这就是表级锁的死锁。
这种死锁比较常见,解决方法就是在进行数据库的多表操作的时候,尽量按照一定的顺序进行处理,尽量避免同时锁定两个资源。
行级锁的死锁
如果在事务中执行了一条没有使用索引的查询,引发了全表扫描,就会把行级锁上升为全表记录锁定(等价于表级锁),多个这样的事务执行后,就很容易产生死锁和阻塞。
解决方法就是 在SQL语句中不要使用太复杂的关联多表的查询
update是行锁还是表锁?
即可以加行锁也可以加表锁,要分情况考虑,取决于update后面的条件、事务隔离级别等因素
MySQL的存储引擎 InnoDB 与 MyISAM 有什么区别?
MySQL默认使用的是InnoDB作为存储引擎,具体的区别有几点:
面试题:
谈下你对 Redis 的了解?
Redis 就是远程数据服务,是一个基于内存且支持持久化的高性能 key-value 非关系型数据库,具备以下三个基本特征:
Redis 支持的数据类型有哪些?
五种常用的数据类型(key都是string,数据类型指定的是对应的value)
Redis各种数据类型 的底层数据结构:
ZSet的底层数据结构是什么?
压缩列表 或者 跳表
有序集合中保存的元素数量小于128个 且 有序集合中保存的所有元素的长度小于64字节 的时候使用 压缩列表(ziplist)
其他时候使用跳表(skiplist)
Redis优缺点?
优点:
缺点:
Redis 为什么这么快?
Redis为何选择单线程?
这里的单线程指的是 Redis 网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。
Redis6.0为何引入多线程?
Redis存在线程安全问题吗?
Redis 一般来说是单线程的,所以是线程安全的。虽然在 Redis 6.0 里面,引入了多线程,但是增加的多线程只是用来处理网络 IO 事件,对于指令的操作执行过程,仍然是由主线程来处理,所以不会存在多个线程 执行操作指令的情况,就还是线程安全的。
如果对指令的执行也采用多线程,那为了解决线程安全问题,需要对数据的操作加锁,增加了复杂度,还影响了性能,性价比不高。
Redis中事务和MySQL中事务的区别:
Redis的使用场景
Java怎么实现简单的缓存(不用redis的话)?
可以使用基于concurrentHashmap的缓存,然后配合上SpringCache框架(不需要单独导入依赖,SpringContext自带的),就可以用注解的方式简化缓存代码实现。
好处就是比较方便,不需要使用其他中间件
坏处就是java自带的map是基于内存的,程序一关掉就没有了,如果是Redis的话,那缓存数据还存在。
缓存穿透是什么?
缓存击穿是什么?
缓存雪崩是什么?
缓存雪崩是指在同一时段缓存中大量的key同时失效 or Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案一:(可能设置key的失效时间都设置的差不多)给不同的Key的TTL(过期时间)添加随机值
解决方案二:利用Redis集群 提高Redis服务的可用性
比如可以提前搭建好Redis的主从服务器进行数据同步,并配置哨兵机制,这样在Redis服务器因为宕机而无法提供服务时,可以由哨兵将Redis从服务器 设置为主服务器,继续提供服务。
Redis做缓存,怎么保证缓存和数据库的一致性(双写一致性)?
Redis做缓存的主要目的就是为了减少数据库的IO,提升性能,因为数据库的数据放在磁盘,磁盘的IO速度相比内存要慢很多。整体逻辑就是去读取数据的时候,先看在Redis缓存里面能否命中该条记录,如果有的话就直接返回,没有的话就去查询数据库,查询到了数据再放到缓存里面,以便下次查询的时候能直接从缓存命中。
双写一致性就是指:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库中的数据要保持一致。
如何保证一致性?(要分为两种业务场景)
1. 强一致性业务:对一致性的要求很高,缓存和数据库的数据必须时刻保持一致,我项目中主要的也是这种业务场景
2. 允许短暂不一致的业务:对一致性的要求没那么高,缓存和数据库的数据可以短暂的不一致,实际更主流的就是这个)
Redis持久化是怎么实现的?
Redis 的读写操作都是在内存中,但是当 Redis 重启后,内存中的数据就会丢失,那为了保证Redis 中的数据不会丢失,就要通过Redis的持久化机制,这个机制会把数据存储到磁盘,在 Redis 重启时就能够从磁盘中恢复原有的数据。Redis默认采用的是RDB持久化机制,还有一种AOF持久化机制,要去配置才能开启
两种持久化机制RDB和AOF的对比:
RDB:快照就是记录某个瞬间的内存数据,是实际的数据
AOF:记录的是命令操作的日志,不是实际的数据
Redis 4.0 使用了混合持久化
Redis的数据过期策略(假如redis的key过期之后,会立即删除吗?)
Redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称之为数据的数据过期策略。
Redis默认是 这两种删除策略配合使用
Redis的内存用完了会怎么办?
要根据数据淘汰策略来看:
Redis的数据淘汰策略(假如缓存太多,内存被占满了怎么办?)
当Redis中的内存不够用时,再向Redis中添加新的key,那么Redis就会按照某种规则将内存中的数据删除掉,删除规则被称之为内存的淘汰策略。
八种不同策略来选择要删除的key:
LRU和LFU的对比
区别:
因此需要根据实际的应用场景,可以选择合适的缓存淘汰策略。
什么是Redis集群?
作用和优势:
Redis常用的集群方案/模式有哪些?
【后续继续补充,敬请期待】