ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

数据库开发实习笔试:SQL优化、索引与事务考点全解析

数据库开发实习笔试:SQL优化、索引与事务考点全解析 “数据库开发实习生”这个岗位在网易这类公司的招聘体系里属于技术岗里的“基础岗”但它的笔试通过率并不高。我当年第一次刷到这类笔试题目的时候第一反应是“SQL我会写”结果真正上手才发现人家考的不是你会不会写而是你能不能在生产环境里把数据查对、查快、查稳。这篇文章就围绕网易2018年实习生招聘的数据库开发岗位笔试聊聊这类题到底在考什么、怎么备考最有效以及我在实际面试和工作中踩过的那些坑。需要先说明一点我不会逐字复述原题笔试题目本身有版权网上的回忆版也残缺不全但这类大厂实习笔试的考察逻辑非常稳定我可以用同类型、同难度的题目还原出完整的考点版图。你把这套考点吃透刷不刷得到原题其实无所谓因为换汤不换药。1. 先搞清楚数据库开发实习笔试到底在考什么很多人把数据库开发理解成“会写SQL就行”这是最大的误解。数据库开发和DBA数据库管理员不一样DBA更侧重运维、备份、监控、集群搭建数据库开发更侧重数据建模、SQL优化、存储过程设计、数据一致性方案以及和业务代码的衔接。网易这类公司招实习生笔试阶段就要筛掉两类人一类是只会增删改查的“工具人”另一类是理论背得熟但一遇到实际场景就懵的“背书人”。所以笔试题目通常由三块组成SQL基本功、数据库原理、场景设计。SQL基本功考你能不能把复杂查询写对原理考你懂不懂索引、事务、锁、隔离级别背后的机制场景设计考你能不能针对一个业务需求给出合理的表结构、查询方案或优化策略。2018年那份卷子给我印象最深的不是某个题特别难而是它把这三块融合得非常好——每道题都不只是考一个孤立知识点。1.1 岗位JD反推出来的考点清单我看过不少大厂数据库开发实习生的JD职位描述核心要求可以浓缩成几条熟悉MySQL或Oracle、掌握SQL优化、理解事务和锁、了解分库分表、有数据建模意识。这几条对应到笔试就是固定题型。从我自己的备考经验来看你可以把考点分成四个优先级。第一优先级是SQL复杂查询包括多表关联、子查询、聚合函数、窗口函数这基本占了笔试30%以上的分数。第二优先级是索引机制和SQL优化包括索引失效场景、执行计划分析、覆盖索引、最左前缀原则。第三优先级是事务与并发控制包括ACID、隔离级别、锁的分类和死锁。第四优先级是数据建模和方案设计包括三大范式、反范式设计、表结构拆分、主键选择。这四个优先级不是随意排的。我用一个生活化类比来解释SQL是“你用什么话提需求”索引是“需求提出后系统怎么快速找到数据”事务是“多个人同时提需求时怎么保证不出乱子”建模是“你一开始怎么把数据摆放好让人提需求时不需要东翻西找”。笔试就是围绕这四层一层层往上考。1.2 从一道经典真题看考察逻辑网易2018年那份试卷里有一道题的大意是给定一个订单表和一个用户表要求统计每个用户的下单次数和总金额并且只显示下单次数大于等于2次的用户按总金额降序排列。这道题看起来简单但实际上覆盖了JOIN、GROUP BY、HAVING、聚合函数、ORDER BY五个知识点而且每个地方都埋了坑。比如用INNER JOIN还是LEFT JOIN如果用户没有订单LEFT JOIN后金额是NULLCOUNT函数会不会把NULL算进去SUM函数遇到NULL怎么办HAVING和WHERE的执行顺序有什么区别这类题的考察逻辑就是“看着简单但你的答案能暴露你对SQL执行顺序的掌握程度”。我见过不少人写出的语句逻辑上能跑通但用COUNT(字段)统计出了错误数据原因就是没搞清楚COUNT(*)、COUNT(1)、COUNT(字段)三者的区别。后面我会专门拆解这些细节。2. 高频考点拆解从SQL到索引、事务、范式这一节是整个备考的核心我把笔试中最高频的几类考点逐一拆开讲。每一类我都会给出考察形式、原理分析、容易踩的坑以及我自己的应对经验保证你去刷真题的时候能“见题知考点”。2.1 SQL基础写不对JOIN就别想拿高分SQL笔试题的第一题通常是复杂查询而复杂查询里最常考的就是各种JOIN的语义区别。INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL JOIN很多人背得滚瓜烂熟但一到手写SQL就出错问题往往出在“关联条件放在WHERE还是ON”上。我讲一个实战案例。假设有两张表学生表studentid, name, class_id班级表classid, name。要统计每个班级的学生人数包括没有学生的班级。第一反应是写SELECT c.name, COUNT(s.id) AS student_count FROM class c LEFT JOIN student s ON c.id s.class_id GROUP BY c.id, c.name;这里有两个关键点一是不能用COUNT(*)因为LEFT JOIN后没有学生的班级也会有一行记录但s.id是NULL用COUNT(s.id)才能正确统计出0二是关联条件要写在ON里如果你把s.class_id c.id放到WHERE里LEFT JOIN就会被“阉割”成INNER JOIN没学生的班级直接被过滤掉。这个题目就是笔试里最常见的“陷阱型”SQL题答案看起来和普通写法几乎一样但只有真正理解JOIN执行逻辑的人才能写对。我的建议是备考时把“ON过滤发生在连接阶段、WHERE过滤发生在连接完成之后”这句话刻在脑子里。2.2 聚合函数与分组COUNT、SUM的隐藏坑GROUP BY配套的聚合函数也是笔试必考。COUNT()统计行数COUNT(字段)统计该字段非NULL值的个数COUNT(1)和COUNT()在MySQL里效果基本一样但COUNT(字段)的行为经常被忽视。SUM也一样SUM(amount)遇到全是NULL的组会返回NULL而不是0这时候要用IFNULL或COALESCE包装。给你一道经典的易错题一个销售表salessalesman_id, order_date, amount要统计每个销售员的“最近一次销售金额”。如果直接在GROUP BY中使用MAX(order_date)取最近日期再关联原表取金额能跑通但效率差。更优的写法是用窗口函数SELECT salesman_id, amount FROM ( SELECT salesman_id, amount, ROW_NUMBER() OVER (PARTITION BY salesman_id ORDER BY order_date DESC) AS rn FROM sales ) t WHERE rn 1;窗口函数在大厂笔试里出现频率越来越高它比传统的自关联写法简洁得多。MySQL 8.0以上才支持窗口函数有些笔试环境可能标注“MySQL 5.7”这时候你要会退回到“派生表GROUP BY”的写法。备考时我建议把这两种方案的写法都练熟考场上根据环境选择。2.3 索引机制为什么明明建了索引还是慢索引部分几乎是必考而且考察深度逐年增加。最常考的是给定一个联合索引(a, b, c)以下查询哪些能用到索引哪些不能。这背后的核心是最左前缀原则。联合索引是把多个字段合成一棵B树排序规则是先按a排序a相同再按b排序b相同再按c排序。所以以下查询能用到索引WHERE a 1、WHERE a 1 AND b 2、WHERE a 1 AND b 2 AND c 3。但如果查询条件是WHERE b 2、WHERE c 3、WHERE b 2 AND c 3因为跳过了a无法利用索引的有序性只能回表或全表扫描。还有一个高频陷阱WHERE a 1 AND c 3。很多人以为这条语句能用上联合索引但实际上是“用到了索引的一部分但只用了a字段的索引”c字段的索引因为中间断层用不上。这会影响效率但不至于全表扫描。这个区分在笔试里经常被拿来出选择题。索引失效的常见场景也要滚瓜烂熟对索引列使用函数、隐式类型转换、LIKE以通配符开头、使用OR连接非索引列、索引列参与运算。我遇到过一道题WHERE DATE(create_time) 2023-01-01这个写法会导致create_time索引失效正确写法是写成范围查询WHERE create_time 2023-01-01 00:00:00 AND create_time 2023-01-02 00:00:00;2.4 事务与隔离级别并发场景下的数据一致性事务的ACID四个特性几乎是送分题但笔试真正想考的是隔离级别和锁。MySQL默认的隔离级别是REPEATABLE READ可重复读它通过MVCC多版本并发控制和间隙锁来解决幻读问题。很多人不清楚四个隔离级别分别解决了什么问题这里我用一张表帮你理清隔离级别脏读不可重复读幻读READ UNCOMMITTED可能可能可能READ COMMITTED不可能可能可能REPEATABLE READ不可能不可能可能InnoDB下基本避免SERIALIZABLE不可能不可能不可能笔试里常见的出题方式是给你一个事务执行时间线让你判断某个隔离级别下会发生什么现象。比如事务A先查询事务B插入一条新记录并提交事务A再查询问两次查询结果一不一致。在REPEATABLE READ下由于MVCC的快照读事务A两次查询结果一致不会看到新插入的数据这就避免了幻读至少对普通SELECT是这样。锁相关的考点重点记住悲观锁和乐观锁的区别、共享锁和排他锁、行锁和表锁、死锁产生的必要条件。MySQL里SELECT ... FOR UPDATE是加排他锁普通SELECT是快照读不加锁。笔试里经常让你判断一条语句会不会锁表比如不带WHERE条件的UPDATE会锁全表带主键条件的UPDATE锁对应行。2.5 存储引擎InnoDB和MyISAM的区别这个考点在实习笔试里不算大题但选择题出现概率很高。InnoDB支持事务、支持行级锁、支持外键、崩溃恢复能力强MyISAM不支持事务、只支持表级锁、查询速度快但写入并发差。现在的MySQL默认引擎已经是InnoDB实际开发中基本不会主动选择MyISAM。有一道经常出现的题是为什么MyISAM查询比InnoDB快为什么现在主流还是用InnoDB答案不是“InnoDB慢”而是“InnoDB在保证数据一致性上付出了代价”。它要维护事务日志、MVCC版本链、双写缓冲这些机制都消耗资源但换来了更高的数据可靠性。笔试里你如果能把这个“权衡”讲清楚比单纯背差异更容易拿分。2.6 范式与反范式不考理论考设计三大范式不是让你背定义而是让你用它去分析表结构是否合理、数据冗余是否可控。第一范式要求字段原子性第二范式要求非主键字段完全依赖主键第三范式要求非主键字段之间不能有传递依赖。但实际业务中过度追求范式会带来大量的JOIN查询性能反而下降。所以笔试会给你一个业务场景让你判断表设计是否合理或者要求你指出冗余字段存在的必要性。比如订单表里同时存user_name这违反第三范式但可以避免每次查询都要JOIN用户表属于典型的“用空间换时间”的反范式设计。我在做这类题时总结了一个答题模板先说设计目标减少冗余还是提升查询性能再分析范式级别最后指出冗余字段是否有业务意义。这个答题思路在笔试和面试中都很好用。3. 典型笔试题实战手把手拆解四类高频题光讲理论不够我直接给你拆四类高频笔试题每道题都还原网易2018年那类笔试的风格并给出详细解题思路和参考答案。建议你先别看答案自己动手写一遍再对照解析这样效果最好。3.1 手写SQL统计每个类别的热销商品题目给定商品表productproduct_id, product_name, category_id, price和订单明细表order_detailorder_id, product_id, quantity统计每个商品类目下的商品销量销量按所有订单中该商品的quantity之和计算只显示销量前3的类目按销量降序排列。这个题综合了JOIN、聚合、排序和TOP N。我的解题思路分三步。第一步先算每个商品的销量从order_detail里按product_id分组SUM(quantity)得到商品总销量。第二步关联product表拿到category_id再按category_id分组聚合。第三步按销量排序取前3。SELECT p.category_id, SUM(t.total_qty) AS category_sales FROM ( SELECT product_id, SUM(quantity) AS total_qty FROM order_detail GROUP BY product_id ) t JOIN product p ON t.product_id p.product_id GROUP BY p.category_id ORDER BY category_sales DESC LIMIT 3;注意点子查询里先聚合再关联比先把两张表JOIN在一起再聚合效率更高因为减少了JOIN的数据量。这也是笔试中考察“是否具备性能意识”的隐藏考点。3.2 索引优化一条慢查询怎么优化题目有一个用户登录日志表login_logid, user_id, login_time, ip数据量5000万行业务方反馈以下查询特别慢SELECT user_id, COUNT(*) AS login_count FROM login_log WHERE login_time BETWEEN 2023-06-01 AND 2023-06-30 GROUP BY user_id;这个题的核心是三个字覆盖索引。原始表的主键是id查询条件login_time没有索引数据量一大必然全表扫描。我们给(login_time, user_id)建一个联合索引查询就能覆盖所有需要的字段既满足WHERE条件定位又不需要回表拿其他字段。ALTER TABLE login_log ADD INDEX idx_time_user (login_time, user_id);这样优化后查询直接从索引中读取login_time和user_id索引已经按login_time排序区间扫描效率很高同时GROUP BY user_id在索引中也能部分实现。这个题还有一个进阶问法如果业务方要求查询任意时间段的登录用户数但表数据量还在增长怎么处理答案是考虑按时间分区或按月分表。笔试里只要答到“加联合索引覆盖索引”和“必要时做分区/分表”两层基本就稳了。3.3 事务隔离级别判断两个事务的交叉结果题目表t(id int, value int)初始数据是(1, 100)。事务A执行SELECT value FROM t WHERE id 1; 然后事务B执行UPDATE t SET value 200 WHERE id 1; COMMIT; 然后事务A再次执行SELECT value FROM t WHERE id 1; 问在READ COMMITTED和REPEATABLE READ下事务A两次查询的值分别是什么。答案在READ COMMITTED下第一次查询是100第二次查询是200因为每次SELECT都会生成新的快照能读到已提交的最新数据这也叫不可重复读。在REPEATABLE READ下第一次查询是100第二次查询还是100因为事务A开启后快照在第一次SELECT时建立后续都从这个快照读不受事务B提交影响。如果你把上面第二步的普通SELECT改成SELECT ... FOR UPDATE加锁读那么REPEATABLE READ下第二次查询也会读到200因为加锁读读到的是最新已提交数据不走MVCC快照。这是笔试里最容易出错的进阶考点一定要记住快照读和当前读走的是两套逻辑。3.4 方案设计给短视频业务设计评论表题目为短视频App设计评论表需要支持按视频查看评论列表、按时间排序、统计评论数并考虑高并发场景。这类开放性设计题没有标准答案但考官心里有一个“合理方案”的底线。我的设计思路如下。核心表commentcomment_id, video_id, user_id, content, parent_id, like_count, create_time。主键用自增id或雪花idvideo_id建普通索引用于查询列表create_time和video_id建联合索引用于排序和分页查询。CREATE TABLE comment ( comment_id BIGINT PRIMARY KEY AUTO_INCREMENT, video_id BIGINT NOT NULL, user_id BIGINT NOT NULL, content VARCHAR(500) NOT NULL, parent_id BIGINT DEFAULT NULL, like_count INT DEFAULT 0, create_time DATETIME NOT NULL, KEY idx_video_time (video_id, create_time), KEY idx_parent (parent_id) );为什么video_id和时间要建联合索引而不是单独各建一个索引因为查询条件是WHERE video_id ? ORDER BY create_time联合索引能同时用于过滤和排序避免filesort。这是典型的“根据查询场景设计索引”思维。高并发场景下评论数可以在video表里加一个comment_count字段做冗余计数每次插入评论时原子自增。但如果并发量极大频繁更新视频表会成为瓶颈可以考虑异步计数或用Redis缓存计数。笔试阶段你答到“冗余计数异步化”就比只答一张表设计高出不少。4. 笔试中的常见陷阱与个人经验这一节我不会系统讲某个知识点而是分享我在刷题和真实笔试中积累的踩坑经验。这些细节看起来小但决定你能不能拿高分。4.1 手写SQL的规范性比你想象的更重要笔试题一般是在线OJ或纸笔作答很多环境不会有真实数据库帮你校验结果所以你写出来的SQL阅卷人是靠“人肉执行”来判断对错的。这种情况下SQL的规范性就非常重要。我见过很多人在笔试题里写出SELECT *写出没有别名的子查询写出对NULL不加处理的聚合。这些写法在本地能跑但阅卷人第一眼就会觉得你经验不足。我建议每条SQL都做到明确列出查询字段不用星号、给表和子查询起清晰别名、聚合函数考虑NULL场景、排序字段明确归属表。还有一个非常实用的习惯写完SQL后口头模拟执行一遍从FROM开始到WHERE、GROUP BY、HAVING、SELECT、ORDER BY、LIMIT按顺序推演数据流向。这个习惯能帮你发现大量逻辑错误比反复检查语法更有效。4.2 搞懂SQL执行顺序很多题迎刃而解SQL的执行顺序和书写顺序不一样这个知识点笔试考得很多。FROM是先找到数据源JOIN是连接表WHERE是过滤行GROUP BY是分组HAVING是过滤分组SELECT是投影列ORDER BY是排序LIMIT是限制行数。举一个常见的错误WHERE后面想用聚合函数作为条件比如“筛选出金额大于100的订单所在的用户”写成WHERE SUM(amount) 100这在SQL语法上就不合法因为WHERE执行时还没有聚合。正确写法是放到HAVING或者用子查询。我在备考时把执行顺序写在便签上贴显示器旁边每做一道题就对照一遍。一周后这些顺序就变成条件反射了笔试时不用刻意去想。4.3 时间分配策略先做会做的再啃硬骨头网易这类笔试一般时间在90到120分钟题目大约5到8道包含选择题和手写SQL。我做过几次模拟后发现一个规律最后一道通常是开放设计题前面是SQL和原理题。我的策略是拿到卷子先用3分钟扫一遍所有题目标记出“一眼就有思路”和“需要思考”的题。优先做前者确保基本分全部拿到再集中火力啃难点。最忌讳的是在第一道SQL上死磕半小时导致后面的设计题没时间写。设计题只要有思路框架就能拿一半分完全不写就是零分差距非常大。4.4 易错点速查表考前翻一遍这几条我把自己踩过的坑整理成一个速查表考前看一遍就能避免大部分低级错误。易错点错误示例正确做法COUNT(字段)统计统计订单数时用COUNT(order_id)但不清楚NULL会被忽略明确区分COUNT(*)和COUNT(字段)LEFT JOIN后统计GROUP BY后COUNT(*)把NULL补的行算进去了统计业务字段而非行数索引列运算WHERE salary * 2 10000改写为WHERE salary 5000LIKE前缀通配WHERE name LIKE %张%尽量用张%或考虑全文索引隐式类型转换WHERE phone 13800138000确保phone是字符串时用引号日期函数调用WHERE DATE(create_time) 2023-01-01改为范围查询5. 笔试之后从“会做题”到“会干活”笔试只是门槛真正入职后你会发现数据库开发实习生的工作和笔试题目有着很直接的联系但又有明显差异。这一节聊聊入职后你会接触什么以及如何从“应试型选手”转变成“工程型选手”。5.1 数据库开发实习生的日常我在实习期间做过最基础也最频繁的工作是写存储过程和定时任务跑各种数据报表配合后端同学排查慢SQL。这些事看起来技术含量不高但特别考验SQL功底尤其是复杂报表需要把多张表的数据做横向纵向比对一个JOIN写错数据对不上排查起来非常痛苦。比写SQL更重要的是看懂执行计划。入职第一周带我的同事就让我养成一个习惯任何线上慢查询第一步不是改SQL而是先EXPLAIN。看type字段是ALL还是range还是ref看rows预估扫描行数看Extra里有没有Using filesort、Using temporary。这些信息直接决定优化方向笔试里考的索引失效场景在工作里每天都能遇到。5.2 延伸学习建议从经典案例到工程实践笔试准备到后期光刷题提升有限我更推荐结合经典案例来学。市面上有一类“数据库开发经典案例解析”的资料虽然很多以Access为示例但核心思路是通用的从需求分析、表设计、SQL实现到性能优化完整跑一遍。Access是一个桌面级数据库语法和MySQL有些差异但它的可视化查询设计器很适合新手理解JOIN、GROUP BY等操作的数据流向。如果你想系统性提升我个人的学习路径是先拿一个小项目比如图书管理系统、订单管理系统完整做一遍表设计和查询实现再在MySQL里用模拟数据把复杂SQL跑一遍最后尝试优化自己的SQL。这个过程远比刷100道题更接近真实工作状态。笔试只是职业路上的一个小节点它的价值不只是拿到offer更是逼你把数据库的核心知识体系梳理一遍。这些知识会在后续的实习、校招、正式工作中反复用到。我见过不少同学笔试前突击两周就过了但入职后因为基础不牢写出的SQL经常被review打回重写。换个角度看能在笔试阶段就把这些基础打牢反而是件好事。
返回列表