ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

数据库开发笔试核心考点:SQL、索引与事务优化

2026/8/29 12:33:32 拓冰建站 浏览量
数据库开发笔试核心考点:SQL、索引与事务优化 1. 笔试题目背后的数据库开发基本功网易2018年的实习生招聘笔试里数据库开发这个方向一直是关注度挺高的岗位。当年我在准备校招的时候也刷过这套题印象很深的是它不像算法岗那样上来就甩给你一道hard级别的动态规划也不像后端岗那样拿分布式架构连环追问。数据库开发的笔试题表面上看着都是“基础题”但想拿高分真的需要扎实的基本功。这套题里反复出现的几个主题放到今天依然有很高的参考价值SQL的灵活运用、索引优化、事务与隔离级别、存储引擎特性、数据建模能力。坦白讲这几年我作为面试官也参与过类似的招聘流程发现很多候选人的问题恰恰出在这些“基础题”上。笔试不是考你会不会背概念而是考你在具体场景里能不能作出合理的技术决策。那篇文章的正文内容没有提供网上的热词里出现了“数据库开发”和“access数据库开发经典案例解析电子书下载”这类词虽然access这类桌面数据库在互联网公司里已经不算主流但这个热词反而说明一个现实很多初学者对数据库的理解还停留在“建表、存数据、查数据”这种工具使用层面而真正的数据库开发岗位要求的是从原理到实践的全链路能力。这篇文章我就以网易这套笔试题为引子把数据库开发实习生必须掌握的核心知识体系拆开揉碎讲一遍。无论你是准备校招笔试还是想转行做数据库方向这篇文章都能让你少走很多弯路。1.1 网易这类公司考数据库开发实习生到底在考什么先说个我在面试中观察到的现象很多同学简历上写着“熟悉MySQL”但一问到“为什么这个查询慢”“怎么加索引才能让它快起来”就开始支支吾吾。笔试环节也一样题目看起来很朴素但每个考点背后都对应着真实工作里会踩的坑。网易这套笔试题的方向大致可以归为四类考察方向典型题型隐含能力SQL编程多表连接、聚合分组、子查询逻辑思维与业务理解索引与优化执行计划分析、索引失效场景性能调优意识事务与并发隔离级别、锁机制、MVCC并发场景处理能力存储与设计存储引擎对比、范式设计架构设计基础笔试的选题逻辑其实很明确实习生进来后不会一开始就接触核心系统的架构设计但一定会写SQL、会排查慢查询、会处理数据一致性问题。所以笔试筛的就是这些最底层的硬功夫。1.2 数据库开发和后端开发的边界在哪里很多准备笔试的同学会把数据库开发和后端开发混为一谈这是个挺要命的认识误区。后端开发的核心关注点是业务逻辑、接口设计、服务治理而数据库开发的核心关注点是数据本身数据怎么存、怎么查才快、怎么保证不丢不错、怎么支撑高并发。用一个生活化的类比来说后端开发是餐厅的服务员负责接待客人、下单传菜数据库开发是后厨的掌勺师傅负责把食材变成能上桌的菜。客人感受最直接的是服务员的态度但餐厅能不能开得下去靠的是后厨的出菜速度和菜品稳定度。这也解释了为什么网易这种体量的公司会单独设立数据库开发实习生的招聘通道。他们的业务场景里几千万上亿的用户数据、订单数据、行为数据全靠数据库团队来保障稳定性和性能。实习生虽然不会直接上手核心系统但从第一天起就要建立“数据敏感度”。2. SQL编程能力从“会写”到“写对”先说笔试里占比最大的部分SQL编程题。网易的笔试题一般会给两到三张表然后让你写查询常见的有求每门课成绩最高的学生、统计某时段内的订单量、找出连续登录N天的用户。题目本身不复杂但考察点很集中你能不能写出逻辑正确、性能可控、可读性强的SQL。很多同学在刷题网站练SQL的时候只看结果对不对忽略了执行效率这在笔试里是个隐性丢分点。笔试答卷通常不只看你最终写没写对面试官在筛卷子的时候会看你的SQL写法是否合理——比如有没有在索引列上做函数运算、有没有用SELECT *、有没有不必要的子查询嵌套。2.1 聚合查询和分组最容易被绕进去的地方说到聚合查询最经典的坑就是“分组后取组内某条记录”这类题。举个例子表结构大概是这样的学生表student(id, name, class_id)成绩表score(student_id, subject, score)。题目要求查询每门科目最高分对应的学生姓名。很多新手第一反应是这么写SELECT subject, student_id, MAX(score) FROM score GROUP BY subject;在MySQL的默认设置下这条SQL可能能跑出来但它隐含的问题很大student_id并不是分组字段取出来的值在逻辑上不确定虽然在MySQL的特定版本里它碰巧返回了某个值但不保证是最高分对应的那个学生。这是SQL标准里明确不允许的只有在MySQL的ONLY_FULL_GROUP_BY模式关闭时才会侥幸通过。正确的做法有两种思路。一种是先查出每科最高分再join回原表SELECT s.name, t.subject, t.max_score FROM ( SELECT subject, MAX(score) AS max_score FROM score GROUP BY subject ) t JOIN score sc ON sc.subject t.subject AND sc.score t.max_score JOIN student s ON s.id sc.student_id;另一种是使用窗口函数这也是我更推荐的写法可读性好性能也不差SELECT subject, student_id, score FROM ( SELECT subject, student_id, score, ROW_NUMBER() OVER (PARTITION BY subject ORDER BY score DESC) AS rn FROM score ) t WHERE rn 1;窗口函数在面试中是个加分项因为它体现你掌握了更新的SQL标准能力。我见过不少候选人窗口函数用得比我还溜但连GROUP BY和HAVING的语义都讲不清楚——基础不牢地动山摇别只顾着追新。2.2 子查询与JOIN别为了炫技牺牲可读性笔试题里经常出现“哪些学生选修了所有课程”这类问题。典型的解法是用NOT EXISTSSELECT s.id, s.name FROM student s WHERE NOT EXISTS ( SELECT 1 FROM course c WHERE NOT EXISTS ( SELECT 1 FROM score sc WHERE sc.student_id s.id AND sc.course_id c.id ) );这种双重否定写法第一次见确实有点绕但它逻辑清晰性能也可控。我见过有人用GROUP BY COUNT(DISTINCT)来解逻辑也对但前提是你要保证没有重复选课记录如果不加去重就会出错。这里想强调的是笔试不是炫技场写出的SQL要让面试官一眼看懂你的思路。遇到同一个需求有多种解法时优先选逻辑最简单的那种然后在注释或说明里补充你对其他方案的理解这样反而能体现你的思考深度。2.3 笔试题目里的“易错点”清单根据我刷题和改卷的经验SQL笔试常见的丢分点集中在下面几个地方易错点错误示例正确做法GROUP BY误用分组后SELECT非聚合字段确保SELECT字段要么在GROUP BY里要么被聚合函数包裹等值连接漏条件FROM a JOIN b 后面直接WHERE明确连接条件用ON指定NULL值处理用“ NULL”判断用IS NULL 或 IS NOT NULL字符集导致乱码连接没指定UTF8连接串加characterEncoding参数忘记去重一对多join后数据翻倍先聚合再去重或先DISTINCT再JOIN每一个坑都对应真实工作场景。比如NULL判断这个问题我在线上系统排查数据异常时经常遇到某个统计报表的数据莫名少了一查发现是WHERE条件里写了status NULL永远查不到数据。3. 索引优化笔试里的“隐形大头”索引这块内容在笔试里往往不会直接以“请说明B树的原理”这种死板方式考而是会给你一条慢SQL让你分析问题在哪、怎么优化。这其实比背概念难多了因为它要求你理解索引的底层结构、最左前缀原则、回表、覆盖索引这些概念并且能在具体场景里灵活运用。3.1 为什么MySQL的索引选B树而不是哈希表先解释一个基础但常考的点哈希索引的查询时间复杂度是O(1)听起来比B树的O(log N)快多了为什么主流OLTP数据库还是默认用B树答案是哈希索引不支持范围查询也不支持排序。你的业务SQL里WHERE create_time BETWEEN 2024-01-01 AND 2024-01-31这类范围条件太常见了哈希索引直接无法应对。而B树的数据结构天然支持区间扫描叶子节点之间用指针串联可以顺序读取对磁盘IO也更加友好。还有个面试常问的点B树和B树的区别。一句话版答案是B树每个节点都存数据一次查询可能在非叶子节点就命中了B树只在叶子节点存数据非叶子节点只存索引键所以树更矮、层数更少磁盘IO次数更少。而且B树的叶子节点形成了有序链表范围查询效率极高。3.2 联合索引的最左前缀原则笔试必考联合索引是笔试中的高频考点考察方式通常是表里有三个字段a、b、c建了一个联合索引(a, b, c)问下面哪些查询能用到这个索引。这里必须记住最左前缀原则查询条件里必须包含联合索引最左侧的字段索引才会被使用。换句话说a、ab、abc都能命中索引但只查b、只查c、查bc都用不上这个联合索引的完整能力。实际工作中有一个非常典型的场景订单表有个联合索引(shop_id, status, create_time)。你要查某个店铺某个状态的订单并且按时间排序这条SQL就能完美命中索引。有时候你把条件顺序写成WHERE status PAID AND shop_id 123查询优化器也会自动调整顺序但如果你只传了status索引就废了。这背后的逻辑其实和字典的检索方式一样你要查“张三”联合索引(姓, 名)能帮你先按姓定位再按名筛选。如果你只知道名是“三”那这本字典就没法直接帮你定位了只能全表翻。3.3 回表与覆盖索引慢查询优化的关键InnoDB的主键索引和二级索引结构不同。主键索引的叶子节点直接存整行数据所以叫聚簇索引二级索引的叶子节点存的是主键值查到二级索引后还需要拿着主键值回到主键索引里再查一次完整数据这个动作就叫回表。回表不是问题问题在于回表的次数。查10行回表10次没关系但如果你用二级索引查出了50万行然后全部回表性能就会急剧下降。解决思路之一就是覆盖索引让二级索引的叶子节点直接包含你需要的所有字段这样查询就不用回表了。举例来说查询语句是SELECT order_id, status FROM order_table WHERE shop_id 123如果你建的联合索引是(shop_id, status, order_id)那么查询要的字段全在索引里直接就能从索引返回数据不需要回表。这就是覆盖索引的威力。我在排查慢查询时经常遇到的一种情况是查询只用了索引的一部分字段但SELECT后面带了一堆不在索引里的字段导致每条记录都要回表。这种情况一般不需要改SQL只需要把索引调整成覆盖查询字段的联合索引就能解决。当然索引不是越多越好因为每次写入都要维护索引索引多了写入性能会下降这是一个需要权衡的决策。3.4 拿到一条慢SQL我是怎么分析的在笔试或者面试的延伸追问里面试官可能会给你一条SQL问你怎么优化。我一般的排查思路是分四步走第一步看表的数据量和索引情况。没有索引的话任何一种查询都可能是慢查询先建索引再说。第二步用EXPLAIN看执行计划。重点关注type列全表扫描是ALL走索引是ref或range最理想的是const。还要看key列确认实际用到的索引是不是你预想的那个。第三步看是否回表、是否filesort、是否产生临时表。filesort通常意味着排序没有用到索引临时表意味着查询可能太重需要想办法分流。第四步分析业务逻辑是否可以改写SQL。比如把大查询拆成小查询把复杂关联拆成多次简单查询在业务代码里做拼装。很多情况下拆开以后性能反而更好因为数据库压力更小、锁的范围更小。4. 事务、锁与隔离级别数据库开发的灵魂如果SQL和索引是数据库开发的“外功”那事务和锁就是“内功”。这也是网易笔试里最容易拉开差距的地方。因为这部分内容靠死记硬背很难应付场景题。4.1 ACID四个特性到底怎么理解事务的ACID四个特性笔试几乎必考但很少有人能讲到点子上。我的理解是原子性Atomicity保证一个事务要么全部成功、要么全部回滚强调的是“不做半拉子事”一致性Consistency保证事务执行前后数据库的约束都满足强调的是“数据合乎逻辑”隔离性Isolation保证并发事务之间互不干扰持久性Durability保证事务提交后数据不会丢失。这四个特性中一致性是目的原子性、隔离性、持久性都是手段。这个理解很关键因为数据库里很多机制——undo log、redo log、锁、MVCC——本质上都是为了让“一致性”在各种异常场景下依然成立。举个例子转账场景A给B转100元数据库要做的就是把A的余额减100、B的余额加100。这两个操作必须在一个事务里完成如果中间任何一步失败整个事务回滚A和B的余额都保持不变。这就是原子性。如果此时C正好在查A和B的总资产他应该看到的是转账前或转账后的结果而不是“A扣了钱但B没加上”的中间状态这是隔离性要做的事。4.2 四种隔离级别和它们解决的问题SQL标准定义了四种隔离级别从低到高分别是读未提交READ UNCOMMITTED、读已提交READ COMMITTED、可重复读REPEATABLE READ、串行化SERIALIZABLE。每个隔离级别解决不同的并发问题但也各有代价隔离级别脏读不可重复读幻读实现方式读未提交可能可能可能无隔离读已提交不可能可能可能快照读当前读可重复读不可能不可能可能MVCC串行化不可能不可能不可能全表加锁这里需要区分三个概念脏读是读到了另一个事务未提交的数据不可重复读是同一个事务内两次读取同一行结果不一样幻读是同一个事务内两次查询同一范围结果集行数不一样。MySQL的InnoDB默认隔离级别是可重复读比SQL标准的默认值读已提交更严格。InnoDB在可重复读级别下通过MVCC加上间隙锁Gap Lock在特定条件下还能解决幻读问题。但要注意间隙锁也不是万能的如果查询走的是全表扫描幻读还是会出现。4.3 MVCC机制理解了它很多概念就串起来了MVCCMulti-Version Concurrency Control多版本并发控制是InnoDB实现高并发读的核心机制。它的思路是写操作创建新版本数据读操作读旧版本快照读写互不阻塞。在InnoDB里每行数据有两个隐藏字段trx_id最后一次修改该行的事务ID和roll_pointer指向undo log中旧版本的指针。当你开启一个事务并执行SELECT时会根据事务的隔离级别生成一个读视图ReadView通过比较事务ID和当前活跃事务列表判断哪些版本对当前事务可见。MVCC带来的好处很明显普通SELECT不加锁读性能和单线程一样高写操作也不会被读操作阻塞。这就是为什么InnoDB在默认级别下依然能支撑很高的并发TPS。笔试里如果出现MVCC题记住一个核心点MVCC解决的是快照读普通SELECT的隔离问题而当前读SELECT ... FOR UPDATE、UPDATE、DELETE走的是另一套加锁逻辑。很多同学把这两者混为一谈一说到隔离级别就只会背“可重复读解决不可重复读”但完全说不清底层是怎么实现的。4.4 锁的类型与控制性能与一致性的平衡事务并发控制离不开锁。InnoDB的锁可以从两个维度划分按粒度分有表锁、行锁按模式分有共享锁S锁、排他锁X锁。行锁在InnoDB里其实有三种实现记录锁Record Lock锁定单行记录间隙锁Gap Lock锁定一个范围但不包含记录本身临键锁Next-Key Lock锁定范围和记录本身。InnoDB默认使用临键锁来解决幻读问题。实际开发中锁最容易出问题的场景是死锁。死锁的本质是多个事务按不同顺序持有资源互相等待对方释放。经典的死锁场景事务A先更新id1的记录事务B先更新id2的记录然后A想更新id2B想更新id1。此时A持有id1的锁等id2B持有id2的锁等id1两个事务都不愿意放手死锁就产生了。InnoDB的解决办法是死锁检测发现死锁后会自动回滚代价更小的事务让另一个事务继续执行。虽然数据库能自动处理但高并发场景下死锁频繁发生说明你的业务代码存在锁顺序不一致的问题。我的经验是涉及多个资源的更新操作尽量按照相同的顺序加锁这样可以显著减少死锁概率。5. 存储引擎与数据建模笔试向工程实践的延伸存储引擎和数据建模这部分看似是理论题实际是数据库开发实习生后续参与真实项目的基础。网易的题库里会涉及但通常不会考得太深。不过如果你想在面试中脱颖而出这部分是很好的加分点。5.1 InnoDB和MyISAM的对比常考但要有新意InnoDB和MyISAM的对比是面试题库里的常青树。常规答案我已经不想重复了直接上表对比项InnoDBMyISAM事务支持支持不支持锁粒度行锁表锁外键支持不支持崩溃恢复支持redo log不支持全文索引8.0支持支持适用场景OLTP在线交易读多写少的分析表但我想补充一个很多人忽略的点MyISAM的索引文件和数据文件是分开的所以二级索引可以直接引用数据文件的偏移量不需要像InnoDB那样在二级索引里存主键值。这导致在某些单行查询场景下MyISAM的二级索引查询反而更直接。但MyISAM整体性能还是输在表锁和崩溃恢复上——一旦数据库异常重启MyISAM表很容易损坏恢复起来非常痛苦。从工程实践的角度如果你现在还负责维护一个MyISAM表我建议尽快迁到InnoDB。MySQL 8.0开始系统表也已经是InnoDB了MyISAM在新版本里基本处于被淘汰状态。5.2 三范式与反范式数据建模的平衡术笔试题里偶尔会考范式相关的内容比如“第三范式和第二范式的区别”或者给你一个表结构问它符合第几范式。理解范式不难难的是实际建模时该怎么取舍。第一范式1NF要求字段不可再分第二范式2NF要求非主键字段完全依赖于主键不能只依赖主键的一部分第三范式3NF要求非主键字段之间不能有传递依赖。实际工作中完全遵循第三范式的设计往往是“理想化”的。比如订单表和商品表如果严格按照3NF订单表上不应该存商品名称只能存商品ID需要展示时再去join商品表。但真实系统里订单快照里有商品名称、商品价格这些冗余字段因为下单时商品名称可能会变如果订单表不冗余历史订单展示就会出现信息漂移。这说明建模的终极目标不是“符合范式”而是“符合业务”。高并发系统里适度冗余、适度反范式是常态。笔试如果考这类题你的回答思路应该是先说明范式标准再结合实际场景说哪些地方可以反范式以及为什么这么做。5.3 主键设计自增主键还是业务主键主键设计是数据建模的一个重要细节。常见的选择有自增主键、UUID作为主键、业务字段作为主键。自增主键的好处是B树写入时是顺序的不会频繁页分裂写入性能最好占用空间小二级索引的大小也随之变小。UUID主键的坏处是长度大、无序写入会导致随机IO和频繁页分裂。如果一定要用UUID存储订单号之类的字段我个人的建议是把它作为业务唯一键加唯一索引而不是作为主键。不过自增主键在分布式场景下也有问题多个节点同时自增会冲突所以需要引入分布式ID生成方案比如雪花算法。这属于分库分表的范畴了实习生笔试一般不会考这么深但如果面试官问到你最近在关注什么技术可以聊一聊这块展示你的技术视野。5.4 分库分表高并发下的无奈之选网易这种体量的业务单库单表肯定扛不住。分库分表是数据库开发工程师绕不开的话题。虽然实习生笔试一般不会考设计题但如果你能在面试里聊清楚“为什么要分库分表、分什么、怎么分”会很加分。分库分表的核心思路是把数据分散到多个独立的数据库实例或多个表中降低单库单表的压力。常见的拆分方式有垂直拆分和水平拆分。垂直拆分是把不同业务的数据放到不同库比如订单库、用户库、商品库分开水平拆分是把同一张表的数据按某个维度分到多个表比如按用户ID哈希分到32个表。水平拆分最核心的问题是选择分片键。分片键选择得好查询可以精确定位到某个分片选择不好跨分片查询就会变成灾难。以订单表为例如果业务上经常按用户ID查订单按用户ID分片就是合理的但如果你经常按时间范围统计全量订单那按用户ID分片会导致所有分片都要扫一遍这时候就需要引入中间层来做聚合复杂度会上升一个量级。6. 笔试经验与避坑指南最后这部分我想结合我自己当年准备笔试和后来帮团队筛简历、出面试题的经验聊聊实际应试时的策略和常见问题。6.1 时间分配不要在一道题上死磕数据库开发笔试一般限时60到90分钟题目量在5到8道之间。我的建议是先快速扫一遍所有题目把会做的、有思路的题先做完再回头啃难题。编程题如果卡住了先写出伪代码或者解题思路也可以拿部分分。笔试系统一般不只看最终结果你的解题过程、代码注释、思路说明都可能被阅卷人看到。见过太多候选人SQL题写得乱成一团最后结果也不对而另一些人虽然答案不完全正确但注释里写清了每一步的思路反而会被捞进面试。6.2 SQL题目写完以后一定要“跑一遍”我见过一个真实案例有位候选人写了一条查询“每个部门工资最高的员工”逻辑完全正确但表名拼错了。笔试系统直接报错最后零分。这种低级错误完全可以在提交前检查出来。SQL写完之后我一般会做三件事第一检查表名、字段名拼写第二检查JOIN条件是否完整避免笛卡尔积第三在心里模拟一遍数据确认边界情况。比如查“最近7天订单”如果当前是凌晨1点日期边界怎么算是用DATE_SUB(NOW(), INTERVAL 7 DAY)还是用CURDATE()两个结果在不同时间点会有差异。6.3 原理题别只背答案要能讲出“为什么”比如问你“为什么数据库连接池的大小不能设得太大”很多人会回答“连接多了会占用资源”。这种答案等于没答。更好的回答是数据库连接数是有限的每个连接都占用内存和文件描述符连接数太多数据库CPU上下文切换开销变大反而导致吞吐下降连接数是瓶颈时排队等待的请求超时率上升。这种有因果链条的答案才是面试官想听的。笔试同理判断题和简答题里你给出的理由比你的结论重要得多。6.4 笔试之后如何为面试做铺垫笔试不是终点。网易的流程一般是笔试通过后进入面试面试官会拿着你的笔试卷来提问。所以笔试交卷后一定要复盘尤其是你踩坑的题目面试时大概率会被问到。复盘思路有三条第一把每道题的考察点标注出来比如“这题考的是SQL窗口函数”“那题考的是索引失效”第二查漏补缺把不会的知识点补上第三准备一两个你真实做过的数据库相关的项目或实验面试时能拿出来讲细节的项目比你简历上写一堆技术栈有用得多。我自己在面试候选人的时候最看重的就是两件事基础的扎实程度和解决问题的思路。基础不牢后面培养成本很高思路清晰哪怕当前技术水平一般也值得培养。7. 给备考者的最后建议回过头看网易2018年这套数据库开发实习生的笔试题万变不离其宗的还是我上面讲的那些核心知识模块。题型可能会变公司可能会变但数据库开发这个岗位对基本功的要求这些年基本没变过。我个人在实际操作中的体会是备考数据库开发方向最忌讳的就是“只看不练”。SQL语法看十遍不如手写一遍索引原理背十遍不如真的用EXPLAIN分析一条慢SQL。我推荐一个练习路径先拿网易这套题练手把不会的知识点整理出来然后去LeetCode或者牛客网上找同类题巩固最后拿出你自己的项目里的真实SQL来优化看执行计划看索引命中情况。数据库开发这个岗位入门门槛不算高但天花板很高。从写SQL到调优从调优到架构设计每一步都需要大量的实践积累。实习生的笔试只是一个起点后面还有很长的路要走。但如果你能把这些基础打得足够扎实后面的路就会越走越顺。