Python 数据类型详解
1️⃣ 数值类型(Number)
| 类型 | 核心特性 | 可变性 | 常用操作 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
int | 整数类型 | 不可变 | + - * / // % ** | Python 3 长整型不限大小,掌握位运算 | 航班编号、票价整数计算 |
float | 浮点数 | 不可变 | + - * / ** round() | 浮点数精度问题,decimal 高精度计算 | 航班票价、折扣计算 |
complex | 复数 | 不可变 | real, imag | 少用,多考数学理解 | 科学计算(航班优化算法可能用) |
bool | 布尔值 | 不可变 | and, or, not | Python 内部实现为 int 子类 | 条件判断、航班状态标记 |
2️⃣ 序列类型(Sequence)
| 类型 | 核心特性 | 可变性 | 常用操作 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
list | 有序、可重复 | 可变 | append, extend, insert, pop, remove, sort | 切片 lst[start:end:step],列表推导式 | 航班列表存储、票价列表 |
tuple | 有序、可重复 | 不可变 | index, count | 元组解包,节省内存 | 航班固定信息组合 (航班号, 日期) |
range | 有序、不可重复 | 不可变 | range(start, stop, step) | 延迟生成数字序列,占用内存少 | 航班循环编号生成 |
str | 字符串 | 不可变 | split, join, strip, replace, find | f-string, encode/decode | 航班号、乘客姓名 |
3️⃣ 集合类型(Set)
| 类型 | 核心特性 | 可变性 | 常用操作 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
set | 无序、唯一 | 可变 | add, remove, union, intersection, difference | 集合运算效率高,hash 特性 | 存储已售票航班号,快速去重 |
frozenset | 无序、唯一 | 不可变 | union, intersection | 可作为 dict key | 固定航班组合标识 |
4️⃣ 映射类型(Mapping)
| 类型 | 核心特性 | 可变性 | 常用操作 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
dict | key-value | 可变 | get, setdefault, pop, keys, values, items | Python 3.7+ 保持插入顺序 | 航班信息存储 {"航班号": "状态"} |
collections.OrderedDict | 有序字典 | 可变 | 保持插入顺序 | Python 3.6+ dict 已默认有序 | 有序航班列表 |
defaultdict | 带默认值字典 | 可变 | 自动初始化 key | 避免 KeyError | 航班票价分类统计 |
5️⃣ 二进制类型(Binary)
| 类型 | 核心特性 | 可变性 | 常用操作 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
bytes | 不可变字节序列 | 不可变 | b'abc', decode, split | 字符编码、网络传输 | 航班 API 数据传输 |
bytearray | 可变字节序列 | 可变 | append, extend | 网络/文件操作 | 二进制缓存处理 |
memoryview | 内存视图 | 可变 | cast | 零拷贝,节省内存 | 大规模航班数据处理 |
6️⃣ 面试加分点
-
可变 vs 不可变:理解引用、拷贝行为
-
切片与序列操作:快速操作列表和字符串
-
字典/集合底层原理:哈希表实现
-
生成器 & 迭代器:节省内存,处理大数据
-
Python 类型设计思路:适合不同数据场景的选择
| 特性 | list | tuple | dict |
|---|---|---|---|
| 是否有序 | ✅ | ✅ | ✅(3.7+) |
| 是否可变 | ✅ | ❌ | ✅ |
| 是否支持索引 | ✅ | ✅ | ❌ |
| 是否支持 key 访问 | ❌ | ❌ | ✅ |
| 是否可作为 dict key | ❌ | ✅ | ❌ |
7️⃣ 实战场景示例
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 航班编号列表 | list | 可变、有序、支持切片 |
| 航班信息存储 | dict | key-value 快速访问 |
| 售票用户集合 | set | 去重快速查找 |
| 不可修改的航班组合 | tuple / frozenset | 不可变,保证安全 |
| 大批量航班数据迭代 | generator | 节省内存,按需生成 |
| 二进制 API 数据 | bytes / bytearray | 网络传输和缓存处理 |
Python 基础
1️⃣Python 数据类型 & 内存管理
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| list 和 tuple 区别 | list 可变,tuple 不可变 | list 底层动态数组;tuple 固定大小,连续内存 | tuple 可 hash,可做 dict key,内存更小 | 不可修改 tuple 内部对象;tuple 内嵌可变对象仍可修改 | 坐标点保存为 tuple,节省百万条数据内存 |
| dict 查找复杂度 | 平均 O(1),最坏 O(n) | 哈希表实现;Py3.6+ dict 有插入顺序 | 讲解 hash 冲突解决:开放寻址或红黑树 | hash 冲突导致最坏情况 O(n) | 用户 ID 映射信息查找 |
| set 和 dict 区别 | set 只有 key,没有 value,都是哈希表 | 底层和 dict 类似 | set 运算:交集、并集、差集 | set 中元素必须可 hash,否则报错 | 去重用户列表 |
| 浅拷贝 vs 深拷贝 | 浅拷贝只复制引用,深拷贝递归复制 | copy 模块底层实现 浅拷贝: 创建新对象,其内容是原对象的引用。 深拷贝:和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。 | 对嵌套结构理解,避免共享副作用 | 浅拷贝嵌套可变对象会被修改 | 复制航班票价结构体 |
| 可变 vs 不可变对象 | 可变对象传入函数会修改,immutable 不会 | 内存引用 + 栈帧 | 对函数参数传递理解 | 使用 immutable 做 key 时需确保不可变 | 配置 dict vs tuple key 缓存结果 |
| 内存管理与 GC | 引用计数 + 循环引用垃圾回收 | PyObject 引用计数,gc 模块触发代际回收 | gc.collect() 强制回收 | 循环引用导致内存泄漏 | 大批量订单对象处理 |
Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。
1 引用计数
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。
优点:
- 简单
- 实时性
缺点:
- 维护引用计数消耗资源
- 循环引用
2 标记-清除机制
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。
3 分代技术
分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
Python默认定义了三代对象集合,索引数越大,对象存活时间越长。
举例: 当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。
2️⃣函数、闭包、装饰器
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 参数传递 | Python 传对象引用,mutable 会修改 | 栈帧存储引用,函数局部不复制对象 | 对 list/dict/int 不同表现理解 | mutable 被意外修改 | API 配置 dict 传入多个函数 |
| *args / **kwargs | *args → tuple, **kwargs → dict | Python 可变参数实现 | 调用灵活 | 拼写错误导致收集不到参数 | 通用函数包装任意参数 API |
| 闭包 | 内层函数引用外层变量形成闭包 | closure 保存外层变量 cell 对象 | 使用 nonlocal 修改外层变量 | 闭包引用循环引用造成内存问题 | 装饰器缓存内部状态 |
| 装饰器 | 函数套函数,返回新函数,@语法糖 | 闭包 + 高阶函数 | functools.wraps 保留元信息 | 多层装饰器顺序错误 | 权限校验、缓存、日志装饰器 |
| lambda | 匿名函数,单表达式 | 编译为函数对象 | 搭配 map/filter/comprehension | 单表达式限制 | 列表字段提取 |
3️⃣并发与异步
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| threading vs multiprocessing | threading 受 GIL 限制,多用于 IO,multiprocessing 真并行 | GIL 只允许一个线程执行字节码,进程有独立 GIL | 选择适用场景 | CPU 密集型用 thread 会慢 | 高并发 HTTP 请求 vs 数据计算 |
| asyncio | 单线程事件循环协程切换 | Event Loop + Task Scheduling | async/await | 忘记 await,任务不执行 | 异步爬虫、异步 API 调用 |
| queue / asyncio.Queue | 线程安全 / 协程安全 | 内部锁机制 | 生产者-消费者模式 | 阻塞与非阻塞使用混乱 | 日志异步写入 |
| futures / concurrent.futures | 封装线程池/进程池 | submit/map 任务调度 | 任务结果异步获取 | 忘记 shutdown 导致进程泄露 | 并行数据处理 |
4️⃣ 面向切面编程AOP和装饰器
这个AOP一听起来有点懵,同学面阿里的时候就被问懵了...
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
AOP 是 面向切面编程(Aspect-Oriented Programming) 的缩写。它是一种 程序设计思想,用于 将程序中的横切关注点(cross-cutting concerns)从业务逻辑中分离出来,让核心业务逻辑更清晰,同时可复用、可维护。
1️⃣ 核心概念
| 概念 | 解释 |
|---|---|
| 切面(Aspect) | 横切关注点的封装,例如日志、事务、安全校验 |
| 连接点(Join point) | 程序执行中的一个点,比如方法调用、异常抛出 |
| 通知(Advice) | 在连接点上执行的操作,可以是: - 前置通知(Before) - 后置通知(After) - 环绕通知(Around) |
| 切入点(Pointcut) | 指定哪些连接点需要被通知 |
| 目标对象(Target) | 被增强的业务对象 |
| 织入(Weaving) | 将切面应用到目标对象的过程 |
2️⃣ 作用
-
解耦:日志、事务、安全检查等不污染核心业务逻辑
-
复用:同一个切面可以应用到多个类或方法
-
统一管理:系统级功能集中管理,提高维护性
3️⃣ 典型场景
| 场景 | 示例 |
|---|---|
| 日志记录 | 在方法执行前后打印日志 |
| 权限校验 | 在方法执行前检查用户权限 |
| 事务管理 | 方法执行出错时自动回滚 |
| 缓存 | 方法执行结果自动缓存 |
4️⃣ Python 中 AOP 实现思路
Python 没有原生 AOP 框架,但可以用 装饰器、上下文管理器或 元类实现 AOP 风格:
# 前置 + 后置通知的装饰器实现
def aspect(func):
def wrapper(*args, **kwargs):
print(f"[Before] 调用 {func.__name__}")
result = func(*args, **kwargs)
print(f"[After] 调用 {func.__name__}")
return result
return wrapper
@aspect
def business_logic(x, y):
print("核心业务逻辑执行")
return x + y
business_logic(2, 3)
输出:[Before] 调用 business_logic
核心业务逻辑执行
[After] 调用 business_logic
5️⃣ 鸭子类型
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
比如在python中,有很多file-like的东西,比如StringIO,GzipFile,socket。它们有很多相同的方法,我们把它们当作文件使用。
又比如list.extend()方法中,我们并不关心它的参数是不是list,只要它是可迭代的,所以它的参数可以是list/tuple/dict/字符串/生成器等.
鸭子类型在动态语言中经常使用,非常灵活,使得python不像java那样专门去弄一大堆的设计模式。
6️⃣Python中重载
引自知乎:http://www.zhihu.com/question/20053359
http://www.zhihu.com/question/20053359
函数重载主要是为了解决两个问题。
- 可变参数类型。
- 可变参数个数。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。
7️⃣新式类和旧式类
这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html
http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.html
新式类很早在2.2就出现了,所以旧式类完全是兼容的问题,Python3里的类全部都是新式类.这里有一个MRO问题可以了解下(新式类继承是根据C3算法,旧式类是深度优先),<Python核心编程>里讲的也很多.
一个旧式类的深度优先的例子
class A():
def foo1(self):
print("A")
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print("C")
class D(B, C):
pass
d = D()
d.foo1()
# A
按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过
8️⃣ Python自省
这个也是python彪悍的特性.
自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance().
a = [1,2,3]
b = {'a':1,'b':2,'c':3}
c = True
print(type(a),type(b),type(c)) # <type 'list'> <type 'dict'> <type 'bool'>
print(isinstance(a,list)) # True
Python 并发与异步(线程 / 进程 / 协程)
1️⃣ 线程(Thread)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| Python 线程概念 | 线程是轻量级进程,共享内存空间 | 使用 threading.Thread | 多线程适合 I/O 密集型 | GIL 限制 CPU 密集型性能 | Python 爬虫、网络请求 |
| GIL(全局解释器锁) | 同一时刻只有一个线程执行 Python 字节码 | 防止对象操作冲突 | 理解 Python 多线程局限 | CPU 密集型无性能提升 | I/O 密集型任务多线程提升效率 |
| 线程同步 | Lock, RLock, Event, Condition, Semaphore | 防止数据竞争 | 理解死锁风险 | 忘记释放锁导致死锁 | 多线程修改航班缓存计数器 |
| 线程池 | concurrent.futures.ThreadPoolExecutor | 线程复用减少开销 | submit 与 map 使用 | 阻塞调用导致线程池堵塞 | 批量发送航班 API 请求 |
| daemon 线程 | 后台线程,主线程结束自动退出 | 线程标记属性 | 守护线程应用 | 忘记 join 导致数据未写完 | 后台日志写入 |
2️⃣ 进程(Process)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| Python 进程 | 独立内存空间,完全并行 | 使用 multiprocessing.Process | CPU 密集型任务优先 | 启动开销大 | 航班票价计算任务并行化 |
| 进程池 | multiprocessing.Pool | 复用进程减少创建开销 | apply/apply_async/map | 进程间共享数据需 Queue / Manager | 批量航班计算 |
| 进程间通信 | Queue, Pipe, Manager | IPC 数据交换 | 理解共享 vs 拷贝 | 不可直接传复杂对象 | 多进程统计航班票价 |
| fork vs spawn vs forkserver | 不同启动模式 | Linux fork 拷贝父进程内存 | Windows 默认 spawn | 数据复制大内存占用 | 跨平台多进程启动 |
| GIL 与进程 | GIL 不影响多进程并行 | 每个进程独立 Python 解释器 | CPU 密集型任务并行 | 共享数据需 IPC | CPU 密集型计算优化 |
3️⃣ 协程(Coroutine / async / await)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| I/O 密集型 | 使用多线程或 asyncio | 网络/文件 I/O 阻塞 | asyncio 高并发 | 阻塞操作仍会堵塞 | 爬取航班信息 |
| CPU 密集型 | 使用多进程 | 充分利用多核 CPU | multiprocessing + pool | 进程间通信开销大 | 航班票价计算任务 |
| 混合任务 | async + 线程池/进程池 | I/O 协程 + CPU 并行 | 异步+并行组合 | 混合模式复杂 | 高并发查询 + 票价计算 |
| 死锁 / 饥饿 | Lock / Condition 使用不当 | 线程 / 进程同步 | 理解锁粒度 | 阻塞任务无限等待 | 多线程缓存更新 |
4️⃣ Python 并发设计选择
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| I/O 密集型 | 使用多线程或 asyncio | 网络/文件 I/O 阻塞 | asyncio 高并发 | 阻塞操作仍会堵塞 | 爬取航班信息 |
| CPU 密集型 | 使用多进程 | 充分利用多核 CPU | multiprocessing + pool | 进程间通信开销大 | 航班票价计算任务 |
| 混合任务 | async + 线程池/进程池 | I/O 协程 + CPU 并行 | 异步+并行组合 | 混合模式复杂 | 高并发查询 + 票价计算 |
| 死锁 / 饥饿 | Lock / Condition 使用不当 | 线程 / 进程同步 | 理解锁粒度 | 阻塞任务无限等待 | 多线程缓存更新 |
5️⃣ Python 并发实战案例
| 场景 | 实现方式 | 面试加分点 | 典型坑 | 说明 |
|---|---|---|---|---|
| 批量航班 API 请求 | asyncio.gather / ThreadPoolExecutor | 高并发处理 I/O | 忘记 await 或线程阻塞 | 高并发 I/O 任务 |
| CPU 密集型票价计算 | multiprocessing.Pool | 多进程并行 | IPC 数据传输 | 多核 CPU 利用最大化 |
| 异步 + 队列 | asyncio + aiohttp + asyncio.Queue | 控制并发量 | 队列未 await 导致阻塞 | 并发请求控制 |
| 线程安全缓存更新 | threading.Lock | 防止数据竞争 | 忘记释放锁 | 更新航班缓存计数器 |
Python 高级特性
1️⃣ 闭包 (Closure)
闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:
- 必须有一个内嵌函数
- 内嵌函数必须引用外部函数中的变量
- 外部函数的返回值必须是内嵌函数
感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料.
重点是函数运行后并不会被撤销,就像16题的instance字典一样,当函数运行完后,instance并不被销毁,而是继续留在内存空间里.这个功能类似类里的类变量,只不过迁移到了函数上.
闭包就像个空心球一样,你知道外面和里面,但你不知道中间是什么样.
def count_time_wrapper(func):
def improved_func():
start_time = time.time()
func()
end_time = time.time()
print(f"it takes {end_time - start_time}s to find all the olds")
return improved_func
# 闭包本质上是一个函数
# 闭包函数的传入参数和返回值都是函数
# 闭包函数的返回值函数是对传入函数进行增强的函数
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 闭包的定义 | 内层函数引用外层函数变量,形成闭包 | Python 用 cell 对象保存外层变量 | nonlocal 修饰可修改外层变量 | 忘记 nonlocal,外层变量不可修改 | 缓存计算结果、延迟计算 |
| 闭包使用场景 | 数据封装、装饰器实现、回调 | 内层函数保持外层状态 | 减少全局变量使用 | 循环闭包共享变量问题 | 缓存航班价格、装饰器状态保持 |
| 闭包 vs lambda | lambda 是匿名函数,闭包可保存状态 | lambda 可以生成闭包 | 两者结合使用提高代码简洁性 | lambda 只适合单表达式 | map/filter 中用闭包生成函数 |
2️⃣ 装饰器 (Decorator)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 装饰器定义 | 函数作为参数,返回新函数 | 高阶函数 + 闭包 | @语法糖,functools.wraps 保留原函数信息 | 不加 wraps,元信息丢失 | 权限校验装饰器、日志装饰器 |
| 带参数装饰器 | 外层函数接收参数,返回装饰器 | 三层嵌套闭包 | 支持灵活配置 | 参数传递顺序错 | 缓存 TTL 装饰器 |
| 多层装饰器执行顺序 | 从内向外应用,调用从外向内 | 栈式闭包 | 结合 wraps 保留元信息 | 调用顺序理解错误 | 日志 + 缓存装饰器叠加 |
3️⃣ 迭代器 (Iterator) 与生成器 (Generator)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 迭代器 | 实现 __iter__() 和 __next__() 的对象 | 迭代器协议 | 支持 for 循环 | 忘记 StopIteration | 遍历大文件、分页数据 |
| 生成器 | 使用 yield 生成迭代器 | 保存状态在 frame 对象中 | 节省内存、惰性计算 | 多线程中共享生成器需加锁 | 处理百万条航班数据流 |
| 生成器表达式 vs 列表推导 | generator expression 惰性,列表推导立即计算 | 节省内存 | 大数据量处理 | 忘记 list() 转化 | 分页结果流式处理 |
| send() 与 yield | yield 可接收 send 值 | 协程初级实现 | 实现双向数据流 | 忽略 send 语义 | 协程任务调度、异步生成器 |
4️⃣ 魔法函数 (Dunder / Special Methods)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
__init__ vs __new__ | __new__ 创建对象,__init__ 初始化 | 元类 + 对象创建链 | 单例模式 | 忘记返回对象 | Redis 客户端单例模式 |
__str__ vs __repr__ | str 用户友好,repr 调试友好 | 调用顺序 | 调试与日志打印 | 覆盖不当 | 日志打印对象信息 |
| 算术运算符重载 | __add__, __sub__, __mul__ | 支持自定义对象运算 | 可用于向量、矩阵等 | 返回类型错误 | 航班票价对象加法 |
| 比较运算符 | __lt__, __eq__ 等 | 支持 sorted / min / max | 与 total_ordering 配合 | 未实现全序 | 航班排序、过滤 |
| 可调用对象 | __call__ | 对象像函数调用 | 实现策略模式 | 返回值忽略 | 配置对象直接调用计算票价 |
| 容器类型 | __getitem__, __setitem__, __len__ | 支持索引访问、切片 | 可实现自定义 dict / list | 切片返回新对象注意浅深拷贝 | 航班数据封装类 |
5️⃣ 上下文管理器 (Context Manager / with)
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
with 的作用 | 自动管理资源释放 | 调用 __enter__ 与 __exit__ | 文件、数据库连接、锁 | 忘记处理异常 | 数据库事务自动提交/回滚 |
| 自定义上下文管理器 | 实现 __enter__ 和 __exit__ | 可抛出异常控制 | 提高代码可读性与安全性 | exit 忘记返回 False 导致异常吞掉 | Redis 连接池获取/释放 |
contextlib.contextmanager | 使用生成器装饰函数 | 自动生成上下文管理器 | 简化自定义管理器 | yield 异常处理 | 数据库事务、文件处理 |
Python 高并发架构详解
1️⃣ 架构分层概览
高并发系统一般分为以下层次:
-
客户端层
-
负载均衡器(LB):Nginx、F5、LVS
-
缓存 CDN:静态资源加速,减轻服务器压力
-
-
接入层 / API 网关
-
统一入口、流量控制、鉴权、路由
-
Python 常用框架:FastAPI / Tornado / Sanic
-
限流策略:漏桶 / 令牌桶
-
-
应用层
-
Web 服务 / 微服务
-
Python 高并发框架:asyncio、uvloop、Gevent
-
多进程 + 协程混合,提高 CPU + I/O 利用率
-
-
缓存层
-
Redis / Memcached
-
缓存热点数据,避免 DB 压力
-
缓存策略:缓存穿透、击穿、雪崩防护
-
-
消息队列层
-
Kafka / RabbitMQ / RocketMQ
-
异步削峰填谷
-
消息可靠性策略:ACK / 重试 / 幂等性
-
-
数据库层
-
主从分离、读写分离
-
分库分表 + 分片
-
连接池:SQLAlchemy + asyncpg / aiomysql
-
-
存储与搜索
-
对象存储:S3 / MinIO
-
搜索引擎:Elasticsearch
-
用于快速检索和日志分析
-
-
监控与运维
-
Prometheus + Grafana
-
日志收集:ELK / Loki
-
告警策略:流量异常、延迟、错误率
-
┌────────────────────────── 用户 / 客户端 ──────────────────────────┐
│ Web / App / 小程序 │
└───────────────┬────────────────────────────────────────────────────┘
│
▼
┌────────────────────── CDN(静态 & 热查询缓存) ────────────────────┐
│ 航线列表 / 热门城市 / 常见查询 │
└───────────────┬────────────────────────────────────────────────────┘
│
▼
┌──────────────────────── API Gateway ───────────────────────────────┐
│ 鉴权 | 路由 | 限流 | 熔断 | 日志 | 灰度 │
└───────────────┬───────────────────────────┬────────────────────────┘
│ │
│ │
查询链路(读多) 下单链路(写少)
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ 查询服务(无状态) │ │ 订单服务(强一致) │
└───────────────┬───────┘ └───────────────┬───────┘
│ │
│ ▼
┌───────▼────────┐ ┌────────────────────┐
│ 本地缓存(LRU) │ │ Redis(库存锁) │
└───────┬────────┘ └────────────┬───────┘
│ │
┌───────▼────────┐ ┌────────────▼───────┐
│ Redis Cluster │ │ MySQL 主库(订单) │
│ 航班 / 价格 / 座 │ └────────────┬───────┘
└───────┬────────┘ │
│ ▼
┌───────▼────────┐ ┌────────────────────┐
│ MySQL 从库 │ │ MQ(Kafka/RMQ) │
│ 航班 / 规则 │ └────────────┬───────┘
└────────────────┘ │
▼
┌────────────────────┐
│ 航司 / 供应商接口 │
└────────────────────┘
Q1:为什么查询和下单要拆?
查询是读多写少,适合缓存;下单要求强一致,必须独立。
Q2:Redis 挂了怎么办?
本地缓存兜底 + 降级返回旧数据 + 限流。
Q3:如何保证不超卖?
Redis 原子锁库存 + 订单状态机 + 超时回滚。
每一层的设计思路
① CDN 层 —— 抗第一波洪峰
解决什么问题?
-
重复查询
-
地域延迟
-
防刷
设计点
-
缓存航线列表、热门查询
-
只缓存 GET
-
TTL 秒级~分钟级
原则
能在 CDN 解决的,坚决不进系统
② API Gateway —— 系统入口控制器
职责
-
统一入口
-
路由
-
限流
-
鉴权
-
熔断
设计点
-
IP / 用户 / 接口多维限流
-
查询接口优先级高于下单
-
熔断后返回缓存数据
③ 查询服务(读多)—— 扛流量的核心
特点
-
无状态
-
可水平扩展
-
不持久化
查询链路
本地缓存 → Redis → DB 从库
关键设计
-
本地缓存:抗 Redis
-
Redis:抗 DB
-
DB 只兜底
必须解决的 3 个问题
| 问题 | 对策 |
|---|---|
| 穿透 | Bloom Filter |
| 击穿 | 热 key 永不过期 |
| 雪崩 | TTL 加随机 |
④ 下单服务(写少)—— 系统最敏感部分
特点
-
独立部署
-
强一致
-
严格限流
核心原则
库存只认一个地方:Redis
下单流程
请求
↓
Redis 原子扣库存
↓
写订单
↓
异步确认
关键点
-
Lua 原子性
-
锁粒度:航班 + 舱位
-
超时回滚库存
⑤ 消息队列 —— 削峰 & 解耦
解决什么?
-
航司接口慢
-
支付回调不稳定
使用场景
-
下单后通知航司
-
支付完成
-
库存回滚
设计原则
-
消息可重试
-
幂等处理
⑥ 数据层 —— 支撑高并发
数据库设计
-
订单主库(写)
-
查询从库(读)
分表策略
-
按日期
-
按航线
索引优先级
-
查询条件
-
状态字段
⑦ 外部航司 —— 最不可靠的一环
对策
-
超时控制
-
重试
-
熔断
-
缓存结果
2️⃣ 核心技术点与原理
| 技术点 | 原理解析 | 面试加分点 | 优化技巧 | 实战案例 |
|---|---|---|---|---|
| 负载均衡 | LVS/HAProxy/Nginx 轮询、最少连接、IP hash | 会讲三种策略和适用场景 | keepalive 连接、健康检查 | 多机房航班查询 API |
| 异步框架 | asyncio + uvloop,事件循环 + 协程 | 高并发 I/O 设计 | 限制协程数量,避免阻塞 | 并发航班票价请求 |
| 多进程 + 协程混合 | CPU 密集型多进程,I/O 协程 | CPU + I/O 最大化 | 进程池 + 协程池 | 航班票价计算 + 查询 |
| 缓存策略 | Redis,热点缓存 | 防穿透、防击穿、防雪崩
| 布隆过滤器、互斥锁、随机过期 | 航班票价查询缓存 |
| 消息队列 | 异步削峰填谷 | 消息持久化、分区、幂等 | 异步任务、延迟队列 | 航班库存异步扣减 |
| 数据库优化 | 分库分表 + 主从读写分离 | SQL 优化 + 索引 | 使用连接池、缓存热点数据 | 查询航班库存信息 |
| 限流与熔断 | 漏桶/令牌桶/Hystrix | 防止系统崩溃 | 热点接口单独限流 | 高并发抢票系统 |
| 分布式锁 | Redis / Zookeeper / etcd | 保证原子操作 | Lua 脚本实现原子扣减 | 抢票系统库存控制 |
| 监控与告警 | Prometheus + Grafana | 指标选择:QPS、RT、错误率 | 动态扩容触发 | 系统延迟高告警 |
幂等(Idempotent):对同一个操作,无论执行一次还是执行多次,结果都是一样的,不会因为重复执行而产生副作用。
-
简单理解:多次执行等于一次执行
-
数学上也有幂等概念:
f(f(x)) = f(x)
| 场景 | 是否幂等 | 说明 |
|---|---|---|
| HTTP GET /api/user/123 | ✅ 幂等 | 多次请求都返回同一用户信息,不会修改数据 |
| HTTP POST /api/order | ❌ 非幂等 | 每次请求可能生成新的订单 |
| HTTP PUT /api/order/123 | ✅ 幂等 | 无论执行多少次,订单状态被更新为相同的值 |
| Redis SET key value | ✅ 幂等 | 不管执行多少次,key 的值最终是 value |
| Redis INCR key | ❌ 非幂等 | 每次执行都会加 1,结果不同 |
1️⃣ 负载均衡策略详解
| 策略 | 原理 | 面试加分点 | 典型应用场景 | 实战案例 |
|---|---|---|---|---|
| 轮询 (Round Robin) | 每个请求按顺序分配到后端服务器,简单公平 | 适合请求处理能力相近的服务器,简单高效 | 小型 Web 服务、API 服务,服务器性能差异不大 | Python 航班查询 API,每个实例处理能力相同,平均分发请求 |
| 最少连接 (Least Connections) | 将新请求分配给当前连接数最少的服务器 | 适合请求处理时间长、服务器性能不同的场景 | 文件上传、长连接 API、WebSocket | Python 文件上传服务,长请求分发给负载较轻的实例 |
| 源地址哈希 (IP Hash) | 根据客户端 IP 哈希计算分配到固定后端服务器 | 保证同一客户端请求分配到同一台服务器(会话保持) | 会话依赖场景(未使用 Redis Session) | 航班预订系统,用户操作需要保持会话状态,避免跨实例丢失缓存 |
3️⃣ 高并发设计模式
-
缓存 + 异步 + 队列
-
查询先 Redis,再 DB
-
写操作异步 MQ 处理
-
减少同步阻塞
-
-
读写分离
-
读多写少场景:主库写、从库读
-
Python ORM 支持自动路由
-
-
分库分表
-
水平拆分大表
-
避免单表瓶颈
-
-
限流 / 限速 / 熔断
-
防止瞬间洪峰压垮系统
-
实时统计 + 拒绝策略
-
-
幂等设计
-
消息队列 + Redis 幂等 key
-
防止重复扣减库存
-
-
异步处理 / 延迟队列
-
高并发下异步操作
-
延迟任务异步执行,削峰填谷
-
4️⃣ Python 实战案例
| 场景 | 架构方案 | 技术点 | 说明 |
|---|---|---|---|
| 航班票价查询高并发 | Nginx + FastAPI + asyncio + Redis | 协程 + 热点缓存 | 高并发 I/O 处理,查询秒级响应 |
| 抢票系统库存扣减 | FastAPI + Redis Lua + MQ | 原子扣减 + 异步队列 | 避免超卖,削峰填谷 |
| 航班更新推送 | Kafka + asyncio consumers | 异步消费 | 批量推送航班变动通知 |
| 日志分析 | Python + Logstash + Elasticsearch | 异步写日志 | 实时监控航班访问日志 |
| 弹性扩容 | Kubernetes + HPA | 自动水平扩容 | 高并发流量下自动伸缩 |
Python 常见算法详解
1️⃣ 排序算法
| 算法 | 核心思想 | 时间复杂度 | 空间复杂度 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
| 冒泡排序 | 相邻元素两两比较交换 | O(n²) | O(1) | 稳定排序,优化为提前退出 | 小规模航班价格列表排序 |
| 选择排序 | 每次选择最小元素放前面 | O(n²) | O(1) | 不稳定排序 | 小规模航班号排序 |
| 插入排序 | 将元素插入已排序部分 | O(n²) | O(1) | 稳定排序,适合近乎有序数组 | 航班按时间顺序排序 |
| 快速排序 | 分治法,基准划分左右 | O(n log n) 平均 | O(log n) | 不稳定,递归实现 | 航班票价排序 |
| 归并排序 | 分治法,递归合并 | O(n log n) | O(n) | 稳定排序 | 航班按时间 + 票价多关键排序 |
| 堆排序 | 利用堆选择最大/最小 | O(n log n) | O(1) | 不稳定,适合大数据 | 航班票价 top-k |
2️⃣ 查找算法
| 算法 | 核心思想 | 时间复杂度 | 空间复杂度 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
| 线性查找 | 遍历元素逐个匹配 | O(n) | O(1) | 简单易实现 | 航班列表简单查找 |
| 二分查找 | 有序数组对半查找 | O(log n) | O(1) | 注意边界条件 | 航班按时间查询最早航班 |
| 哈希查找 | 利用 dict / set 哈希表 | O(1) 平均 | O(n) | 哈希冲突处理 | 快速查找航班号是否售出 |
3️⃣ 字符串与数组算法
| 算法 | 核心思想 | 时间复杂度 | 空间复杂度 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
| 滑动窗口 | 动态维护窗口 | O(n) | O(1~k) | 处理子串/子数组问题 | 航班连续优惠查询 |
| 双指针 | 两指针从两端或同向 | O(n) | O(1) | 优化数组/链表问题 | 航班列表去重、区间查询 |
| KMP / BM | 字符串模式匹配 | O(n + m) | O(m) | 高级字符串匹配算法 | 航班号匹配 / 文本搜索 |
4️⃣ 树与图算法
| 算法 | 核心思想 | 时间复杂度 | 空间复杂度 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
| 二叉树遍历 | 递归/迭代前中后序 | O(n) | O(h) | DFS/BFS 理解 | 航班航线树结构遍历 |
| BFS / DFS | 图遍历 | O(V+E) | O(V) | 最短路径/连通分量 | 航班航线路径搜索 |
| Dijkstra | 单源最短路径 | O(E log V) | O(V) | 堆优化 | 航班最短时间路径 |
| Floyd / Bellman-Ford | 多源最短路径 | O(V³) / O(VE) | O(V²) | 可处理负权 | 航班时间优化 |
5️⃣ 贪心与动态规划
| 算法 | 核心思想 | 时间复杂度 | 空间复杂度 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
| 贪心 | 每步局部最优 | O(n log n) | O(1~n) | 证明贪心最优性 | 航班机位最优分配 |
| 动态规划 | 子问题最优解组合 | O(n²~n³) | O(n²) | 状态转移方程 | 航班票价组合最大收益 |
| 背包问题 | 动态规划经典 | O(nW) | O(nW) | 完整推导 | 航班优惠券最大化使用 |
6️⃣ 堆与优先队列
| 算法 | 核心思想 | 时间复杂度 | 空间复杂度 | 面试加分点 | 实战场景 |
|---|---|---|---|---|---|
| 堆排序 | 大根堆/小根堆 | O(n log n) | O(1) | heapq 模块 | 航班票价 top-k |
| 优先队列 | 插入 O(log n),取最小 O(1) | O(log n) | O(n) | 异步任务调度 | 航班延误优先处理 |
数据库相关面试题
1️⃣ 数据库基础
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| SQL vs NoSQL | SQL: 关系型,结构化;NoSQL: 非关系型,灵活 | SQL: 表、行、列、ACID;NoSQL: KV、文档、列族、图 | 分布式事务 vs 弱一致性 | 错选数据库导致性能瓶颈 | 航班信息使用 MySQL,缓存票价用 Redis |
| 主从复制 | 主库写,从库读 | binlog + replication thread | 异地备份,提高读吞吐 | 主从延迟导致脏读 | 查询航班库存读从库 |
| 事务特性 ACID | 原子性、一致性、隔离性、持久性 | InnoDB 事务实现机制 | 面试可结合隔离级别 | 不懂隔离级别导致脏读、幻读 | 下单事务处理 |
| SQL 注入防护 | 参数化查询 / ORM | ORM 底层生成预编译 SQL | 提到 Python DB API 规范 | 拼接字符串 SQL 易被注入 | Django ORM 或 SQLAlchemy |
| 索引类型 | B-tree、Hash、Full-text、Bitmap | B-tree 平衡树,高效范围查询 | 讲主键、唯一索引、复合索引 | 滥用索引反而降低写入性能 | 航班号查询加主键索引 |
2️⃣ 高级查询与优化
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 联表查询优化 | 使用 join 而不是子查询 | MySQL 执行计划 | 使用 explain 分析 | select * 导致全表扫描 | 航班 + 舱位表查询 |
| 分页查询优化 | limit offset → 大数据量慢,推荐 keyset 分页 | 索引扫描 vs 全表扫描 | 深入 keyset 分页 | 大表 offset 导致全表扫描 | 票务列表翻页 |
| 索引覆盖 | 索引字段包含查询字段,可直接查索引 | 减少回表 | explain 显示 index-only | 字段不在索引导致回表 | 查询热门航班统计 |
| 查询缓存 | Redis / Memcached | 减少数据库压力 | 热点航班缓存 | 数据不一致 | 查询航班价格缓存 30 秒 |
| 批量写入 | insert ... values (...),(...),... 或 executemany | 减少网络开销 | 提高写入吞吐 | 一条一条 insert 慢 | 航班批量导入 |
3️⃣ 数据库事务与并发
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 事务隔离级别 | READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE | 锁粒度:行锁、表锁 | InnoDB 默认 REPEATABLE READ | 不同隔离级别导致脏读、幻读、不可重复读 | 下单扣库存 |
| 乐观锁 vs 悲观锁 | 乐观锁:版本号/时间戳;悲观锁:select ... for update | 并发冲突处理 | 讲解适用场景 | 乐观锁重试次数不足 | 多用户同时抢票 |
| 行级锁 vs 表锁 | 行锁粒度小,吞吐高;表锁阻塞 | InnoDB 锁机制 | 锁等待和死锁 | 死锁未处理 | 航班库存锁定 |
| 并发控制 | select for update + Redis 原子操作 | 跨数据库事务 | 分布式锁实现 | 锁粒度太粗 | 机票预定下单 |
| 数据库死锁处理 | 捕获异常重试 | InnoDB 自动检测死锁 | 设计重试策略 | 死锁未处理导致下单失败 | 高并发抢票场景 |
4️⃣ 分布式与高可用
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 分库分表 | 按航班/日期/用户划分 | 水平切分,减少单表压力 | 读写分离设计 | 跨库 join 慢 | 大规模航班表 |
| 分布式事务 | TCC / 2PC / Saga | 保证多库操作一致 | 异步补偿策略 | 复杂度高 | 下单 + 支付 + 航司接口 |
| 高可用 HA | 主从切换、双活 | keepalived + heartbeat | 主从切换自动化 | 数据延迟导致脏读 | 机票查询服务高可用 |
| 缓存与数据库一致性 | 缓存穿透 / 击穿 / 雪崩 | Redis + Bloom filter + TTL | 缓存更新策略 | 数据不一致 | 航班票价缓存 |
| 数据库性能监控 | slow query log、explain | 找瓶颈 | 索引、查询优化 | 不监控导致慢查询累积 | 高频查询航班表优化 |
5️⃣ ORM 与 Python 操作数据库
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| ORM 优点 | 抽象 SQL,方便 CRUD | 内部生成 SQL | 避免 SQL 注入 | 不合理 ORM 导致 N+1 查询 | Django ORM 查询航班 |
| ORM 缺点 | 性能差,复杂 join 慢 | 生成 SQL 不总最优 | 结合 raw SQL | N+1 问题 | 批量导入航班 |
| 事务操作 | with session.begin(): | 自动 commit/rollback | 自动管理事务 | session 未关闭导致连接泄漏 | 下单操作 |
| 批量操作 | session.bulk_insert_mappings | 减少循环 insert | 提高性能 | ORM 循环 insert 慢 | 航班批量导入 |
6️⃣ 面试常问数据库综合题
-
如何设计高并发机票库存表?
→ 分库分表 + Redis 原子扣库存 + 事务 + MQ 异步通知 -
机票查询 QPS 万级,如何保证性能?
→ CDN + 本地缓存 + Redis + 从库 + 限流 -
下单超卖问题如何避免?
→ Redis 原子操作 + 乐观锁/悲观锁 + 事务 -
高并发支付失败如何保证一致性?
→ 分布式事务/Saga + 异步补偿 + MQ -
数据库迁移或升级如何不影响业务?
→ 双写策略 + 灰度切换 + 回滚方案
Redis 高级面试题
1️⃣ Redis 基础与数据类型
Redis数据库
通常局限点来说,Redis也以消息队列的形式存在,作为内嵌的List存在,满足实时的高并发需求。在使用缓存的时候,redis比memcached具有更多的优势,并且支持更多的数据类型,把redis当作一个中间存储系统,用来处理高并发的数据库操作
- 速度快:使用标准C写,所有数据都在内存中完成,读写速度分别达到10万/20万
- 持久化:对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上,主要有两种策略,一是根据时间,更新次数的快照(save 300 10 )二是基于语句追加方式(Append-only file,aof)
- 自动操作:对不同数据类型的操作都是自动的,很安全
- 快速的主--从复制,官方提供了一个数据,Slave在21秒即完成了对Amazon网站10G key set的复制。
- Sharding技术: 很容易将数据分布到多个Redis实例中,数据库的扩展是个永恒的话题,在关系型数据库中,主要是以添加硬件、以分区为主要技术形式的纵向扩展解决了很多的应用场景,但随着web2.0、移动互联网、云计算等应用的兴起,这种扩展模式已经不太适合了,所以近年来,像采用主从配置、数据库复制形式的,Sharding这种技术把负载分布到多个特理节点上去的横向扩展方式用处越来越多。
Redis缺点
- 是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| Redis 数据类型 | string, list, set, sorted set, hash, bitmap, hyperloglog, stream | 内存存储,基于字典 + skip list | 了解每种类型适用场景 | 错用数据类型导致性能下降 | 航班票价缓存用 string,排行榜用 sorted set |
| String 操作 | set/get/incr/decr | 字符串 + 原子操作 | incr/decr 可实现计数器 | 超长字符串导致内存爆炸 | 票价访问计数 |
| List 操作 | lpush/rpush/lpop/rpop/lrange | 链表实现 | 队列/栈应用 | lrange 全表扫描 | 异步任务队列 |
| Hash 操作 | hset/hget/hgetall | hash table | 节省内存,适合存对象 | hgetall 大 hash 内存占用大 | 航班信息对象存储 |
| Set 操作 | sadd/srem/sismember/sinter/sunion | hash table | 去重、交集/并集 | 大量交集操作慢 | 用户已选航班去重 |
| Sorted Set | zadd/zrange/zscore | skip list + hash table | 排行榜、优先级队列 | 大量 score 频繁更新慢 | 航班热度排行榜 |
2️⃣ Redis 高级特性
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| TTL / Key 过期 | expire/setex | 内部惰性删除 + 定期删除 | 缓存自动失效 | 大量过期键可能触发删除慢 | 航班票价缓存 30s |
| Redis 持久化 | RDB / AOF / 混合 | RDB 快照,AOF 追加日志 | 数据安全 vs 性能权衡 | AOF 文件过大 | 机票历史价格持久化 |
| 发布/订阅 | pub/sub | 消息发送到 channel | 简单消息通知 | 不持久化,订阅者掉线消息丢失 | 航班变动通知 |
| 事务 | MULTI/EXEC/DISCARD/WATCH | 命令队列 + 原子执行 | watch 实现乐观锁 | 事务内命令失败不会回滚 | 扣库存 + 写日志原子操作 |
| Lua 脚本 | EVAL | 原子执行 + 避免网络延迟 | 跨命令原子操作 | 脚本错误导致事务失败 | 扣库存 + 记录订单原子执行 |
3️⃣ Redis 高并发与缓存策略
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 缓存穿透 | 使用布隆过滤器 | 请求不存在直接过滤 | 减少 DB 压力 | 未用布隆可能大量空查询 | 查询不存在航班缓存 |
| 缓存击穿 | 加锁 / 单点缓存 | 热点 key 并发访问 | 互斥锁 + 防止 DB 压力暴涨 | 锁粒度太粗 | 热门航班票价查询 |
| 缓存雪崩 | key 同时过期 | 分散 TTL 或互斥锁 | 避免大量 key 同时失效 | TTL 全一致 | 航班票价批量刷新 |
| 高并发扣库存 | Lua 原子脚本 | 避免 race condition | 原子操作,性能高 | 不用 Lua 可能超卖 | 抢票系统 |
| 分布式锁 | setnx + expire / RedLock | 单点锁 / 多节点锁 | 防止超卖 | 死锁、锁失效 | 订单扣库存 + 支付 |
4️⃣ Redis 集群与高可用
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 主从复制 | 主库写,从库读 | 异步复制,延迟 | 提高读吞吐量 | 从库延迟导致脏读 | 航班查询读从库 |
| 哨兵模式 | sentinel 监控 + 自动 failover | 自动主从切换 | 高可用 | sentinel 异常导致切换不及时 | Redis 高可用部署 |
| Redis Cluster | slot 分片 + 多节点 | hash slot + 迁移 | 水平扩展 | hash slot 不均衡导致热点 | 航班大表分片 |
| 分布式锁 RedLock | 多节点 setnx + expire | 保证跨节点锁 | 防止单点失效 | 节点网络延迟可能导致误解锁 | 高并发库存扣减 |
5️⃣ Python 与 Redis 实战
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| Python 操作 Redis | redis-py 库:StrictRedis/Redis | 连接池 + 命令封装 | connection pool 提高性能 | 不关闭连接导致泄漏 | 航班票价缓存 |
| 缓存 + DB 同步 | Cache Aside / 读写穿透 | 先读缓存,没命中查 DB 更新缓存 | 缓存策略优化 | 多线程同时更新缓存可能超卖 | 查询航班票价 + 更新缓存 |
| 分布式锁 | Python + Redis Lua 脚本 | 原子操作,避免 race | 设置合理 TTL | 锁没释放,死锁 | 下单扣库存 |
| 批量操作 | pipeline | 减少网络开销 | 提高吞吐量 | 忘记 execute | 批量更新票价 |
Docker 高级面试题
1️⃣ Docker 基础概念
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| Docker 与虚拟机区别 | Docker 轻量级容器,共享宿主内核;VM 独立内核,资源开销大 | 容器利用 Linux Namespace + Cgroups 实现隔离 | 讲解 Namespace (PID, NET, MNT, IPC) | 容器误以为完全隔离内核 | Python 服务部署,节省资源 |
| Docker 镜像与容器区别 | 镜像是静态只读模板,容器是镜像运行时实例 | copy-on-write 文件系统 | 镜像版本管理 | 容器误删导致数据丢失 | 航班票价 API 服务镜像化 |
| Dockerfile 基础指令 | FROM, RUN, COPY, ADD, CMD, ENTRYPOINT, ENV | 层级构建,每层缓存 | 多阶段构建优化镜像大小 | CMD 与 ENTRYPOINT 混用错误 | Python 微服务镜像构建 |
2️⃣ Docker 镜像与构建优化
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 镜像分层机制 | 每条 Dockerfile 指令形成一层,分层缓存 | copy-on-write | 利用缓存加速构建 | 每条 RUN 指令增加层 | Python 应用多阶段构建,减少 50% 镜像大小 |
| 镜像体积优化 | 使用轻量基础镜像、合并 RUN 指令、多阶段构建 | 每层文件系统叠加 | Alpine / slim 镜像 | RUN apt-get install 多行导致层增大 | Python Flask 服务 |
| Docker Compose | 多容器编排,定义网络和依赖 | Compose 文件解析,生成容器网络 | 服务依赖顺序,环境变量配置 | 端口冲突、依赖未启动 | MySQL + Redis + Python API 组合部署 |
3️⃣ Docker 容器运行与管理
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 容器启动方式 | docker run -d -p, CMD / ENTRYPOINT | Namespace 隔离进程,Cgroups 限制资源 | 使用 --restart 保持高可用 | 忘记挂载 volume 导致数据丢失 | Python API 容器化运行 |
| 容器网络模式 | bridge, host, overlay, none | NAT + iptables | 理解端口映射、跨主机通信 | host 模式安全风险 | 微服务之间通信 |
| 数据卷 (volume) | 持久化数据,独立于容器生命周期 | bind mount / volume | 避免数据丢失 | 容器删除数据丢失 | MySQL 数据持久化 |
4️⃣ Docker 高级特性
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 镜像缓存与加速 | 利用层级缓存,加速构建 | 分层存储 | registry 镜像加速 | 每次修改文件导致整个层重新构建 | Python 服务 CI/CD |
| 健康检查 | HEALTHCHECK 指令 | 容器自我状态检测 | 自动重启 unhealthy 容器 | 健康检查脚本返回值错误 | Python API 健康探针 |
| 多阶段构建 | 使用多个 FROM,减少最终镜像 | 构建阶段与运行阶段分离 | 减少依赖和镜像体积 | 忘记复制二进制到最终镜像 | Python 打包部署 |
5️⃣ Docker 与高可用 / DevOps
| 题目 | 核心答案 | 底层原理 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| 容器编排 | Docker Compose / Kubernetes | 多容器调度 | 了解 k8s pod、service、deployment | 端口冲突、依赖未就绪 | Python 微服务部署 |
| CI/CD 与 Docker | 自动构建镜像 + 发布容器 | Dockerfile + Registry + Runner | 镜像版本化,流水线 | 镜像未打 tag,发布混乱 | GitLab CI/CD + Python API |
| 日志管理 | docker logs / volume 持久化 | stdout/stderr 重定向 | 集中化日志收集 | 容器日志丢失 | Python API 统一日志到 ELK |
6️⃣ Python 与 Docker 实战
| 题目 | 核心答案 | 原理解析 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
| Python 应用容器化 | Dockerfile + virtualenv 或 venv | 每层独立运行环境 | 避免依赖冲突 | 容器内未安装依赖 | Flask/FastAPI 服务容器化 |
| Python 与 Redis/MySQL 容器化 | 多容器网络 | 使用 docker-compose | 配置环境变量 | 容器未链接网络 | Python API + Redis + MySQL |
| 体积优化 | 多阶段构建 + slim/alpine | 减少镜像层 | 构建缓存 | Python 编译依赖未清理 | Python 数据处理服务 |
Git面试题
1️⃣Git 常用命令
| 命令 | 核心作用 | 原理/补充说明 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
git init | 初始化仓库 | 创建 .git 目录,建立对象数据库 | 讲解 Git 数据结构 | 初始化目录错误 | 新项目 Python 服务 |
git clone <repo> | 克隆远程仓库 | 复制对象数据库 + HEAD | 指定分支 clone:-b | 未指定 branch 拉取默认分支 | 拉取团队仓库 |
git status | 查看工作区/暂存区状态 | 对比 HEAD、索引、工作区 | 快速检查修改 | 忽略 .gitignore 文件 | 开发前检查 |
git add <file> | 暂存修改 | 更新 index | 区分工作区和暂存区 | 忘记 add,commit 无效 | 提交航班模块修改 |
git commit -m "msg" | 提交到本地版本库 | 生成 commit 对象 | message 规范化 | message 不明确 | 保存功能更新 |
git log | 查看提交历史 | DAG + SHA-1 | --oneline, --graph 展示 | 历史太多未分页 | 查看 bug 提交历史 |
git diff | 对比修改内容 | 工作区 vs 暂存区 | 查看变更细节 | 忘记 add 影响 diff | 查看航班逻辑修改差异 |
git branch | 查看/创建分支 | 分支是指针 | git branch -a 显示远程 | branch 名混乱 | 创建 feature 分支 |
git checkout <branch> | 切换分支 | 更新 HEAD | -b 创建并切换 | 切换前未 stash 导致修改丢失 | 切换开发/测试分支 |
git merge <branch> | 合并分支 | DAG 合并 | fast-forward vs 3-way merge | 冲突未解决 | feature 合并到 master |
git rebase <branch> | 变基 | 线性化 commit 历史 | 理解冲突解决 | 历史被修改 | 整理 feature 分支历史 |
git pull | 拉取远程更新并合并 | fetch + merge | 避免直接 push 冲突 | 未 stash 导致冲突 | 同步团队更新 |
git fetch | 拉取远程更新,不合并 | 更新远程引用 | 可以单独检查 | 不合并误以为本地更新 | 先 fetch 再 rebase |
git push | 推送到远程仓库 | 更新远程分支 | --force 谨慎使用 | 强制 push 可能覆盖别人提交 | 发布 Python 功能模块 |
git reset | 回退 commit 或暂存区 | reset --soft/mixed/hard | 区分回退范围 | hard 可能丢失修改 | 回退误提交 commit |
git revert | 新增 commit 撤销历史 | DAG 操作 | 安全撤销 | 忽略 revert 会累积 | 撤销线上 bug commit |
git stash | 临时保存修改 | stack 保存快照 | 多任务切换 | 忘记 pop 导致修改丢失 | 切换分支前 stash 当前修改 |
git tag | 标签版本 | 指向 commit | 发布版本管理 | 忘记推送远程 | v1.0 发布航班服务 |
git remote | 查看/管理远程仓库 | 保存远程引用 | origin/add/remove | URL 配置错误 | 多仓库协作 |
git cherry-pick <commit> | 选择单个 commit 应用 | 复制 commit 对象 | 修复 bug | 忽略依赖 commit | 热修复 |
git reflog | 查看 HEAD 历史操作 | 本地操作日志 | 恢复误操作 | 忘记 reflog | 恢复误删分支 |
git clean -f | 清理未追踪文件 | 删除工作区未追踪文件 | - | 删除重要文件 | 清理临时生成文件 |
git blame <file> | 查看每行修改记录 | 注释每行 commit | 定位责任人 | large file 慢 | 查找航班功能修改责任人 |
git diff --staged | 查看已暂存修改 | 对比 index 与 HEAD | - | 混淆未暂存修改 | 检查 commit 内容 |
git log --graph --oneline --all | 图形化提交历史 | DAG 可视化 | 面试加分 | log 太长未分页 | 分支合并查看历史 |
2️⃣Git 高级常用操作命令
| 命令 | 核心作用 | 原理/补充说明 | 面试加分点 | 典型坑 | 实战案例 |
|---|---|---|---|---|---|
git remote -v | 查看远程仓库列表和 URL | 显示 fetch/push 地址 | 理解远程仓库管理 | 混淆 origin 与 upstream | 查看项目仓库源 |
git remote add <name> <url> | 添加远程仓库 | 创建远程引用 | 多远程仓库协作 | URL 错误导致 push/pull 失败 | Python 服务关联多个仓库 |
git remote set-url <name> <newurl> | 修改远程仓库 URL | 更新远程引用 | GitHub/GitLab 切换仓库 | 忘记更新 origin | 项目迁移到新仓库 |
git remote remove <name> | 删除远程仓库 | 删除远程引用 | 清理无用远程 | 删除错误远程 | 清理废弃仓库 |
git clone <repo> [<dir>] | 克隆仓库,可指定目录 | copy 对象 + checkout | 指定分支 clone: -b | 克隆大仓库慢 | 拉取航班 API 仓库到指定目录 |
git checkout <branch> | 切换本地分支 | HEAD 指针切换 | -b 创建新分支 | 未保存修改导致丢失 | 切换 feature 分支开发 |
git checkout -b <branch> | 创建并切换分支 | 指针新建 + HEAD 切换 | 快速创建分支 | 同名分支报错 | 新功能开发 |
git branch -a | 查看本地 + 远程分支 | 显示所有分支引用 | - | 不清楚远程分支 | 查看团队分支结构 |
git branch -r | 查看远程分支 | 显示 origin/* | 分支同步管理 | 误以为本地存在 | 拉取远程 hotfix 分支 |
git fetch <remote> | 拉取远程更新,不合并 | 更新远程引用 | 先 fetch 再 rebase | 忘记 merge/pull | 拉取最新远程分支 |
git pull <remote> <branch> | 拉取并合并远程分支 | fetch + merge | 避免直接 push 冲突 | 未 stash 导致冲突 | 同步远程 master 分支 |
git push <remote> <branch> | 推送本地分支到远程 | 更新远程引用 | push 新分支时加 -u | push 被拒绝 | 发布 feature 分支 |
git push -u origin <branch> | 推送并设置上游分支 | 建立跟踪关系 | 下次可直接 git push | 忘记设置 -u 需指定远程 | 新创建分支推送远程 |
git fetch --all | 拉取所有远程更新 | 更新所有远程分支 | 大型项目多远程仓库 | 未检查本地分支 | 拉取团队所有更新 |
git branch -d <branch> | 删除本地分支 | 指针删除 | 确保合并到主分支 | 删除未 merge 分支报错 | 清理完成的 feature 分支 |
git push <remote> --delete <branch> | 删除远程分支 | 删除远程引用 | 清理废弃分支 | 删除错误分支 | 删除已合并 feature 分支 |
git config --global user.name/email | 配置用户名/邮箱 | 保存配置文件 | 面试可展示规范操作 | 忘记配置导致 commit 不规范 | 新环境配置 |
git init | 初始化仓库 | 创建 .git 目录 | 从零开始 | 误操作目录 | 新 Python 项目版本管理 |
git log --oneline --graph --all | 查看所有分支图形化历史 | DAG 可视化 | 快速定位 commit | 历史太长未分页 | 多分支合并查看历史 |
Python 编程题整理
1️⃣ 台阶问题 / 斐波那契
题目:
一只青蛙一次可以跳 1 级或 2 级台阶,求跳上 n 级台阶的总跳法。
思路:
-
递归:
f(n) = f(n-1) + f(n-2) -
记忆化递归:缓存中间结果
-
循环迭代:动态规划
实现:
# 递归
fib = lambda n: n if n <= 2 else fib(n-1) + fib(n-2)
# 记忆化递归
def memo(func):
cache = {}
def wrap(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrap
@memo
def fib(n):
if n < 2:
return 1
return fib(n-1) + fib(n-2)
# 循环迭代
def fib(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return b
面试加分点:
-
对比递归、记忆化、迭代时间复杂度
-
斐波那契公式 / 动态规划优化
2️⃣ 变态台阶问题
题目:
青蛙一次可以跳 1~n 级台阶,求总跳法。
思路:
-
动态规划:
f(n) = 2 * f(n-1)
fib = lambda n: n if n < 2 else 2 * fib(n - 1)
3️⃣ 矩形覆盖问题
题目:
用 21 小矩形覆盖 2n 大矩形,无重叠。
思路:
-
动态规划:
f(n) = f(n-1) + f(n-2)
f = lambda n: 1 if n < 2 else f(n-1) + f(n-2)
4️⃣ 杨氏矩阵查找
题目:
二维数组每行递增,每列递增,判断是否存在某整数。
思路:
-
Step-wise 线性搜索,从右上角开始
def find(matrix, x):
m, n = len(matrix)-1, len(matrix[0])-1
r, c = 0, n
while c >= 0 and r <= m:
if matrix[r][c] == x:
return True
elif matrix[r][c] > x:
c -= 1
else:
r += 1
return False
5️⃣ 去除列表重复元素
方法:
# 用集合
l2 = list(set(l1))
# 用字典
l2 = list({}.fromkeys(l1))
# 保持顺序
l2 = list(dict.fromkeys(l1))
# 列表推导式
l2 = []
[l2.append(i) for i in l1 if i not in l2]
6️⃣ 链表成对调换
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def swapPairs(self, head):
if head and head.next:
next = head.next
head.next = self.swapPairs(next.next)
next.next = head
return next
return head
7️⃣ 创建字典的方法
# 直接创建
d1 = {'name':'earth', 'port':'80'}
# 工厂方法
items = [('name','earth'),('port','80')]
d2 = dict(items)
# fromkeys
d3 = {}.fromkeys(('x','y'), -1)
8️⃣ 合并两个有序列表
循环法:
def merge_sortedlist(a, b):
c = []
while a and b:
if a[0] <= b[0]:
c.append(a.pop(0))
else:
c.append(b.pop(0))
c.extend(a)
c.extend(b)
return c
递归法:
def _recursion_merge(l1, l2, tmp):
if not l1 or not l2:
tmp.extend(l1 or l2)
return tmp
if l1[0] < l2[0]:
tmp.append(l1[0])
return _recursion_merge(l1[1:], l2, tmp)
else:
tmp.append(l2[0])
return _recursion_merge(l1, l2[1:], tmp)
def recursion_merge_sort(l1, l2):
return _recursion_merge(l1, l2, [])
9️⃣ 链表求交点
思路:
-
对齐长度 → 同步遍历 → 找到相同节点
def node(l1, l2):
len1 = len2 = 0
head1, head2 = l1, l2
while head1.next: head1 = head1.next; len1 += 1
while head2.next: head2 = head2.next; len2 += 1
if head1.next != head2.next: return None
# 长链先走
head1, head2 = l1, l2
if len1 > len2:
for _ in range(len1 - len2): head1 = head1.next
else:
for _ in range(len2 - len1): head2 = head2.next
while head1 and head2:
if head1.next == head2.next: return head1.next
head1 = head1.next
head2 = head2.next
1️⃣0️⃣ 二分查找
def binary_search(lst, item):
low, high = 0, len(lst)-1
while low <= high:
mid = low + (high - low)//2
if lst[mid] == item:
return mid
elif lst[mid] < item:
low = mid + 1
else:
high = mid - 1
return None
1️⃣1️⃣ 快速排序
def quicksort(lst):
if len(lst) < 2: return lst
pivot = lst[0]
less = [i for i in lst[1:] if i <= pivot]
greater = [i for i in lst[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater)
1️⃣2️⃣ 找零问题(动态规划)
def coinChange(values, money):
coinsUsed = [0]*(money+1)
for cents in range(1, money+1):
minCoins = cents
for v in values:
if v <= cents:
minCoins = min(minCoins, coinsUsed[cents-v]+1)
coinsUsed[cents] = minCoins
return coinsUsed[money]
1️⃣3️⃣ 二叉树遍历
class Node:
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
# 层次遍历 BFS
def level_traversal(root):
row = [root]
while row:
print([n.data for n in row])
row = [kid for node in row for kid in (node.left, node.right) if kid]
# 深度遍历 DFS
def deep(root):
if not root: return
print(root.data)
deep(root.left)
deep(root.right)
前序 / 中序 / 后序遍历:只改变打印顺序即可
1️⃣4️⃣ 树相关问题
-
求最大树深:
def maxDepth(root):
if not root: return 0
return max(maxDepth(root.left), maxDepth(root.right)) + 1
-
判断两棵树是否相同:
def isSameTree(p, q):
if not p and not q: return True
if p and q:
return p.val==q.val and isSameTree(p.left,q.left) and isSameTree(p.right,q.right)
return False
-
前序 + 中序求后序:
def rebuild(pre, center):
if not pre: return
root = Node(pre[0])
idx = center.index(pre[0])
root.left = rebuild(pre[1:idx+1], center[:idx])
root.right = rebuild(pre[idx+1:], center[idx+1:])
return root
1️⃣5️⃣ 单链表逆置
def rev(head):
prev, cur = None, head
while cur:
nxt = cur.next
cur.next = prev
prev = cur
cur = nxt
return prev
1️⃣6️⃣ 字符串是否为变位词(Anagram)
# 方法1:计数比较
def isAnagram1(s1,s2):
return sorted(s1) == sorted(s2)
# 方法2:计数数组
def isAnagram2(s1,s2):
c1 = [0]*26
c2 = [0]*26
for ch in s1: c1[ord(ch)-ord('a')] += 1
for ch in s2: c2[ord(ch)-ord('a')] += 1
return c1==c2
883

被折叠的 条评论
为什么被折叠?



