【本文章旨在帮助你快速了解Java面试中会遇到的高频面试题,大部分内容来自黑马程序员的Java面试课程->视频链接,以及两个很好用的面试题网站,小林coding->网站链接 和JavaGuide->网站链接,我将这三者进行了总结和归纳,希望能帮助到你,也希望你在准备面试题的时候不只是“背会了”,而是“通过准备面试题拓展自己的知识面,掌握技术背后的细节和逻辑”。准备面试是一个煎熬,焦虑,迷茫的过程,多看天,多想想,多走走。】
最高优先级(必问,出现频率极高)
索引的底层数据结构(B+树)及优势
聚簇索引 vs 非聚簇索引,回表查询
SQL优化:如何定位和分析慢查询(EXPLAIN命令)
事务的ACID特性及实现原理
MySQL的锁机制(行锁、表锁、间隙锁)
高优先级(经常被问到)
索引创建原则及索引失效场景
MVCC多版本并发控制原理
MySQL隔离级别及解决的问题(脏读、不可重复读、幻读)
覆盖索引及其优化作用
超大分页如何处理(分页优化)
中优先级(偶尔问到)
MySQL三大日志(binlog、undo log、redo log)作用
并发事务的问题及解决方案
SQL优化经验(字段类型、UNION ALL等)
主从同步原理
B树和B+树的区别
低优先级(选择性准备)
什么是乐观锁和悲观锁
为什么可重复读不能完全解决幻读
快照读 vs 当前读的区别
存储引擎:MyISAM和InnoDB(Mysql默认引擎)有什么区别?
1.MyISAM只支持表级锁,而InnoDB支持行级锁,锁的粒度更细
2.MyISAM不支持事务,InnoDB提供事务支持,并且定义了四个事务隔离级别
3.InnoDB支持外键约束,能够保证数据的一致性和完整性
4.MyISAM不支持数据库异常崩溃后的数据恢复,InnoDB通过redo log日志支持
1.MySQL中,如何定位慢查询?
MySQL本身提供了慢查询日志功能。可以在MySQL的系统配置文件中开启慢查询日志,并设置SQL执行时间超过多少就记录到日志文件,比如我们之前项目设置的是2秒,超过这个时间的SQL就会记录在日志文件中,我们就可以在那里找到执行慢的SQL。
2.那这个SQL语句执行很慢,如何分析呢?
如果一条SQL执行很慢,我们通常会使用MySQL的EXPLAIN命令来分析这条SQL的执行情况。通过key和 key_len可以检查是否命中了索引,如果已经添加了索引,也可以判断索引是否有效。通过type字段可以判断当前SQL语句是如何查找数据的,比如是否存在全索引扫描 index(复合索引失效)或全表扫描 all(添加索引)。通过extra建议可以判断是否出现回表情况,如果出现,可以尝试添加索引或修改返回字段来优化。
3.了解索引吗?
索引是一种帮助MySQL高效获取数据的数据结构,主要用来提高数据检索效率。
4.索引的底层数据结构了解吗?
MySQL的默认存储引擎InnoDB,索引使用的数据结构是B+树。B+树的非叶子节点存放键值(数据的唯一标识如ID)和指向子节点的指针,用于导航数据的位置,叶子节点存放数据,并且,叶子节点之间形成了一个双向链表,可以进行范围查询。
5.B树和B+树的区别是什么呢?
B树的叶子节点和非叶子节点都存放数据,而B+树的所有数据只出现在叶子节点,这使得B+树在查询时效率更稳定。
B+树在进行范围查询时效率更高,因为所有数据都在叶子节点,并且叶子节点之间形成了双向链表。
6.什么是聚簇索引,什么是非聚簇索引(二级索引)?
聚簇索引通常只有一个,一般由主键构成。数据和索引一起存储,叶子节点保存了主键对应的整行数据,存储的是数据本身。非聚簇索引一般都是我们自定义的索引,可以有多个,叶子节点存储的是聚簇索引的主键值,以及我们当前索引字段的数据,并不是完整数据。
7.什么叫回表查询?
回表查询指的是此时我们先拿到二级索引存放的主键值,再通过聚簇索引查询到主键值对应的数据行,这个过程叫做回表查询。
8.什么叫覆盖索引?
覆盖索引是指查询时使用了索引,并且返回的列全部能在索引中找到,避免了回表查询,提高了性能。
9.MySQL超大分页怎么处理?
超大分页通常发生在数据量大的情况下,使用LIMIT分页查询且需要排序时效率较低。可以通过覆盖索引和子查询来解决。首先查询数据的ID字段进行分页,然后根据ID列表用子查询来过滤只查询这些ID的数据,因为查询ID时使用的是覆盖索引,所以效率可以提升。
原:select * from user limit 9000000 , 10 ;
优化:覆盖查询+子查询
先查询数据的数据的 ID,再根据ID找到对应的数据,id是聚簇索引,查询很快
select * from user u ,
(select id from user order by id limit 9000000,10) a
where u.id=a.id;
10.索引创建的原则有哪些?
1.表中的数据量超过10万以上时必须创建索引。
2.选择查询频繁的字段作为索引,如查询条件where、排序字段order by或分组字段group by。
3.尽量使用覆盖索引,覆盖SQL的返回值。
4.控制索引的数量,单张表控制在5-6个,索引会让查询效率变高,插入修改操作效率变低
5.对于区分度较大的字段,优先考虑创建索引
6.如果字段区分度不高,可以将其放在联合索引的后面。(这样做有助于提高联合索引的效率,因为在使用索引进行查询时,数据库一般会优先根据联合索引前面的字段进行筛选,若前面字段区分度高,就能快速缩小查询范围,若把区分度不高的字段放在前面,可能无法有效缩小范围,影响查询性能。例如,在一个员工信息表中,“性别” 字段区分度相对不高(只有男、女两种值),而 “员工编号” 区分度高(每个员工编号唯一),在创建联合索引时,“员工编号” 应放在前面,“性别” 放在后面。)
11.什么情况下索引会失效?
在使用复合索引时没有遵循最左匹配原则(指的是查询从索引的最左前列开始,并且不跳过索引中的列)
使用了复合索引但在中间使用了范围查询,导致右边的条件索引失效。
使用了模糊查询且
%号在前面。(Like %abc)或者说使用了前模糊匹配。在索引字段上进行了运算操作。
12.SQL优化经验有哪些?
建表时选择合适的字段类型。参考(阿里开发手册《嵩山版》)
使用索引,遵循创建索引的原则,使用索引时避免索引失效
编写高效的SQL语句,比如避免使用
SELECT *,尽量使用UNION ALL代替UNION,UNION会多一次过滤操作,效率低。
union是将多个sql语句的查询结果合并采用主从复制和读写分离提高性能。
13.什么是事务,事务的特性?
事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务的特性是ACID,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。例如,A向B转账500元,这个操作要么都成功,要么都失败,体现了原子性。转账过程中数据要保持一致,A扣除了500元,B必须增加500元。隔离性体现在A向B转账时,不受其他事务干扰。持久性体现在事务提交后,数据要被持久化存储。
ACID靠什么保证?
ACID在InnoDB引擎中主要由以下机制保证:
原子性由undo log回滚日志保证,它记录了事务回滚所需的信息。
隔离性由锁机制和MVCC共同保证。锁处理写写冲突,MVCC通过undo log版本链和ReadView处理读写冲突,实现了无锁的快照读。
持久性由redo log重做日志保证,采用WAL预写日志机制,确保事务提交后数据不会丢失。
一致性是最终目的,它其实是应用层和数据库层共同的责任。数据库通过原子性、隔离性、持久性为一致性提供了基础,而应用程序必须保证正确的业务逻辑,才能最终达成数据的一致性状态。比如说我们现在要进行转账操作,就需要保证在应用层正确编写扣款和加款的 SQL逻辑。
14.MySQL三大日志(binlog,undolog回滚日志,redolog 重做日志)
1、undo log(提供回滚 和 MVCC)回滚日志,用于记录数据被修改前的信息 。当事务回滚时,undo log会通过逆操作恢复原来的数据,比如我们删除一条数据的时候,就会在undo log日志文件中新增一条delete语句,如果发生回滚就执行相反的 Insert 语句来恢复数据;保证事务的原子性.
2、redo log:
redo log(重做日志)可以让mysql的数据进行故障恢复。MySQL 中数据是以页为单位,每查询一条记录,会从硬盘把一页的数据加载出来,将数据页放入到 缓冲池 中。后续的查询都是先从 缓冲池 中找,在更新数据的时候也是这样的,发现 缓冲池 里存在要更新的数据,就直接在 缓冲池 里更新。然后会把“在某个数据页上做了什么修改”记录到 redo log 文件里。 (WAL)
当MySQL 重启时,InnoDB 存储引擎会使用 redo log 恢复数据,保证事务的持久性。
15.事务的隔离性如何保证?/并发事务的控制方式?(MVCC)
事务的隔离性是由锁和mvcc实现的。
锁是悲观方案,当一个事务执行写操作的时候,会加一个排他锁,解决写-写冲突。mvcc是多版本并发控制,维护一个数据的多个版本,不同的事务会读取自己特定版本的数据,是乐观方案,能够解决读-写冲突。它是MYSQL在【读已提交】和【可重复读】这两个隔离级别下实现【读操作无锁化】的核心机制。
(MVCC只在 读已提交 和 可重复读 两个隔离级别下工作。其他两个隔离级别和MVCC不兼容,因为 读未提交 总是读取最新的数据行,而不是符合当前事务版本的数据行。而 串行化则会对所有读取的行加锁。)
它的底层实现主要是分为了三个部分,隐藏字段,undo log日志,和 readView读视图。
隐藏字段是指:在mysql中给每个表都设置了隐藏字段,有一个是trx_id(事务id),记录每一次对表进行写操作的事务id;另一个字段是roll_pointer(回滚指针),指向该行数据的上一个版本在 undo log 中的存储位置。
undo log主要的作用是存储老版本数据,会通过roll_pointer指针将数据行的历史版本形成一个链表。
readView定义事务看哪个版本的数据,会根据隔离级别选择对应的版本。如果是rc (读已提交)隔离级别,每次读取时都会生成一个新的ReadView,这样就能保证当前事务总能读到其他事务已提交的最新数据,如果是rr (可重复读)隔离级别在事务开始时只会生成一次ReadView,后续的读取都复用这个ReadView。
16.MySQL主从同步原理?
MySQL主从复制的核心是二进制日志(Binlog)。步骤如下:
主库在收到客户端的提交事务请求后,会先将事务的操作记录写入 binlog 日志,再提交事务,并更新本地数据。
从库会读取主库的Binlog并将事务的操作记录写入中继日志(Relay Log)。
从库重做中继日志中的事件,实现主从数据一致。
17.并发事务的问题?
(MYSQL服务端是允许多个客户端连接的,这意味着MYSQL会出现同时处理多个事务的情况。)
并发事务可能导致脏读、不可重复读和幻读。
1.脏读是指一个事务读到了另一个事务未提交的“脏数据”。(事务读到的数据有可能会回滚)
2.不可重复读是指在一个事务多次读取同一数据,由于其他事务的修改导致数据不一致。
3.幻读是指一个事务执行了两次相同的范围查询(例如where条件),第二次查询结果中满足条件的行数发生了变化,比如多了一些行(插入数据)或少了一些行(删除数据)。
18.怎么解决并发事务的问题?MySQL默认隔离级别?
解决这些问题的方法是进行事务的隔离。MySQL支持四种隔离级别,隔离级别越高,性能效率越低:
读未提交(READ UNCOMMITTED):解决不了所有问题。
读已提交(READ COMMITTED):能解决脏读,但不能解决不可重复读和幻读。 MVCC
可重复读(REPEATABLE READ):能解决脏读和不可重复读,并通过间隙锁解决大部分幻读问题,这也是MySQL的默认隔离级别 MVCC
串行化(SERIALIZABLE):可以解决所有问题。
可重复读解决幻读的方案?
针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务看到的数据一直是查询时看到的数据。
针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,会锁住一段范围的数据,其他事务是不能操作这段数据的,因此就很好地避免幻读问题。
注:除了普通的select语句是快照读,其他比如 select… for update 或者 …for share 都是当前读。(快照读读的是事务开始时的数据版本,不管其他事务是否修改;当前读指的是读取已提交的最新数据。) for update 表示我当前这个事务读取完数据后,随后就要操作数据,其他事务不能读或写;for share指的是,我读取完数据后,其他事务可以读,不能写。
为什么可重复读不能完全解决幻读?
情况:
1.连续两次当前读,两次之间可能有其他事务修改。
2.先使用快照读,后使用当前读,两次之间可能有其他事务修改。
19.Mysql中的锁:
Mysql锁有哪些,如何理解
按锁粒度分类:
- 行锁:锁某行数据,锁粒度最小,并发度高,开销大
使用场景:高并发、频繁修改某一行数据的场景
只有InnoDB存储引擎支持
- 表锁:锁整张表,锁粒度最大,并发度低,开销小
使用场景:数据迁移备份,全表更新
- 间隙锁:锁的是一个区间
使用场景:防止其他事务在区间内插入新数据(解决幻读)
只在可重复读(Repeatable Read)及以上隔离级别生效
示例:锁定id BETWEEN 10 AND 20,则其他事务无法在此区间插入新记录。
还可以分为:
- 共享锁(S锁):也就是读锁,一个事务给某个数据加了读锁,其他事务也可以读,但是不能写
- 排它锁(X锁):也就是写锁,一个事务给某数据加了写锁,其他事务不能读,也不能写
还可以分为:
- 乐观锁:并不会真正的去锁举记录,而是通过一个版本号来实现的(每修改一次数据,版本号加一)
- 悲观锁:上面所的行锁、表锁等都是悲观锁
20.left join、inner join、right join的区别?
三种JOIN的区别主要看以哪个表为基准:
INNER JOIN:只返回两个表都匹配的行
LEFT JOIN:返回左表所有行,右表匹配不上显示NULL
RIGHT JOIN:返回右表所有行,左表匹配不上显示NULL
比如说:
现在有两张表:公司员工表,项目参与名单
inner join:查询既是公司员工,又参与了项目的人
left join:查询公司员工中,参与项目的人
right join:查询参加项目的人中,有几个是公司员工
21.Mysql性能怎么优化?
性能优化的第一步是找到瓶颈,先通过慢查询日志,找到执行比较慢的sql,
然后通过 explain 进行分析,如果是索引的问题,我们要遵循索引的创建原则,避免索引失效
然后我们可以优化表的结构,选择合适的字段类型,可以参考《阿里开发手册 ( 嵩山版 ) 》
最后我们对 sql 语句进行检查,有没有使用 select *等等。