Appearance
数据库面试题(开发者必看)
看在前面
- 原文:数据库面试题(开发者必看)(掘金特邀作者 | 技术公众号:Java3y,文章导航 https://github.com/ZhongFuCheng3y/3y ;完整镜像见腾讯云开发者社区 article/1062249)
- 一篇覆盖开发者侧数据库常见面试题的合集。内容包括存储过程、范式、视图、删除语句、索引、事务与锁、键与约束、存储引擎、优化思路。原文末尾另有 20 道 SQL 练习题(学生/课程/成绩/教师表)与 Oracle/MySQL 区别,本篇未收录。
一、什么是存储过程?有哪些优缺点?
存储过程就像编程语言中的函数,封装了代码(PLSQL、T-SQL)。
优点:封装代码保存在数据库中供编程语言调用。预编译的代码块执行效率较高。一条存储过程代替 T-SQL 语句,可降低网络通信量、提高通信速率。
缺点:各数据库的存储过程语法几乎都不一样,难以维护(不通用)。业务逻辑放在数据库里,难以迭代。
二、三个范式是什么?
教科书定义:1NF 字段单一不可再分。2NF 不存在非关键字段对候选键的部分函数依赖。3NF 不存在非关键字段间的传递函数依赖(关键字段 → 非关键字段x → 非关键字段y)。
通俗记法(满足 3NF 则必然满足 2NF、1NF):
1NF:字段是最小的单元不可再分(学生表的年龄、性别、学号都不可再分);
2NF:字段必须完全依赖于全部主键而非部分主键(学号 1024 → 姓名Java3y、年龄22,姓名年龄都依赖主键学号);
3NF:非主键字段互不依赖、只依赖主键(系编号、系主任、系简介应单独成表,学生表只留系编号)。否则学生表里冗余存系信息形成传递依赖。
三、什么是视图?使用场景有哪些?
视图是基于基表的一种虚表:向视图提供数据的语句为 SELECT,可理解为存储起来的 SELECT 语句。
要点:视图不存储真正的数据(数据仍在基表)。程序员操作视图最终会转成操作基表。一个基表可以有 0 个或多个视图。
场景一:只给用户看该看的字段。不同角色只关心同一张表的不同列,视图既聚焦又防保密数据泄漏。
场景二:封装长查询——把复杂 SELECT 封装成视图,使用时变简单。
注意:使用视图让我们专注于逻辑,但不提高查询效率。
四、drop、delete 与 truncate 分别在什么场景使用?
| 项 | drop table | truncate table | delete from |
|---|---|---|---|
| 类别 | DDL | DDL | DML |
| 回滚 | 不可回滚 | 不可回滚 | 可回滚 |
| where | 不可带 | 不可带 | 可带 |
| 删除范围 | 表内容和结构 | 表内容 | 表内容(看 where) |
| 速度 | 快 | 快 | 慢(逐行删除) |
- 结论:不再需要一张表用 drop;删除部分数据行用 delete 且带 where;保留表而清空所有数据用 truncate。
五、索引是什么?有什么作用及优缺点?
索引是一种快速查询表中内容的机制,类似新华字典的目录。运用在表中某些字段上,但存储时独立于表之外,把数据变成有序的。
特点:定义 primary key 或 unique 约束后系统自动在相应列创建索引。用户也可按需对单/多字段加索引。是否使用索引由优化器决定。
要创建索引:表经常 SELECT、表很大且记录内容分布范围广、列经常出现在 WHERE 或连接条件中。
不要创建索引:表经常 INSERT/UPDATE/DELETE、表很小、列不常作为连接条件或出现在 WHERE 中。
优点:加快检索速度;唯一索引确保数据唯一性;查询中可使用优化隐藏器提高系统性能。缺点:降低插入、删除、修改这类维护任务的速度(数据更新需同时更新索引);占用物理和数据空间。
分类:唯一索引、主键索引。聚集索引的物理顺序与键值逻辑顺序相同,每表只能一个。非聚集索引的索引与数据分开存、带指向数据的指针,可有多个。深挖见《敖丙系列--我以为自己对索引很了解,直到我遇到了阿里面试官》。
六、什么是事务?
事务:一个 Session 中的所有操作,要么同时成功,要么同时失败。
ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
转账示例(JDBC):
connection.setAutoCommit(false)开始事务后执行两条 UPDATE,中间int a = 3/0抛异常,catch 中显式connection.rollback()——A、B 账户都恢复原样。注意:程序捕获到异常时需要在 catch 中显式回滚。否则,系统非正常退出时才由未处理异常触发自动回滚。四个隔离级别(从高到低):
Serializable(避免脏读、不可重复读、幻读)
Repeatable read(避免脏读、不可重复读)
Read committed(避免脏读)
Read uncommitted(什么都避免不了)
脏读:读到别的事务未提交的数据。不可重复读:一个事务内读到别的事务已提交的修改,前后查询不一致。幻读:一个事务内读到别的事务插入的数据,前后记录数不一致。
七、数据库的乐观锁和悲观锁是什么?
两者都是并发控制的技术手段,保证多事务同时存取同一数据时不破坏隔离性与统一性。
悲观锁:假定会发生并发冲突,查询数据时就把事务锁起来直到提交;实现用数据库锁机制:
select * from eb_sku where sku_id = 1001 for update再 update。缺点:性能低。乐观锁:假设不会发生并发冲突,提交时才检查;通过 version 或时间戳实现:
update eb_sku set stock = ..., version = #{version}+1 where sku_id = #{skuId} and version = #{version} and stock >= #{quantity}。更多见库内《for update和for update nowait》《数据库两大神器之锁》。
八、超键、候选键、主键、外键分别是什么?
超键:能唯一标识元组的属性集,单个或组合属性均可,包含候选键和主键。
候选键(候选码):最小超键,即没有冗余元素的超键。
主键(主码):从候选键中选出的、唯一完整标识数据对象的列或组合。一个表只有一个主键,且不能取空值。
外键:在一个表中存在的另一个表的主键。
例:邮寄地址(城市名,街道名,邮政编码,单位名,收件人)。它有两个候选键 {城市名,街道名} 和 {街道名,邮政编码}。选定前者作唯一标识,它就是主键。
九、SQL 约束有哪几种?
- NOT NULL:字段内容不能为空。UNIQUE:内容不能重复,一个表允许多个。PRIMARY KEY:不能重复且一个表只允许一个。FOREIGN KEY:预防破坏表间连接、防止非法数据插入外键列(必须是它指向表中的值之一)。CHECK:控制字段的取值范围。
十、快问快答
数据库运行于哪种状态可防数据丢失:archivelog mode(归档模式)。只要归档日志文件不丢失,即可有效防止数据丢失。
varchar 和 char 的区别:char 固定长度,varchar 可变长度。
MySQL 权限表:user(全局)、db(库级)、table_priv(表级)、columns_priv(列级)、host(配合 db 做主机级细控,不受 GRANT/REVOKE 影响),存于 mysql 库。
数据表损坏的修复:停服后执行
myisamchk –recover 库路径/*.MYI;或REPAIR TABLE修复表、OPTIMIZE TABLE回收删除数据后的闲置空间并重排磁盘数据行。InnoDB 的行锁加在什么上:基于索引完成。
select * from tab_with_index where id = 1 for update且 id 是索引列时为行锁;id 不是索引键时 InnoDB 将升级为表锁,并发无从谈起。
十一、MySQL 存储引擎
InnoDB(5.5+ 默认):支持 ACID 事务、行级锁、外键,面向处理大容量数据库系统。
MyISAM(旧默认):不支持事务、行级锁和外键。
MEMORY:数据全在内存,处理快、安全性不高,适合临时查询表。
同一数据库可混用多种引擎:事务要求高的表选 InnoDB,查询要求高的选 MyISAM,临时表选 MEMORY。
两引擎索引结构:都是 B+ 树。MyISAM 的 B+ 树叶子存实际数据的地址值,索引与数据分离(非聚集);InnoDB 的 B+ 树叶子存实际数据(聚集)。
十二、数据库优化的思路(原文收尾)
三个层面:SQL 优化、数据库结构优化、服务器硬件优化("这个么多花钱咯"——原作者语)。
SQL 优化书写清单
选择最有效率的表名顺序:解析器按从右到左处理 FROM 子句。记录数最少的表(或被引用最多的表)写在最后。
WHERE 子句中的连接顺序:解析顺序自右而左。表间连接写在前(左),能过滤最多记录的条件放在最右。
SELECT 子句避免使用 *:查数据字典耗更多时间且不直观。
用 TRUNCATE 替代 DELETE:全表删除数据、保留表结构时更快。
多使用内部函数提高 SQL 效率:如用 concat() 代替 || 拼接。
使用表或列的别名:名称缩短可减少扫描字符长度。
多使用 commit:会释放回滚点。
善用索引:表记录量大时用索引提升查询速度。
SQL 关键字写大写:Oracle 服务器总是先将小写转成大写后才执行。
避免在索引列上使用 NOT:遇 NOT 转为全表扫描。
避免在索引列上使用计算:索引列作为函数一部分时优化器不走索引。
用 >= 替代 >、用 IN 替代 OR:
DEPTNO >= 4直接跳到首条等于 4 的记录。总是使用索引的第一个列。复合索引只有在第一个列被 WHERE 引用时才会被使用。
数据库结构优化
- 范式优化:消除冗余、节省空间。反范式优化:适当加冗余、减少 join。拆分表:垂直拆分和水平拆分(详见《数据库怎么分库分表》)。
内容来源:原文 · 2026-10 整理回填。