JAVA八股文

2025年 Java 面试八股文(20w字)_java面试八股文-优快云博客

四、MySQL

1.Select 语句完整的执行顺序

SQL Select 语句完整的执行顺序:

(1)from 子句组装来自不同数据源的数据;

(2)where 子句基于指定的条件对记录行进行筛选;

(3)group by 子句将数据划分为多个分组;

(4)使用聚集函数进行计算;

(5)使用 having 子句筛选分组;

(6)计算所有的表达式;

(7)select 的字段;

(8)使用order by 对结果集进行排序。

2.MySQL事务

事务的基本要素(ACID)

1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位
2.一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3.隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4.持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

 MySQL事务隔离级别:

事务的并发问题

脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致
幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

如何解决脏读、幻读、不可重复读

脏读: 隔离级别为 读提交、可重复读、串行化可以解决脏读
不可重复读:隔离级别为可重复读、串行化可以解决不可重复读
幻读:隔离级别为串行化可以解决幻读、通过MVCC + 区间锁可以解决幻读


小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

3.MyISAM和InnoDB的区别

索引结构

  • InnoDB:主键索引为聚集索引,数据直接存储在索引的叶子节点,主键查询极快。

  • MyISAM:所有索引均为非聚集索引,索引与数据分离,需通过地址二次查找。

适用场景

场景推荐引擎理由
高并发写入、事务需求InnoDB行级锁、事务支持
复杂查询(OLTP)InnoDB缓冲池优化、索引效率高
只读或读多写少MyISAM简单结构、全表扫描快
日志记录、数据仓库MyISAM无事务需求,写入后很少修改

总结

  • InnoDB 是现代应用的首选,尤其在需要事务、高并发或数据可靠性时。

  • MyISAM 适用于简单查询、只读场景,但已逐渐被 InnoDB 取代。

  • MySQL 5.5+ 默认使用 InnoDB,建议优先选择。

4.悲观锁和乐观锁的怎么实现

悲观锁和乐观锁是两种常见的并发控制机制,用于解决多线程或分布式环境下数据竞争问题。它们的核心区别在于对并发冲突的预期以及实现方式

悲观锁:

核心思想:默认并发操作会频繁发生冲突,因此在访问数据时直接加锁,确保独占操作。

数据库层面

1.行级锁(InnoDB)
使用 SELECT ... FOR UPDATE 显式锁定目标行,其他事务需等待锁释放

特点

  • 锁在事务提交或回滚后释放。

  • 需确保事务尽量短,避免长事务导致性能问题。

2.表级锁(MyISAM)
MyISAM 引擎默认在写操作时自动加表级锁

适用场景

  • 写操作频繁,冲突概率高(如账户扣款)。

  • 需要强一致性保证。

乐观锁:

核心思想:默认并发冲突较少,只在提交数据时检查是否发生冲突,若冲突则重试或放弃。

数据库层面
  1. 版本号机制
    为表添加一个版本号字段(如 version),更新时校验版本号

  2. 时间戳机制
    类似版本号,但使用时间戳字段 update_time 作为校验依据

对比总结

特性悲观锁乐观锁
冲突预期默认高概率冲突,提前加锁默认低概率冲突,提交时校验
实现复杂度简单(直接加锁)复杂(需处理冲突重试逻辑)
性能高并发下可能阻塞,吞吐量低无锁竞争,吞吐量高
适用场景强一致性、短事务、写多读少最终一致性、长事务、读多写少
典型应用银行转账、订单支付商品库存、评论点赞

 

选择建议

  1. 若系统并发冲突频繁,或要求强一致性(如金融系统),优先选择悲观锁

  2. 若系统读多写少,或能容忍短暂不一致(如电商库存),优先选择乐观锁

  3. 分布式场景中,乐观锁更易扩展(如结合 Redis 或 ZooKeeper)。

5.聚簇索引与非聚簇索引区别

都是B+树的数据结构

聚簇索引:将数据存储与索引放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的

非聚簇索引叶子节点不存储数据、存储的是数据行地址,也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本书的目录,比如我们要找第三章第一节,那我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。

优势:

1、查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高

2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的

3、聚簇索引适合用在排序的场合,非聚簇索引不适合

劣势;

1、维护索引很昂贵,特别是插入新行或者主键被更新导至要分页(pagesplit)的时候。建议在大量插入新行后,选在负载较低的时间段,通过OPTIMIZETABLE优化表,因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片

2、表因为使用uuId(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫面更慢,所以建议使用int的auto_increment作为主键

3、如果主键比较大的话,那辅助索引将会变的更大,因为辅助索引的叶子存储的是主键值,过长的主键值,会导致非叶子节点占用占用更多的物理空间

6.什么情况下mysql会索引失效

MySQL 索引失效会导致查询性能急剧下降,甚至触发全表扫描。以下是常见的索引失效场景及其原因和优化方法:

失效条件:

  • where 后面使用函数
  • 使用or条件
  • 模糊查询 %放在前边
  • 类型转换
  • 组合索引 (最佳左前缀匹配原则)

1. 对索引列使用函数或计算

失效原因:索引存储的是列的原始值,对列进行函数操作或计算后,MySQL 无法直接匹配索引结构。
示例

-- 索引失效(对 date 字段使用函数)
SELECT * FROM orders WHERE YEAR(create_time) = 2023;

-- 索引失效(对 price 进行计算)
SELECT * FROM products WHERE price * 0.9 > 100;

优化方法

  • 将函数或计算移至表达式另一侧:

    -- 优化后(直接使用时间范围)
    SELECT * FROM orders 
    WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';
    
    -- 优化后(避免对列计算)
    SELECT * FROM products WHERE price > 100 / 0.9;

2. 隐式类型转换

失效原因:索引列与查询值的类型不一致,触发隐式类型转换(如字符串转数字)。
示例

-- user_id 是 VARCHAR 类型,但查询使用数字(导致类型转换)
SELECT * FROM users WHERE user_id = 10086;

优化方法

  • 保持类型一致:

    SELECT * FROM users WHERE user_id = '10086';

3. 使用 LIKE 前导通配符

失效原因:以 % 或 _ 开头的模糊查询无法利用索引的有序性。
示例

-- 索引失效(前导通配符)
SELECT * FROM articles WHERE title LIKE '%数据库%';

优化方法

  • 避免前导通配符,或使用全文索引(如 MATCH ... AGAINST):

    -- 仅后缀模糊匹配(可能走索引)
    SELECT * FROM articles WHERE title LIKE '数据库%';

4. 组合索引未遵循最左前缀原则

失效原因:组合索引按最左列优先排序,若查询未包含最左列,索引失效。
示例

-- 组合索引为 (a, b, c)
SELECT * FROM table WHERE b = 2 AND c = 3; -- 未使用 a 列,索引失效

优化方法

  • 调整查询条件顺序,确保包含最左列:

    SELECT * FROM table WHERE a = 1 AND b = 2 AND c = 3;

5. 使用 OR 连接非索引列

失效原因:若 OR 的某一侧字段无索引,优化器可能放弃使用索引。
示例

-- name 有索引,age 无索引
SELECT * FROM users WHERE name = '张三' OR age = 25; -- 索引失效

优化方法

  • 为 OR 两侧字段都添加索引,或改用 UNION

    SELECT * FROM users WHERE name = '张三' 
    UNION 
    SELECT * FROM users WHERE age = 25;

6. 索引列参与 !=NOTIS NULL 判断

失效原因:非等值查询(如 !=NOT IN)可能导致优化器选择全表扫描。
示例

-- 索引失效
SELECT * FROM orders WHERE status != 'paid';
SELECT * FROM users WHERE email IS NULL;

优化方法

  • 避免高频使用非等值查询,或结合覆盖索引优化:

    -- 覆盖索引(仅查索引列)
    SELECT id FROM users WHERE email IS NULL;

7. 数据量过小或索引选择性低

失效原因

  • 表数据量过小(如几百行),优化器认为全表扫描更快。

  • 索引选择性低(如性别字段只有 男/女),索引效率不如全表扫描。
    示例

-- 性别字段索引选择性低
SELECT * FROM users WHERE gender = '男';

优化方法

  • 选择性低的字段避免单独建索引,可结合其他字段建组合索引。

8. 全表扫描成本低于索引查询

失效原因:当查询需要访问大部分数据时,优化器可能直接选择全表扫描。
示例

-- 查询表中 90% 的数据
SELECT * FROM logs WHERE create_time > '2000-01-01';

优化方法

  • 限制查询范围或强制使用索引(需谨慎):

    SELECT * FROM logs USE INDEX(idx_time) WHERE create_time > '2023-01-01';

9. 索引列使用 IN 或 BETWEEN 的范围过大

失效原因IN 列表过长或 BETWEEN 范围过广,优化器可能放弃索引。
示例

-- IN 列表包含数千个值
SELECT * FROM products WHERE id IN (1, 2, 3, ..., 10000);

优化方法

  • 分批次查询或改用临时表关联。

7.B+tree 与 B-tree区别

1. 数据结构设计

特性B树(B-Tree)B+树(B+ Tree)
节点存储内容每个节点存储键(Key)和对应的数据(Value)非叶子节点仅存储键,叶子节点存储键和数据
叶子节点链接叶子节点独立,无链表连接叶子节点通过指针形成有序双向链表
数据分布数据可能分布在所有节点数据仅存储在叶子节点

 

2. 查询性能

场景B树B+树
单值查询可能在非叶子节点命中数据(更快)必须遍历到叶子节点才能获取数据
范围查询需中序遍历,效率较低(无链表支持)通过叶子节点链表直接遍历,效率极高
查询稳定性查询时间波动较大(数据分布不均)查询时间稳定(所有查询必须到叶子节点)

3. 存储与I/O效率

特性B树B+树
节点容量节点存储键+数据,单节点键数量较少非叶子节点仅存键,单节点可容纳更多键
树高度相对较高(相同数据量下)更矮胖(键密度高,减少磁盘I/O次数)
空间利用率非叶子节点存储数据,空间利用率较低非叶子节点仅存键,空间利用率更高

4. 插入与删除

操作B树B+树
分裂与合并频繁(数据分布在所有节点)主要在叶子节点操作,非叶子节点仅调整键
复杂度较高(需处理数据和键的重新分配)较低(数据仅存于叶子节点)

5. 适用场景

场景B树B+树
数据库索引较少使用(如MongoDB的某些存储引擎)主流选择(如MySQL InnoDB、Oracle)
文件系统适用(如早期文件系统)更高效(如NTFS、ReiserFS)
内存数据库可能更优(减少指针跳转)适用但需权衡链表维护成本

总结

维度B树B+树
设计目标快速随机访问高效范围查询和顺序扫描
优势单点查询可能更快高I/O效率、适合大规模数据存储
劣势范围查询性能差、树高度较高单点查询需访问叶子节点
典型应用内存受限或随机访问密集场景数据库、文件系统等磁盘存储场景

为什么数据库(如MySQL)选择B+树?

  1. 范围查询优化:叶子节点的链表结构天然支持高效范围查询(如WHERE id > 100)。

  2. 更低的树高度:减少磁盘I/O次数(适合海量数据)。

  3. 稳定的查询性能:所有查询最终落到叶子节点,时间波动小。

  4. 更适合磁盘预读:连续存储的叶子节点匹配磁盘块读取特性。

8.以MySQL为例Linux下如何排查问题 

类似提问方式:如果线上环境出现问题比如网站卡顿重则瘫痪 如何是好?

一、快速应急(止血)

临时重启服务(仅限极端情况)

# 强制终止 MySQL 进程(慎用!可能丢失数据)
kill -9 $(pidof mysqld)
# 安全重启 MySQL
systemctl restart mysql

限制流量入口

# 通过 iptables 临时屏蔽外部请求(示例屏蔽 80 端口)
iptables -A INPUT -p tcp --dport 80 -j DROP

紧急杀会话

-- 登录 MySQL,终止长时间运行的查询
SHOW PROCESSLIST;
KILL <query_id>;

二、系统资源排查

CPU 负载
# 查看 CPU 使用率及负载(重点看 %us 用户态、%sy 内核态)
top -c
htop

# 按进程查看 CPU 占用(-p 指定进程)
pidstat -p <mysql_pid> 1 5
内存使用
# 检查内存和 Swap 使用情况
free -h
vmstat 1 5

# 查看 MySQL 内存分配
mysql> SHOW VARIABLES LIKE '%buffer%';
磁盘 I/O
# 查看磁盘 I/O 压力(关注 %util、await)
iostat -x 1 5

# 定位高 I/O 进程(按进程统计)
iotop
 网络流量
# 检查网络连接和带宽(排查 DDoS 或异常连接)
iftop -P
netstat -antp | grep ESTABLISHED

三、MySQL 内部诊断

慢查询分析

-- 查看是否开启慢查询日志
SHOW VARIABLES LIKE 'slow_query_log';

-- 临时开启慢查询日志(立即生效)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; -- 阈值设为 1 秒

-- 使用工具分析慢日志(示例)
pt-query-digest /var/lib/mysql/slow.log

锁与事务

-- 查看当前锁等待
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

-- 检查长事务
SELECT * FROM information_schema.INNODB_TRX\G

-- 查看表级锁(MyISAM)
SHOW OPEN TABLES WHERE In_use > 0;

连接数暴增

-- 查看最大连接数限制
SHOW VARIABLES LIKE 'max_connections';

-- 检查当前连接数与来源
SHOW STATUS LIKE 'Threads_connected';
SHOW PROCESSLIST;

-- 临时调整连接数(立即生效)
SET GLOBAL max_connections = 1000;

缓存命中率

-- 检查 InnoDB 缓冲池命中率(低于 99% 需警惕)
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
-- 计算公式:
-- 命中率 = (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100%

四、进阶工具

性能剖析

# 使用 perf 分析 CPU 热点(需 root)
perf top -p <mysql_pid>

内存泄漏检测

# 查看 MySQL 内存分配(valgrind 需重启)
valgrind --tool=memcheck --leak-check=full mysqld
死锁分析

-- 开启 InnoDB 监控(记录到错误日志)
SET GLOBAL innodb_print_all_deadlocks = ON;

五、故障复盘与优化

日志归档

备份 /var/log/mysql/error.log 和慢查询日志。

配置调优

调整 innodb_buffer_pool_size(通常设为物理内存的 70%~80%)。

架构改进

引入读写分离、分库分表、缓存(Redis)等。

监控告警

部署 Prometheus + Grafana 监控 MySQL 核心指标。

排查流程图

网站卡顿/瘫痪
  │
  ├─ 应急处理:重启服务、限流、杀会话
  │
  ├─ 系统资源检查:CPU、内存、磁盘、网络
  │
  ├─ MySQL 内部分析:慢查询、锁、连接数、缓存
  │
  └─ 根因定位:日志分析、工具诊断
       │
       ├─ 优化配置
       ├─ 架构升级
       └─ 监控加固

9.如何处理慢查询

一、定位慢查询

1. 开启慢查询日志

2. 分析慢日志

二、分析执行计划

1. 使用 EXPLAIN

关键字段解读

  • type:访问类型(ALL=全表扫描,index=全索引扫描,range=范围扫描)

  • key:实际使用的索引

  • rows:预估扫描行数

  • Extra

    • Using filesort:需额外排序

    • Using temporary:使用临时表

    • Using where:需回表查询

2. EXPLAIN ANALYZE(MySQL 8.0+)

三、优化策略

1. 索引优化

  • 添加缺失索引

  • 避免索引失效

  • 使用覆盖索引

2. SQL 重写

  • 分页优化

  • 分解复杂查询

3. 业务逻辑优化

  • 避免全量数据拉取

  • 缓存热点数据
    使用 Redis 缓存高频查询结果(如用户基本信息)。

四、数据库调参

1. 内存优化

# 调整 InnoDB 缓冲池(通常设为物理内存的 70%~80%)
innodb_buffer_pool_size = 16G

2. 并发控制

# 调整连接池(根据硬件资源设置)
max_connections = 500
innodb_thread_concurrency = 16     -- CPU 核心数 × 2

五、架构级优化

场景

解决方案

单表数据量过大(亿级)

分库分表(Sharding)

高频复杂查询

读写分离 + 缓存(Redis)

实时聚合分析

同步至 OLAP 系统(ClickHouse)

 六、预防机制

1.自动化监控:部署 Prometheus + Grafana 监控慢查询数量、QPS、索引命中率等。

2.定期审计

3.开发规范

  • SQL 上线前必须经过 EXPLAIN 审核

  • 禁止全表更新(如 UPDATE users SET status = 1

10.MySQL优化

1)尽量选择较小的列

(2)将where中用的比较频繁的字段建立索引

(3)select子句中避免使用‘*’

(4)避免在索引列上使用计算、not in 和<>等操作

(5)当只需要一行数据的时候使用limit 1

(6)保证单表数据不超过200W,适时分割表。针对查询较慢的语句,可以使用explain 来分析该语句具体的执行情况。

(7)避免改变索引列的类型。

(8)选择最有效的表名顺序,from字句中写在最后的表是基础表,将被最先处理,在from子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。

(9)避免在索引列上面进行计算。

(10)尽量缩小子查询的结果

11.SQL语句优化案例 

例1:where 子句中可以对字段进行 null 值判断吗?

可以,比如 select id from t where num is null 这样的 sql 也是可以的。但是最好不要给数据库留NULL,尽可能的使用 NOT NULL 填充数据库。不要以为 NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了, 不管是否插入值(NULL 也包含在内),都是占用 100 个字符的空间的,如果是 varchar 这样的变长字段,null 不占用空间。可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询:select id from t where num= 0。

例2:如何优化?下面的语句?

select * from admin left    join      log on admin.admin_id    = log.admin_id where log.admin_id>10

优化为:select * from (select * from admin where admin_id>10) T1 lef join log on T1.admin_id = log.admin_id。

使用 JOIN 时候,应该用小的结果驱动大的结果(left join 左边表结果尽量小如果有条件应该放到左边先处理, right join 同理反向),同时尽量把牵涉到多表联合的查询拆分多个 query(多个连表查询效率低,容易到之后锁表和阻塞)。

例3:limit 的基数比较大时使用 between

例如:select * from admin order by admin_id limit 100000,10

优化为:select * from admin where admin_id between 100000 and 100010 order by admin_id。

例4:尽量避免在列上做运算,这样导致索引失效

例如:select * from admin where year(admin_time)>2014

优化为: select * from admin where admin_time> '2014-01-01′

12.有没有设计过数据表?你是如何设计的

13.SQL编写

例1:

用一条SQL语句查询出每门课都大于80分的学生姓名

name   kecheng    fenshu
张三     语文     81
张三     数学     75
李四     语文     76
李四     数学          90
王五     语文     81
王五     数学     100
王五     英语     90

例2:

学生表 如下:
自动编号    学号     姓名     课程编号    课程名称    分数
1                      2005001    张三   0001          数学          69
2                      2005002    李四   0001          数学          89
3                      2005001    张三    0001          数学          69
删除除了自动编号不同,其他都相同的学生冗余信息

例3:

一个叫team的表,里面只有一个字段name,一共有4条纪录,分别是a,b,c,d,对应四个球队,现在四个球队进行比赛,用一条sql语句显示所有可能的比赛组合.

例4:

怎么把这样一个表
year           month amount
1991    1         1.1
1991   2         1.2
1991   3         1.3
1991   4         1.4
1992   1         2.1
1992   2         2.2
1992   3         2.3
1992   4         2.4
查成这样一个结果
year           m1       m2       m3       m4
1991   1.1            1.2            1.3            1.4
1992   2.1            2.2            2.3            2.4 

例5:

说明:复制表(只复制结构,源表名:a新表名:b) 

例6:

原表:
courseid    coursename    score
1         java                70
2         oracle       90
3         xml                 40
4         jsp                  30
5         servlet            80

为了便于阅读,查询此表后的结果显式如下(及格分数为60):
courseid    coursename    score   mark
1         java                70       pass
2         oracle       90       pass
3         xml                 40       fail
4         jsp                  30       fail
5         servlet            80       pass
写出此查询语句

例7:

表名:购物信息

购物人  商品名称    数量

A         甲              2

B         乙          4

C         丙          1

A         丁          2

B         丙          5

给出所有购入商品为两种或两种以上的购物人记录

例8:

info 表

date                result

2005-05-09    win

2005-05-09    lose

2005-05-09    lose

2005-05-09    lose

2005-05-10    win

2005-05-10    lose

2005-05-10    lose

如果要生成下列结果, 该如何写sql语句?

date                win      lose

2005-05-09         2        2

2005-05-10         1        2

例9 mysql 创建了一个联合索引(a,b,c) 以下 索引生效 的是(1,2,4)

1、where a = 1 and b = 1 and c =1

2、where a = 1 and c = 1

3、where b = 1 and c = 1,

4、where b = 1 and a =1 and c = 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值