2025 面试记录
# 宝洁(外包)
# Django 与 Flask 技术选型问题
# Django
- 全功能的框架,适合中大型项目开发。
- 内置了强大的 ORM 工具,简化了数据库操作。
- 提供了许多内置的功能模块和插件,如认证、管理后台等。
- 学习曲线较陡峭,配置复杂,但一旦熟悉,可以提高开发效率。
# Flask
- 简洁、灵活的框架,适合小型项目或快速原型开发。
- 与 Django 相比,学习曲线较平缓,配置相对简单。
- 没有内置的 ORM 工具,但可以与其他 ORM 库集成。
- 可扩展性强,可以根据项目需求选择需要的插件和库。
# 项目中出现 OOM,如何进行原因分析及定位
# 横表变竖表表格设计问题
# 横表(宽表)设计
- 特点:
- 列代表同一属性的不同维度(如 语文成绩, 数学成绩)。
- 适合固定属性的场景,查询时无需多表关联。
- 优点:
- 业务描述:横表的好处是清晰可见,一目了然,数据描叙很清晰。每个字段就是一个 KPI 指标。
- 性能方面:横表从数据库映射到内存的速度比竖表要快很多。
- 代码复杂:横表不需要做行列转换,代码比较简单
- 缺点:
- 扩展性差:新增科目需修改表结构(ALTER TABLE)。
- 冗余存储:若学生未参加某科目考试,字段值为 NULL,浪费空间。
- 查询复杂度高:统计多科目时需遍历多个列。
- 示例
| 姓名 | 语文 | 数学 | 英语 |
|--------|------|------|------|
| 张三 | 90 | 85 | 95 |
| 李四 | 88 | 92 | 89 |
2
3
4
# 竖表(长表)设计
- 特点:
- 将横表中多列拆解为多行,通过 属性-值 模式存储。
- 符合数据库 第三范式(3NF),减少冗余。
- 优点:
- 扩展性强:新增科目只需在 subjects 表中插入一行。
- 无冗余:仅存储存在的成绩。
- 灵活查询:通过 JOIN 实现复杂分析。
- 缺点
- 业务描述:竖表的数据描叙很不清晰,举例说明:学生成绩表的竖表形式,成绩这个字段,即可是数学成绩、也可是语文成绩,不像横表形式数学成绩、语文成绩各成一个字段描述 KPI 指标来得清晰明了。
- 性能方面:系统展现的报表大部分是横表,这意味着竖表要进行转列。这样需要额外的性能开销。尤其是当报表进行聚合计算时,性能更糟糕。这是因为竖表从数据库映射到内存比横表要慢。
- 代码复杂:需要做行列转换,代码量、复杂性都会增加很多。
- 示例
| 姓名 | 科目 | 成绩 |
|--------|------|------|
| 张三 | 语文 | 90 |
| 张三 | 数学 | 85 |
| ... | ... | ... |
2
3
4
5
# 设计原则与场景选择
- 何时选择竖表(长表)? 属性动态变化:如电商商品的动态属性(颜色、尺寸)。
稀疏数据:大量字段可能为 NULL。
需要灵活扩展:频繁新增属性或维度。
- 何时保留横表(宽表)? 属性固定不变:如身份证号、出生日期等固定字段。
高频查询且需高性能:宽表查询无需 JOIN,适合 OLAP 场景(如宽表模型)。
# 竖表设计模式
关系模型(推荐)
设计要点:
主表:存储主体信息(如 students)。
维度表:存储属性维度(如 subjects)。
事实表:通过外键关联主表和维度表,存储具体值(如 student_scores_long)。
如下查询张三的数学成绩
SELECT s.student_name, sub.subject_name, sc.score
FROM students s
JOIN student_scores_long sc ON s.student_id = sc.student_id
JOIN subjects sub ON sc.subject_id = sub.subject_id
WHERE s.student_name = '张三' AND sub.subject_name = '数学';
2
3
4
5
# 推荐阅读
数据库设计---关于建表的时候选择横表和竖表(纵表)的一点思考 - 程序员大本营 (opens new window) 横表与竖表性能浅析 - 潇湘隐者 - 博客园 (opens new window)
# 项目中经常用到的索引优化举例
在项目中,数据库索引优化是提升查询性能的关键手段。以下通过具体场景和示例说明常见的索引优化方法:
# 1. 选择高选择性的列建立索引
场景:用户表根据手机号快速查询。
优化:
- 手机号(
mobile
)唯一性高,适合作为索引字段。
CREATE INDEX idx_mobile ON users(mobile);
效果:使 WHERE mobile='138xxxx'
的查询从全表扫描变为索引查找。
# 2. 复合索引的最左前缀原则
场景:订单表按用户 ID 和创建时间查询最近的订单。
优化:
- 建立复合索引
(user_id, created_at)
,优先高筛选性字段。
CREATE INDEX idx_user_created ON orders(user_id, created_at);
生效的查询:
SELECT * FROM orders WHERE user_id=1001 ORDER BY created_at DESC LIMIT 10;
不生效的查询:
SELECT * FROM orders WHERE created_at > '2023-01-01'; -- 未使用user_id,无法命中索引
# 3. 覆盖索引减少回表
场景:查询文章标题和作者,避免回表。
优化:
- 包含所有查询字段的复合索引。
CREATE INDEX idx_title_author ON articles(title, author);
查询:
SELECT title, author FROM articles WHERE title LIKE 'Python%'; -- 直接通过索引返回数据
# 4. 索引下推(Index Condition Pushdown, ICP)
场景:MySQL 中筛选复合索引部分条件。
优化:
- 启用 ICP 后,在索引层过滤数据,减少回表次数。
-- 索引: (age, city)
SELECT * FROM users WHERE age > 25 AND city = 'Beijing';
2
效果:直接在索引中过滤 city
,减少访问数据行的次数。
# 5. 前缀索引优化长字段
场景:存储长文本(如地址)时减少索引大小。
优化:
- 对
VARCHAR(255)
的address
字段,取前 20 字符建立索引。
CREATE INDEX idx_address_prefix ON users(address(20));
适用场景:前 N 个字符足够区分不同值(如城市+街道缩写)。
# 6. 函数索引处理计算字段
场景:按月份统计订单,避免全表扫描。
优化:
- 对日期字段使用函数索引(如 MySQL 8.0+)。
CREATE INDEX idx_month ON orders((DATE_FORMAT(created_at, '%Y-%m')));
查询:
SELECT COUNT(*) FROM orders WHERE DATE_FORMAT(created_at, '%Y-%m') = '2023-10';
# 7. 避免索引失效的常见陷阱
隐式类型转换:字段为字符串,查询用数字导致索引失效。
-- mobile字段为VARCHAR,但查询使用数字 SELECT * FROM users WHERE mobile = 13800138000; -- 索引失效
1
2索引列参与运算:
SELECT * FROM users WHERE age + 1 > 30; -- 无法使用age索引
1OR 条件不当使用:
-- 索引: (a), (b) SELECT * FROM table WHERE a=1 OR b=2; -- 可能全表扫描,改用UNION优化
1
2
# 8. 定期维护索引
场景:频繁更新的表出现索引碎片。
优化:
- 重建索引(如 MySQL 的
OPTIMIZE TABLE
)。
OPTIMIZE TABLE orders;
效果:减少索引碎片,提升查询效率。
# 9. 删除冗余索引
分析工具:
- 使用
pt-duplicate-key-checker
(Percona 工具)检测重复索引。
优化: - 删除重复或未被使用的索引。
-- 假设存在 (a) 和 (a,b),删除单列索引 (a)
DROP INDEX idx_a ON table;
2
# 10. 使用部分索引(条件索引)
场景:只索引部分数据(如有效订单)。
优化:
- 创建条件索引(PostgreSQL 支持)。
CREATE INDEX idx_active_orders ON orders(user_id) WHERE status = 'active';
效果:减少索引大小,提升查询效率。
# 总结
优化方法 | 适用场景 | 示例 |
---|---|---|
高选择性索引 | 唯一值多的列(如手机号、邮箱) | CREATE INDEX ON users(mobile) |
复合索引最左前缀 | 多条件查询或排序 | (user_id, created_at) |
覆盖索引 | 避免回表的高频查询 | SELECT title FROM articles |
前缀索引 | 长文本字段(地址、备注) | address(20) |
函数索引 | 计算字段的查询条件 | DATE_FORMAT(created_at, ...) |
索引维护 | 频繁写入导致碎片 | OPTIMIZE TABLE |
通过合理设计索引并结合执行计划分析(EXPLAIN
),可显著提升数据库性能。
# 项目中实际使用 Redis 和 MQ 的场景举例
在项目中,Redis 和消息队列(MQ)通常结合使用以实现高性能、高可靠性和解耦的系统设计。以下结合 任务调度系统 和 付费系统 的具体场景,分析两者的典型应用:
# 一、Redis 的典型应用场景
# 1. 任务调度系统
任务队列管理
Redis 的List
或Stream
数据结构可作为轻量级任务队列,支持任务的快速入队和消费。
示例:
在任务调度中,将待执行任务(如数据清洗、报表生成)存入 Redis 队列,由多个 Worker 节点通过BLPOP
命令竞争消费任务,实现分布式任务处理。
优势:- 单线程模型保证任务顺序性;
- 高吞吐量,适合实时性要求较高的任务。
分布式锁
使用 Redis 的SETNX
命令实现分布式锁,防止多个节点同时处理同一任务。
示例:
任务调度中心需确保某个定时任务(如每日对账)仅被一个节点执行,通过 Redis 锁实现互斥。
# 2. 付费系统
库存预扣减与缓存
Redis 的高性能读写能力适合处理瞬时高并发请求,如秒杀场景中的库存扣减。
示例:
用户下单时,先通过 Redis 原子操作(DECR
)预扣减库存,再将订单信息发送至 MQ 异步持久化到数据库,避免数据库直接承压。
优势:- 内存操作响应快;
- 结合 Lua 脚本保证原子性。
会话状态与临时数据存储
存储用户支付状态、临时令牌等短期数据。
示例:
用户发起支付后,将支付会话状态(如支付超时时间)存入 Redis,支付完成后清除。
# 二、MQ 的典型应用场景
# 1. 任务调度系统
异步解耦与削峰填谷
MQ 将任务生产与消费解耦,支持流量削峰。
示例:
当系统突发大量计算任务(如用户批量导出数据),MQ 缓存任务请求,Worker 按自身处理能力消费,避免服务崩溃。
优势:- 生产者与消费者无需强依赖;
- 支持消息持久化,防止任务丢失。
任务状态同步
通过 MQ 广播任务状态变更事件(如任务完成、失败),其他系统订阅处理。
示例:
任务完成后发送消息至 MQ,通知监控系统更新任务日志。
# 2. 付费系统
异步支付与最终一致性
MQ 实现支付流程的异步化,确保核心链路高效。
示例:
用户支付成功后,通过 MQ 异步触发后续操作(如发送短信、更新会员积分),避免阻塞支付主流程。
优势:- 支付接口响应快;
- 通过重试机制保证消息最终处理。
订单与库存一致性
采用 MQ 的可靠投递机制,结合补偿事务保证数据一致性。
示例:
支付成功后发送扣减库存消息至 MQ,若消费失败则触发重试或回滚 Redis 预扣库存。
# 三、Redis 与 MQ 的联合使用场景
# 1. 任务调度 + 付费系统的协同设计
场景示例:
用户购买会员后,系统需异步执行多个任务(如发放优惠券、更新权限)。
流程设计:- Redis 缓存用户购买状态,防止重复提交;
- 支付成功后,发送任务消息至 MQ;
- 多个 Worker 消费 MQ 消息,并行执行子任务;
- Redis 记录任务执行进度,供前端查询。
优势:
- Redis 处理实时状态,MQ 保障任务可靠性;
- 系统扩展性强,新增任务类型只需新增消费者。
# 四、技术选型建议
场景 | Redis 适用性 | MQ 适用性 | 典型工具 |
---|---|---|---|
实时库存扣减 | ✅ 高并发原子操作 | ❌ 无需持久化 | Redis + Lua |
支付异步通知 | ❌ 需持久化保障 | ✅ 消息可靠投递 | RabbitMQ/Kafka |
分布式任务调度 | ✅ 轻量级队列 | ✅ 复杂任务依赖 | Redis Stream/RocketMQ |
数据最终一致性 | ❌ 无事务补偿 | ✅ 重试与死信队列 | Kafka + 事务消息 |
# 五、总结
- Redis:适合 实时性高、数据量小、容忍部分丢失 的场景(如缓存、分布式锁、轻量队列)。
- MQ:适合 异步解耦、高可靠性、数据持久化 的场景(如支付通知、任务流水)。
- 联合使用:通过 Redis 处理实时状态,MQ 保障异步流程,可构建高性能且可靠的系统,典型案例如 秒杀系统(Redis 扣库存 + MQ 异步下单)。
# 光际科技
# 如何在限定期限内进行工作量评估
- 理解、明确任务边界
- 任务分解
- 执行人或专业丰富的人员参与
- 三点估算法
- 缓冲机制
- 沟通反馈
- 持续跟进
# 线程、进程、协程的区别
# 针对线上容易出现的一些可预见的异常问题,你应该如何进行预防
- 异常捕获与日志记录
- 熔断/限流/降级
- 幂等性设计、补偿机制(异步对账)
- 指标监控
线上异常预防的核心是 “以终为始”:从历史故障和业务特性出发,在研发全流程中植入 “防御性思维”—— 开发阶段写 “健壮代码”,测试阶段做 “破坏性验证”,上线时做 “可控灰度”,运行中做 “智能监控”。同时,通过团队流程(变更管控、复盘机制)将预防经验转化为可复用的标准,逐步构建 “主动发现风险→提前解决风险→自动化规避风险” 的闭环体系,最终实现线上故障的 “事前预防” 而非 “事后救火”。
线上异常预防的策略与方法 (opens new window)
# DRF 中 Model 和 Serializer 的关系
在 Django 中,model(数据模型)和 serialize(序列化模块)是处理数据时的两个核心组件,它们的关系可以概括为:模型是数据的存储与业务逻辑载体,序列化模块负责将模型数据转换为可传输或存储的格式。
上述讨论基于 Django 原生serializers
模块,若使用 Django REST Framework(DRF),其Serializer
类功能更强大:
- 可定义复杂的验证逻辑(如字段校验)。
- 支持反序列化时的数据创建 / 更新(如 API 接口接收 JSON 并创建模型实例)。
- 与 Model 的关系更灵活:可定义非 Model 字段(如
SerializerMethodField
),甚至不依赖 Model(如GenericSerializer
)。
Django 中 Model 和 Serialize 的关系 (opens new window)
# Django 和 Flask 的框架对比,什么时候会选择 Flask 框架?它的微体现在哪里
Django 和 Flask 框架对比及选择 (opens new window)
# 多线程与 celery 使用的场景区别,什么时候会放弃多线程选择 celery
- 任务需要「异步处理」且「不阻塞主流程」
- 任务需要「跨机器分布式处理」或「动态扩展」
- 任务需要「持久化队列」「重试机制」「状态追踪」
- 任务需要「延迟执行」或「周期性调度」