别院牧志知识库 别院牧志知识库
首页
  • 基础

    • 全栈之路
    • 😎Awesome资源
  • 进阶

    • Python 工匠系列
    • 高阶知识点
  • 指南教程

    • Socket 编程
    • 异步编程
    • PEP 系列
  • 面试

    • Python 面试题
    • 2025 面试记录
    • 2022 面试记录
    • 2021 面试记录
    • 2020 面试记录
    • 2019 面试记录
    • 数据库索引原理
  • 基金

    • 基金知识
    • 基金经理
  • 细读经典

    • 德隆-三个知道
    • 孔曼子-摊大饼理论
    • 配置者说-躺赢之路
    • 资水-建立自己的投资体系
    • 反脆弱
  • Git 参考手册
  • 提问的智慧
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
首页
  • 基础

    • 全栈之路
    • 😎Awesome资源
  • 进阶

    • Python 工匠系列
    • 高阶知识点
  • 指南教程

    • Socket 编程
    • 异步编程
    • PEP 系列
  • 面试

    • Python 面试题
    • 2025 面试记录
    • 2022 面试记录
    • 2021 面试记录
    • 2020 面试记录
    • 2019 面试记录
    • 数据库索引原理
  • 基金

    • 基金知识
    • 基金经理
  • 细读经典

    • 德隆-三个知道
    • 孔曼子-摊大饼理论
    • 配置者说-躺赢之路
    • 资水-建立自己的投资体系
    • 反脆弱
  • Git 参考手册
  • 提问的智慧
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 辨析

  • Sockets编程

  • Django

  • stackoverflow

  • Flask

  • 全栈之路

  • 面试

    • Python 面试-基础知识篇
    • 2019 面试记录
    • 2020 面试记录
    • 2021 面试记录
    • 2022 面试记录
    • 2023 面试记录
    • 2025 面试记录
      • Django 与 Flask 技术选型问题
        • Django
        • Flask
      • 项目中出现 OOM,如何进行原因分析及定位
        • 一、初步确认内存问题
        • 二、定位内存泄漏的核心步骤
        • 三、常见内存泄漏场景
        • 四、高级诊断方法
        • 五、修复与预防
        • 六、工具链总结
        • 七、案例实战
        • 总结
      • 横表变竖表表格设计问题
        • 横表(宽表)设计
        • 竖表(长表)设计
        • 设计原则与场景选择
        • 竖表设计模式
        • 推荐阅读
      • 项目中经常用到的索引优化举例
        • 1. 选择高选择性的列建立索引
        • 2. 复合索引的最左前缀原则
        • 3. 覆盖索引减少回表
        • 4. 索引下推(Index Condition Pushdown, ICP)
        • 5. 前缀索引优化长字段
        • 6. 函数索引处理计算字段
        • 7. 避免索引失效的常见陷阱
        • 8. 定期维护索引
        • 9. 删除冗余索引
        • 10. 使用部分索引(条件索引)
        • 总结
      • 项目中实际使用 Redis 和 MQ 的场景举例
        • 一、Redis 的典型应用场景
        • 二、MQ 的典型应用场景
        • 三、Redis 与 MQ 的联合使用场景
        • 四、技术选型建议
        • 五、总结
    • redis 面试题
    • RabbitMQ 面试
    • 途游面试
    • 项目产品面试
    • 如何更好地准备面试
  • 代码片段

  • 异步编程

  • 😎Awesome资源

  • PEP

  • Python工匠系列

  • 高阶知识点

  • Python 学习资源待整理
  • 设计模式

  • 好“艹蛋”的 Python 呀!
  • FIFO | 待学清单📝
  • pip 安装及使用
  • 数据分析

  • 源码阅读计划

  • OOP

  • 关于 python 中的 setup.py
  • 并行分布式框架 Celery
  • 七种武器,让你的代码提高可维护性
  • 使用 pdb 调试 Python 代码
  • 每周一个 Python 标准库
  • 🐍Python
  • 面试
佚名
2025-05-28
目录

2025 面试记录

# Django 与 Flask 技术选型问题

# Django

  • 全功能的框架,适合中大型项目开发。
  • 内置了强大的 ORM 工具,简化了数据库操作。
  • 提供了许多内置的功能模块和插件,如认证、管理后台等。
  • 学习曲线较陡峭,配置复杂,但一旦熟悉,可以提高开发效率。

# Flask

  • 简洁、灵活的框架,适合小型项目或快速原型开发。
  • 与 Django 相比,学习曲线较平缓,配置相对简单。
  • 没有内置的 ORM 工具,但可以与其他 ORM 库集成。
  • 可扩展性强,可以根据项目需求选择需要的插件和库。

# 项目中出现 OOM,如何进行原因分析及定位

在 Python 后端项目中,出现内存溢出(Out of Memory, OOM) 时,通常是由于内存泄漏或资源未正确释放导致。以下是系统化的原因分析与定位方法:


# 一、初步确认内存问题

  1. 观察现象:
    • 进程占用内存持续增长,直至被系统杀死(如Killed日志)。
    • 出现MemoryError异常或服务性能严重下降。
  2. 监控工具:
    • 系统级监控:top、htop、ps(查看进程内存占用RES或%MEM)。
    • Python 内置模块:tracemalloc(跟踪内存分配)、resource(监控资源限制)。
    import tracemalloc
    tracemalloc.start()
    # ...运行代码...
    snapshot = tracemalloc.take_snapshot()
    top_stats = snapshot.statistics('lineno')
    for stat in top_stats[:10]:
        print(stat)
    
    1
    2
    3
    4
    5
    6
    7

# 二、定位内存泄漏的核心步骤

# 1. 使用内存分析工具

  • 工具选择:

    • objgraph:可视化对象引用关系,检测循环引用。
    • memory_profiler:逐行分析内存增长。
    • pympler:统计对象内存占用。
    • guppy3(或heapy):分析堆内存对象。
  • 示例(memory_profiler):

    # 安装:pip install memory_profiler
    from memory_profiler import profile
    
    @profile
    def my_func():
        a = [1] * 100000
        b = [2] * 200000
        return a + b
    
    if __name__ == "__main__":
        my_func()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    输出结果:

    Line #    Mem usage    Increment  Occurrences  Line Contents
    ===========================================================
    4     38.5 MiB     38.5 MiB           1   @profile
    5                                         def my_func():
    6     39.3 MiB      0.8 MiB           1       a = [1] * 100000
    7     40.9 MiB      1.6 MiB           1       b = [2] * 200000
    8     43.3 MiB      2.4 MiB           1       return a + b
    
    1
    2
    3
    4
    5
    6
    7

# 2. 检测循环引用

Python 的垃圾回收(GC)能处理大部分循环引用,但某些情况(如含__del__方法的对象)仍需手动干预。

  • 使用objgraph查找循环引用:
    import objgraph
    
    # 生成引用关系图(需安装graphviz)
    objgraph.show_backrefs(objgraph.by_type('MyClass'), filename='refs.png')
    
    # 统计前N个最多实例的类型
    objgraph.show_most_common_types(limit=10)
    
    1
    2
    3
    4
    5
    6
    7

# 3. 分析大对象或缓存

  • 检查全局变量或缓存:
    • 全局字典、列表可能意外持有大对象。
    • 缓存未设置过期策略(如使用functools.lru_cache未限制maxsize)。
    # 错误的缓存使用:未限制大小
    @lru_cache(maxsize=None)
    def heavy_calculation(x):
        return x ** x
    
    1
    2
    3
    4

# 4. 检查第三方库或 C 扩展

  • C 扩展模块:如numpy、pandas处理大数据时未释放内存。
    • 显式调用.close()或del释放资源。
  • 数据库连接池未回收:如 SQLAlchemy 连接未正确关闭。

# 三、常见内存泄漏场景

# 1. 未释放文件/网络资源

# 错误:未关闭文件句柄
def read_large_file():
    f = open('huge.log', 'r')
    return f.read()

# 正确:使用上下文管理器
def read_large_file():
    with open('huge.log', 'r') as f:
        return f.read()
1
2
3
4
5
6
7
8
9

# 2. 循环引用 + 无法被 GC 回收

class Node:
    def __init__(self):
        self.parent = None

# 创建循环引用
a = Node()
b = Node()
a.parent = b
b.parent = a

# 即使del后,若对象有__del__方法,GC可能无法回收
del a, b
1
2
3
4
5
6
7
8
9
10
11
12

# 3. 线程或异步任务未终止

  • 后台线程或协程持续运行,持有对象引用。

# 四、高级诊断方法

# 1. 使用gc模块调试

import gc

# 手动触发垃圾回收
gc.collect()

# 查看无法回收的对象
gc.garbage  # 列表显示无法回收的对象
1
2
3
4
5
6
7

# 2. 生成内存快照对比

通过多次快照对比,找出内存增长点:

import tracemalloc

tracemalloc.start()
snapshot1 = tracemalloc.take_snapshot()

# 执行怀疑有泄漏的代码
run_suspected_code()

snapshot2 = tracemalloc.take_snapshot()
top_stats = snapshot2.compare_to(snapshot1, 'lineno')

for stat in top_stats[:10]:
    print(stat)
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3. 使用 Valgrind(Linux)

针对 Python 的 C 扩展内存泄漏:

valgrind --tool=memcheck --suppressions=python.supp python3 my_script.py
1

# 五、修复与预防

  1. 修复代码:
    • 使用上下文管理器(with语句)管理资源。
    • 避免全局变量长期持有大对象。
    • 对缓存设置大小限制和过期时间。
  2. 优化数据结构:
    • 使用生成器(yield)替代列表处理大数据。
    • 使用numpy/pandas时及时释放内存。
  3. 监控与报警:
    • 集成 Prometheus + Grafana 监控进程内存。
    • 使用psutil定时上报内存使用:
      import psutil
      process = psutil.Process()
      print(process.memory_info().rss)  # 获取物理内存占用
      
      1
      2
      3

# 六、工具链总结

工具 用途
tracemalloc 定位内存分配位置
memory_profiler 逐行分析内存增长
objgraph 可视化对象引用关系
gc 手动触发垃圾回收并检查不可回收对象
pympler 统计对象内存占用

# 七、案例实战

场景:一个 FastAPI 服务处理大文件上传后内存不释放。

  1. 分析步骤:
    • 使用memory_profiler发现上传接口内存激增。
    • 发现代码中将文件内容存入全局列表:
      uploaded_files = []
      
      @app.post("/upload")
      async def upload(file: UploadFile):
          content = await file.read()
          uploaded_files.append(content)  # 内存泄漏!
      
      1
      2
      3
      4
      5
      6
  2. 修复方案:
    • 移除全局列表,改为处理完立即释放。
    • 使用临时文件或流式处理。

# 总结

  • 核心思路:监控 → 定位 → 修复 → 预防。
  • 关键点:优先检查全局变量、缓存、资源释放,结合工具快速缩小范围。
  • Python 特性:虽然自动内存管理,但开发者仍需注意对象生命周期和外部资源管理。

# 横表变竖表表格设计问题

# 横表(宽表)设计

  • 特点:
  1. 列代表同一属性的不同维度(如 语文成绩, 数学成绩)。
  2. 适合固定属性的场景,查询时无需多表关联。
  • 优点:
  1. 业务描述:横表的好处是清晰可见,一目了然,数据描叙很清晰。每个字段就是一个 KPI 指标。
  2. 性能方面:横表从数据库映射到内存的速度比竖表要快很多。
  3. 代码复杂:横表不需要做行列转换,代码比较简单
  • 缺点:
  1. 扩展性差:新增科目需修改表结构(ALTER TABLE)。
  2. 冗余存储:若学生未参加某科目考试,字段值为 NULL,浪费空间。
  3. 查询复杂度高:统计多科目时需遍历多个列。
  • 示例
| 姓名   | 语文 | 数学 | 英语 |
|--------|------|------|------|
| 张三   | 90   | 85   | 95   |
| 李四   | 88   | 92   | 89   |
1
2
3
4

# 竖表(长表)设计

  • 特点:
  1. 将横表中多列拆解为多行,通过 属性-值 模式存储。
  2. 符合数据库 第三范式(3NF),减少冗余。
  • 优点:
  1. 扩展性强:新增科目只需在 subjects 表中插入一行。
  2. 无冗余:仅存储存在的成绩。
  3. 灵活查询:通过 JOIN 实现复杂分析。
  • 缺点
  1. 业务描述:竖表的数据描叙很不清晰,举例说明:学生成绩表的竖表形式,成绩这个字段,即可是数学成绩、也可是语文成绩,不像横表形式数学成绩、语文成绩各成一个字段描述 KPI 指标来得清晰明了。
  2. 性能方面:系统展现的报表大部分是横表,这意味着竖表要进行转列。这样需要额外的性能开销。尤其是当报表进行聚合计算时,性能更糟糕。这是因为竖表从数据库映射到内存比横表要慢。
  3. 代码复杂:需要做行列转换,代码量、复杂性都会增加很多。
  • 示例
| 姓名   | 科目 | 成绩 |
|--------|------|------|
| 张三   | 语文 | 90   |
| 张三   | 数学 | 85   |
| ...    | ...  | ...  |
1
2
3
4
5

# 设计原则与场景选择

  1. 何时选择竖表(长表)? 属性动态变化:如电商商品的动态属性(颜色、尺寸)。

稀疏数据:大量字段可能为 NULL。

需要灵活扩展:频繁新增属性或维度。

  1. 何时保留横表(宽表)? 属性固定不变:如身份证号、出生日期等固定字段。

高频查询且需高性能:宽表查询无需 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 = '数学';
1
2
3
4
5

# 推荐阅读

数据库设计---关于建表的时候选择横表和竖表(纵表)的一点思考 - 程序员大本营 (opens new window) 横表与竖表性能浅析 - 潇湘隐者 - 博客园 (opens new window)

# 项目中经常用到的索引优化举例

在项目中,数据库索引优化是提升查询性能的关键手段。以下通过具体场景和示例说明常见的索引优化方法:


# 1. 选择高选择性的列建立索引

场景:用户表根据手机号快速查询。
优化:

  • 手机号(mobile)唯一性高,适合作为索引字段。
CREATE INDEX idx_mobile ON users(mobile);
1

效果:使 WHERE mobile='138xxxx' 的查询从全表扫描变为索引查找。


# 2. 复合索引的最左前缀原则

场景:订单表按用户 ID 和创建时间查询最近的订单。
优化:

  • 建立复合索引 (user_id, created_at),优先高筛选性字段。
CREATE INDEX idx_user_created ON orders(user_id, created_at);
1

生效的查询:

SELECT * FROM orders WHERE user_id=1001 ORDER BY created_at DESC LIMIT 10;
1

不生效的查询:

SELECT * FROM orders WHERE created_at > '2023-01-01'; -- 未使用user_id,无法命中索引
1

# 3. 覆盖索引减少回表

场景:查询文章标题和作者,避免回表。
优化:

  • 包含所有查询字段的复合索引。
CREATE INDEX idx_title_author ON articles(title, author);
1

查询:

SELECT title, author FROM articles WHERE title LIKE 'Python%'; -- 直接通过索引返回数据
1

# 4. 索引下推(Index Condition Pushdown, ICP)

场景:MySQL 中筛选复合索引部分条件。
优化:

  • 启用 ICP 后,在索引层过滤数据,减少回表次数。
-- 索引: (age, city)
SELECT * FROM users WHERE age > 25 AND city = 'Beijing';
1
2

效果:直接在索引中过滤 city,减少访问数据行的次数。


# 5. 前缀索引优化长字段

场景:存储长文本(如地址)时减少索引大小。
优化:

  • 对 VARCHAR(255) 的 address 字段,取前 20 字符建立索引。
CREATE INDEX idx_address_prefix ON users(address(20));
1

适用场景:前 N 个字符足够区分不同值(如城市+街道缩写)。


# 6. 函数索引处理计算字段

场景:按月份统计订单,避免全表扫描。
优化:

  • 对日期字段使用函数索引(如 MySQL 8.0+)。
CREATE INDEX idx_month ON orders((DATE_FORMAT(created_at, '%Y-%m')));
1

查询:

SELECT COUNT(*) FROM orders WHERE DATE_FORMAT(created_at, '%Y-%m') = '2023-10';
1

# 7. 避免索引失效的常见陷阱

  • 隐式类型转换:字段为字符串,查询用数字导致索引失效。
    -- mobile字段为VARCHAR,但查询使用数字
    SELECT * FROM users WHERE mobile = 13800138000; -- 索引失效
    
    1
    2
  • 索引列参与运算:
    SELECT * FROM users WHERE age + 1 > 30; -- 无法使用age索引
    
    1
  • OR 条件不当使用:
    -- 索引: (a), (b)
    SELECT * FROM table WHERE a=1 OR b=2; -- 可能全表扫描,改用UNION优化
    
    1
    2

# 8. 定期维护索引

场景:频繁更新的表出现索引碎片。
优化:

  • 重建索引(如 MySQL 的 OPTIMIZE TABLE)。
OPTIMIZE TABLE orders;
1

效果:减少索引碎片,提升查询效率。


# 9. 删除冗余索引

分析工具:

  • 使用 pt-duplicate-key-checker(Percona 工具)检测重复索引。
    优化:
  • 删除重复或未被使用的索引。
-- 假设存在 (a) 和 (a,b),删除单列索引 (a)
DROP INDEX idx_a ON table;
1
2

# 10. 使用部分索引(条件索引)

场景:只索引部分数据(如有效订单)。
优化:

  • 创建条件索引(PostgreSQL 支持)。
CREATE INDEX idx_active_orders ON orders(user_id) WHERE status = 'active';
1

效果:减少索引大小,提升查询效率。


# 总结

优化方法 适用场景 示例
高选择性索引 唯一值多的列(如手机号、邮箱) 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. 任务调度 + 付费系统的协同设计

  • 场景示例:
    用户购买会员后,系统需异步执行多个任务(如发放优惠券、更新权限)。
    流程设计:

    1. Redis 缓存用户购买状态,防止重复提交;
    2. 支付成功后,发送任务消息至 MQ;
    3. 多个 Worker 消费 MQ 消息,并行执行子任务;
    4. Redis 记录任务执行进度,供前端查询。
  • 优势:

    • Redis 处理实时状态,MQ 保障任务可靠性;
    • 系统扩展性强,新增任务类型只需新增消费者。

# 四、技术选型建议

场景 Redis 适用性 MQ 适用性 典型工具
实时库存扣减 ✅ 高并发原子操作 ❌ 无需持久化 Redis + Lua
支付异步通知 ❌ 需持久化保障 ✅ 消息可靠投递 RabbitMQ/Kafka
分布式任务调度 ✅ 轻量级队列 ✅ 复杂任务依赖 Redis Stream/RocketMQ
数据最终一致性 ❌ 无事务补偿 ✅ 重试与死信队列 Kafka + 事务消息

# 五、总结

  • Redis:适合 实时性高、数据量小、容忍部分丢失 的场景(如缓存、分布式锁、轻量队列)。
  • MQ:适合 异步解耦、高可靠性、数据持久化 的场景(如支付通知、任务流水)。
  • 联合使用:通过 Redis 处理实时状态,MQ 保障异步流程,可构建高性能且可靠的系统,典型案例如 秒杀系统(Redis 扣库存 + MQ 异步下单)。
编辑 (opens new window)
#面试
上次更新: 2025-05-28, 09:03:31
2023 面试记录
redis 面试题

← 2023 面试记录 redis 面试题→

最近更新
01
提升沟通亲和力的实用策略
03-26
02
工作
07-15
03
如何选房子
06-25
更多文章>
Theme by Vdoing | Copyright © 2019-2025 IMOYAO | 别院牧志
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式