4. async/await原理与陷阱面试题

本章聚焦数据库与ORM在实际面试中的高频考点,涵盖索引优化、连接池调优、N+1查询治理、事务与数据一致性等核心技能。面试官往往不只问用法,更关注你对性能瓶颈定位和架构权衡的理解。以下回答框架图可快速串联优化要点。

ORM/数据库 优化策略 索引设计 查询SQL优化 连接池调优 缓存策略 读写分离

面试问答卡片

问题1:如何设计 MongoDB 复合索引?举例说明。

考察点:索引原理、查询模式匹配、ESR 原则、覆盖查询意识。

回答框架:1. 先分析常用查询过滤条件(等值、排序、范围);2. 按 ESR(等值、排序、范围)顺序组织索引字段;3. 确认所有返回字段是否在索引内实现覆盖查询。

示例回答:“对于订单查询,过滤条件是 status 和 createAt 排序。我创建复合索引 { status:1, createAt:-1 }。等值字段 status 在前,排序字段 createAt 在后。若只需返回 _id 和 status,则无需回表查询。” (72字)

可能追问:Q: 如果 status 基数值很小(如 0/1/2),索引效率如何?A: 对 status 的过滤性差,可考虑只对 createAt 建立索引。Q: 如何监控索引使用情况?A: 使用 explain() 检查查询计划。

避坑提醒:避免对每个查询都建单独索引,注意联合索引字段顺序;谨慎使用多键索引,可能引发索引膨胀。

问题2:Sequelize 中 N+1 查询如何产生?如何解决?

考察点:ORM 延迟加载陷阱、eager loading 用法、SQL 合并优化意识。

回答框架:1. 指出 N+1 场景——主查询 N 条记录,关联查询 N 次;2. 使用 include 配合 attributes 或 raw 合并为 JOIN;3. 对比 lazy loading vs eager loading 的适用场景。

示例回答:“例如查询所有用户及其订单,默认会先查用户列表,再对每个用户单独查订单,共 N+1 条 SQL。解决:Model.findAll({ include: [Order] }),Sequelize 自动生成 LEFT JOIN 合并一次查询。” (82字)

可能追问:Q: 如果关联表数据量很大怎么办?A: 分页 + 子查询限制返回量,或使用 raw 查询手动控制。Q: 多级 include 可能导致数据重复如何处理?A: 使用 distinct:true 或通过 group 去重。

避坑提醒:不要盲目给所有关联都加 include,注意性能测试;数据量大时建议分次查询 + 内存关联。

问题3:如何合理配置 MySQL 连接池(Sequelize 下)?

考察点:连接池参数(max/min/acquire/idle)含义、并发场景调优、数据库端限制配合。

回答框架:1. 介绍 max 根据并发峰值 + 单连接处理能力估算;2. min 保持活跃连接减少冷启动;3. acquire 控制等待超时防止雪崩;4. idle 控制空闲回收。

示例回答:“利用注释:并发 500 请求,单连接处理 50 QPS,则 max 设为 20;min 设为 5;acquire 设为 3000ms;idle 设为 10000ms。同时配合 MySQL 的 max_connections 限制,避免破产。” (82字)

可能追问:Q: 如果出现连接泄露如何排查?A: 使用 connection.afterRelease 事件日志,或通过性能监控工具。Q: pool eviction 机制?A: Sequelize 基于 generic-pool,支持 idle 超时回收。

避坑提醒:max 值不能超过 MySQL 的 max_connections;min 过多浪费资源;acquire 超时设置太短会导致频繁重试影响性能。

项目案例:电商订单服务(MySQL + Sequelize 读写分离实战)

项目名称高并发订单系统 V2.0
业务背景电商平台核心订单服务,支撑用户下单、查询、状态变更,要求 99.9% 可用性。
规模指标日均订单 100 万,峰值并发 2000 QPS;订单表 5000 万行;响应时间 P99 < 200ms。
技术栈Node.js + Express + Sequelize + MySQL(主从)+ Redis(缓存)
核心难点1. 热点商品库存与订单数据一致性(分布式事务);2. 订单查询大字段 slow log;3. 主从同步延迟导致刚下单查不到。
面试高频追问Q: 如何保证主从延时场景下的最终一致性?
Q: 为什么用 Sequelize 而不是直接写 SQL?
Q: 分表字段如何选择?
Q: 订单状态更新如何避免并发覆盖?
可迁移回答话术“我们采用读写分离架构,写走主库,读走从库。对于即时一致性要求高的读(如支付回调),强制读主库。订单列表查询使用 Redis 缓存分页索引,回源到从库。Sequelize 的 replication 配置自动负载读从库。在悲观锁和乐观锁之间,我们选择乐观锁(version 字段)处理状态更新,避免行锁长时间持有。” (120字)