Python 面试全套超级详细清单

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

Python 数据类型详解


1️⃣ 数值类型(Number)

类型核心特性可变性常用操作面试加分点实战场景
int整数类型不可变+ - * / // % **Python 3 长整型不限大小,掌握位运算航班编号、票价整数计算
float浮点数不可变+ - * / ** round()浮点数精度问题,decimal 高精度计算航班票价、折扣计算
complex复数不可变real, imag少用,多考数学理解科学计算(航班优化算法可能用)
bool布尔值不可变and, or, notPython 内部实现为 int 子类条件判断、航班状态标记

一、int —— 整数类型(Python 最强的数值类型)

1️⃣ 底层原理
  • Python 3 的 int 是:

arbitrary precision integer(任意精度)
  • 不存在溢出

  • 本质是 多位数组 + 符号位

a = 10
b = a
a += 1
# b 仍然是 10(不可变对象)

📌 不可变 = 每次运算都会创建新对象

2️⃣ 常用 & 易忽略操作
+  -  *  /  //  %  **

整除陷阱(重要)

-7 // 3   # -3  (向下取整,不是向 0)
3️⃣ 位运算(面试高频)
&   # 按位与
|   # 按位或
^   # 异或
<<  # 左移
>>  # 右移
面试点
  • x << 1 == x * 2

  • x & 1 判断奇偶

def is_odd(x):
    return x & 1

4️⃣ 实战(航班编号 / 计数)
flight_id = 102345
seat_count = 180
remaining = seat_count - booked

二、float —— 浮点数(最容易出 Bug)

1️⃣ 底层原理(非常重要)
  • 遵循 IEEE 754

  • 二进制无法精确表示大多数十进制小数

0.1 + 0.2 == 0.3   # False

📌 不是 Python 的问题,是 计算机世界的物理限制


2️⃣ 精度问题示例
price = 199.9
discount = 0.1
price * discount   # 19.990000000000002


3️⃣ 正确处理金钱:decimal
from decimal import Decimal

price = Decimal("199.9")
discount = Decimal("0.1")
price * discount   # 精确

📌 面试加分点:

涉及钱,一定不用 float


4️⃣ round() 陷阱
round(2.5)   # 2
round(3.5)   # 4

👉 银行家舍入法(round half to even)


5️⃣ 实战(票价、折扣)
final_price = round(price * discount, 2)

三、complex —— 复数(冷门但能加分)

1️⃣ 基本结构
z = 3 + 4j
z.real  # 3.0
z.imag  # 4.0

2️⃣ 运算支持
(1+2j) * (3+4j)
abs(3+4j)   # 模长 = 5


3️⃣ 面试加分点
  • Python 内建支持复数

  • 不能比较大小

(1+2j) > (3+4j)  # TypeError

4️⃣ 实战(理论场景)
  • 信号处理

  • 优化算法

  • 路径规划数学模型

📌 业务系统很少用,但你知道就是优势


四、bool —— 布尔值(最容易被忽视)

1️⃣ 本质
isinstance(True, int)  # True

等价关系

True == 1
False == 0

📌 bool 是 int 的子类


2️⃣ 运算特性
True + True    # 2
False * 10    # 0

👉 很多计数逻辑直接利用这一点


3️⃣ 逻辑短路
a and b
a or b
  • and:第一个 False 就停

  • or:第一个 True 就停

user and user.is_active

4️⃣ 真值判断规则

False 的情况

False
0
0.0
''
[]
{}
None

5️⃣ 实战(航班状态)
is_delayed = False
is_cancelled = True

if is_cancelled or is_delayed:
    notify_passengers()

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, findf-string, encode/decode航班号、乘客姓名

一、list —— 万能容器,但“可变=风险”

1️⃣ 核心原理
  • 动态数组

  • 连续内存 + 扩容机制(通常 1.125~2 倍)

lst = [1, 2, 3]
lst.append(4)   # 原地修改

📌 可变对象,传参极易出 Bug


2️⃣ 高频操作 & 本质
append(x)     # O(1) 均摊
extend(iter)  # 批量追加
insert(i,x)  # O(n)
pop(i)       # O(n)
remove(x)    # O(n)
sort()       # 原地排序

3️⃣ 切片
lst[start:end:step]

⚠️ 切片返回新 list

a = lst[:]
a is lst  # False

4️⃣ 列表推导式
a = lst[:]
a is lst  # False

📌 可读性 + 性能都优于 for


5️⃣ 实战(航班列表)
flights = []
flights.append("MU1234")
flights.sort()

二、tuple —— 安全、快、适合“结构化数据”

1️⃣ 核心原理
  • 不可变对象

  • 存储的是 引用不可变

t = (1, [2, 3])
t[1].append(4)   # 不报错

📌 不可变 ≠ 内容不可变


2️⃣ 元组解包
flight_no, date = ("MU1234", "2026-01-08")

高级用法:

a, *b, c = (1,2,3,4,5)

3️⃣ 面试加分点
  • 比 list 更省内存

[list 对象头]
+ size          当前元素个数
+ allocated     已分配容量(>= size)
+ 指针数组      指向元素的指针

[tuple 对象头]
+ size          固定长度
+ 指针数组      正好 size 个

tuple 是定长、不可变序列,
底层结构比 list 简单,
不需要维护扩容容量和多余空间,
所以在 CPython 中比 list 占用更少内存,
同时遍历性能也更好。

  • 可作为 dict key(前提:元素可 hash)

d = {("MU1234", "2026-01-08"): 100}

4️⃣ 实战(固定航班信息)
flight = ("MU1234", "2026-01-08", "SHA", "PEK")

三、range —— 隐形高手(懒加载)

1️⃣ 核心原理
  • 不存数据

  • 只存:start / stop / step

r = range(1, 1_000_000_000)

📌 几乎不占内存


2️⃣ 特性
  • 有序

  • 不重复

  • 支持切片(仍是 range)

range(10)[2:8:2]
3️⃣ 面试加分点
i in range(1000000000)  # O(1)

👉 不遍历,而是数学计算


4️⃣ 实战(编号生成)
for i in range(1000, 1100):
    generate_flight_no(i)

3️⃣ 集合类型(Set)
类型核心特性可变性常用操作面试加分点实战场景
set

无序、唯一

  • Hash Table(哈希表)

  • 元素必须 可 hash

  • 无序(逻辑上)

可变add, remove, union, intersection, difference

集合运算效率高,hash 特性

  • set 底层就是 dict(只存 key)

  • 不能索引

  • 不能包含可变对象

存储已售票航班号,快速去重
frozenset
  • 不可变 set

  • hash 值固定

不可变union, intersection可作为 dict key固定航班组合标识

4️⃣ 映射类型(Mapping)
类型核心特性可变性常用操作面试加分点实战场景
dict
  • 哈希表

  • key → hash → bucket

  • Python 3.7+ 保持插入顺序

可变get, setdefault, pop, keys, values, items
  • key 必须可 hash

  • dict 扩容会 rehash

  • dict 不是线程安全

航班信息存储 {"航班号": "状态"}
collections.OrderedDict有序字典可变保持插入顺序Python 3.7+,普通 dict 已默认有序有序航班列表
defaultdictkey 不存在 → 自动创建可变自动初始化 key避免 KeyError航班票价分类统计

5️⃣ 二进制类型(Binary)
类型核心特性可变性常用操作面试加分点实战场景
bytes不可变字节序列不可变b'abc', decode, split字符编码、网络传输航班 API 数据传输
bytearray可变字节序列可变append, extend网络/文件操作二进制缓存处理
memoryview内存视图可变cast零拷贝,节省内存大规模航班数据处理

6️⃣ 面试连环题

可变 vs 不可变:理解引用、拷贝行为

        在 Python 中,变量并不直接保存数据本身,而是保存对象的引用。不可变对象(如 intstrtuple)一旦创建,其内部值就不能被修改,任何“修改操作”本质上都会创建一个新对象并让变量指向新的引用;因此多个变量指向同一个不可变对象时,不会因为其中一个变量的运算而影响其他变量。相反,可变对象(如 listdictset)允许原地修改内容,当多个变量引用同一个可变对象时,对其中任意一个变量的修改,都会反映到所有引用该对象的地方,这也是可变对象在共享场景下最容易引发 Bug 的根源。

        理解拷贝行为的关键在于区分“拷贝引用”还是“拷贝对象”。对不可变对象而言,所谓拷贝通常只是增加一个新的引用指向同一个对象,因为对象本身无法被修改,复用是安全的;而对可变对象,直接赋值或浅拷贝(如 list[:]copy.copy)只会创建一个新的容器,但容器内部的元素仍然是对原对象中元素的引用,一旦内部元素本身也是可变的,修改就会相互影响。只有深拷贝(copy.deepcopy)才会递归地复制所有层级的对象,从而实现真正意义上的“完全独立”,这也是在复杂业务和并发场景中隔离状态时必须慎重选择的拷贝方式。

为什么 x in setx in list 快?

  • list:顺序遍历 → O(n)

  • set:hash 定位 → O(1)

set 底层是 hash table,本质是 dict 的 key。

set 为什么无序?Python 3.7+ dict 却有序?

  • set:只关心 hash 分布

  • dict:在 hash table 之外维护插入顺序数组

dict 是“有序的 hash table”,set 不是。

set 里为什么不能放 list?

{[1,2]}  # TypeError
  • set 元素必须 可 hash

  • list 可变 → hash 值不稳定

hash 的前提是不可变。

dict 查找一定是 O(1) 吗?

  • 平均 O(1)

  • 极端 hash 冲突 → O(n)

  • hash 是什么?

  • Python 如何解决 hash 冲突?

  • rehash(扩容)什么时候发生?

Python 使用开放寻址法 + 动态扩容,极端情况已被极大避免。

一、什么是开放寻址法(Open Addressing)

冲突了,不建链表,继续找下一个空位

Python 的 dictset 在底层使用开放寻址法(Open Addressing)来解决 hash 冲突。当一个 key 经过 hash 计算后映射到的槽位已经被占用时,并不会像链表法那样在该位置挂链表,而是按照一套探测规则(扰动探测序列)继续在同一个数组中寻找下一个可用槽位。所有 key 都存放在一块连续的内存中,这种设计减少了指针跳转,提高了 CPU cache 命中率,使得查找、插入和删除在平均情况下都能保持接近 O(1) 的性能。

二、动态扩容(rehash)

为什么一定要扩容?

由于开放寻址法在表过满时探测成本会急剧上升,Python 为 dictset 设计了动态扩容机制。当哈希表的装载因子超过一定阈值(约为 2/3)时,系统会触发 rehash:申请一块更大的连续内存作为新表,然后将旧表中的所有 key 重新计算位置并插入到新表中。通过这种“低装载因子 + 扩容重排”的策略,Python 能有效避免性能退化,保证在数据量不断增长的情况下,哈希表操作依然稳定高效。

扩容过程

1. 申请更大的 table(通常 ×2)
2. 遍历旧 table
3. 对每个 key 重新 hash
4. 插入新 table

7️⃣ 航班系统【完整数据结构选型表】
场景类型原因
航班列表list有序、可遍历
固定航班信息tuple不可变、安全
航班编号生成range省内存
已售航班号setO(1) 查重
航线组合frozenset无序 + 可 hash
航班状态dictkey-value
有序航班缓存OrderedDictmove_to_end
票价统计defaultdict(int)自动初始化
航班信息存储dictkey-value 快速访问
售票用户集合set去重快速查找
不可修改的航班组合tuple / frozenset不可变,保证安全
大批量航班数据迭代generator节省内存,按需生成
二进制 API 数据bytes / bytearray网络传输和缓存处理

不推荐搭配

反例原因
list 查重O(n)
dict 当队列pop 慢
defaultdict 存业务数据隐式创建
set 存顺序数据顺序丢失

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时,该对象生命就结束了。

优点:

  1. 简单
  2. 实时性

缺点:

  1. 维护引用计数消耗资源
  2. 循环引用

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 → dictPython 可变参数实现调用灵活拼写错误导致收集不到参数通用函数包装任意参数 API
闭包内层函数引用外层变量形成闭包closure 保存外层变量 cell 对象使用 nonlocal 修改外层变量闭包引用循环引用造成内存问题装饰器缓存内部状态
装饰器函数套函数,返回新函数,@语法糖闭包 + 高阶函数functools.wraps 保留元信息多层装饰器顺序错误权限校验、缓存、日志装饰器
lambda匿名函数,单表达式编译为函数对象搭配 map/filter/comprehension单表达式限制列表字段提取

3️⃣并发与异步

题目核心答案底层原理面试加分点典型坑实战案例
threading vs multiprocessingthreading 受 GIL 限制,多用于 IO,multiprocessing 真并行GIL 只允许一个线程执行字节码,进程有独立 GIL选择适用场景CPU 密集型用 thread 会慢高并发 HTTP 请求 vs 数据计算
asyncio单线程事件循环协程切换Event Loop + Task Schedulingasync/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/20053359http://www.zhihu.com/question/20053359

函数重载主要是为了解决两个问题。

  1. 可变参数类型。
  2. 可变参数个数。

另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。

好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。

那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。

好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。

7️⃣新式类和旧式类

stackoverflowhttp://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python

这篇文章很好的介绍了新式类的特性: http://www.cnblogs.com/btchenguang/archive/2012/09/17/2689146.htmlhttp://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 并发与异步(线程 / 进程 / 协程)

对比项进程线程协程
调度者OSOS用户
内存独立共享共享
GIL 影响
CPU 并行
切换成本极低
编程难度
适合场景计算I/O高并发 I/O

进程解决隔离问题,线程解决并行执行问题,协程解决高并发等待问题。

进程

进程是资源隔离的最小单位,崩一个不影响其他

线程

线程是 CPU 调度的最小单位,切换由操作系统完成

协程

协程是用户态调度的执行单元,切换由程序控制

问题高分回答模板
1️⃣ Python 多线程能并行吗?不能完全并行(受 GIL 限制),但 I/O 密集型可以并发。CPU 密集型任务应使用多进程。
2️⃣ 协程能替代线程吗?不能,协程只能优化 I/O 等待,阻塞或 CPU 任务仍需线程/进程。
3️⃣ asyncio 任务遇到阻塞函数怎么办?会卡死整个事件循环,需要 run_in_executor 或使用异步库。
4️⃣ 线程和进程切换成本哪个高?进程高,线程中等,协程最低(用户态切换)。
5️⃣ 多进程共享内存怎么办?进程独立内存,通信需 Queue、Pipe 或共享内存模块。

1️⃣ 线程(Thread)

特点: 共享进程内存、轻量执行单元、阻塞时可并发
优势: 适合 I/O 密集型任务、轻量并发
劣势: CPU 密集受 GIL 限制,锁竞争复杂

  • 解决阻塞问题:一个线程阻塞时,其他线程继续运行

  • I/O 密集型任务可以通过几十、几百线程实现并发

场景原因
网络爬虫(几十到百线程)网络请求 I/O 阻塞,可并发
文件读写/数据库操作I/O 阻塞可并行
GUI 主线程 + 后台任务主线程保持响应,后台线程处理耗时任务

问题

  1. 线程开销大

    • 每个线程有独立栈空间,创建和切换成本高

    • 数百/上千线程时,内存占用高,CPU 上下文切换频繁

  2. 调度受操作系统控制

    • 上千线程并发时,切换效率低

  3. GIL 限制

    • Python 中 CPU 密集型任务无法在多线程中真正并行

题目核心答案原理解析面试加分点典型坑实战案例
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)

特点: 独立内存空间、资源隔离、CPU 调度单位
优势: 能利用多核 CPU,适合 CPU 密集型任务
劣势: 启动开销大,通信复杂

场景原因
图像/视频处理CPU 密集,进程独立,避免 GIL 限制
数据科学/矩阵计算大量浮点运算,可多核并行
独立服务隔离崩一个进程不影响其他进程
题目核心答案原理解析面试加分点典型坑实战案例
Python 进程独立内存空间,完全并行使用 multiprocessing.ProcessCPU 密集型任务优先启动开销大航班票价计算任务并行化
进程池multiprocessing.Pool复用进程减少创建开销apply/apply_async/map进程间共享数据需 Queue / Manager批量航班计算
进程间通信Queue, Pipe, ManagerIPC 数据交换理解共享 vs 拷贝不可直接传复杂对象多进程统计航班票价
fork vs spawn vs forkserver不同启动模式Linux fork 拷贝父进程内存Windows 默认 spawn数据复制大内存占用跨平台多进程启动
GIL 与进程GIL 不影响多进程并行每个进程独立 Python 解释器CPU 密集型任务并行共享数据需 IPCCPU 密集型计算优化

3️⃣ 协程(Coroutine / async / await)

  • 用户态调度:协程切换不依赖 OS,上下文切换开销非常小

  • 轻量高并发:单线程可以管理成千上万协程

  • 非阻塞 I/O:协程 await 可以让出 CPU,单线程就能处理多个 I/O

  • 逻辑清晰:避免大量锁竞争,代码可读性好

场景原因
高并发网络请求(数千/万连接)协程轻量,不用线程切换开销
异步爬虫/异步 Web 框架(FastAPI / aiohttp)I/O 非阻塞,单线程管理大量请求
异步任务队列/事件处理高并发事件处理无需线程切换
题目核心答案原理解析面试加分点典型坑实战案例
I/O 密集型使用多线程或 asyncio网络/文件 I/O 阻塞asyncio 高并发阻塞操作仍会堵塞爬取航班信息
CPU 密集型使用多进程充分利用多核 CPUmultiprocessing + pool进程间通信开销大航班票价计算任务
混合任务async + 线程池/进程池I/O 协程 + CPU 并行异步+并行组合混合模式复杂高并发查询 + 票价计算
死锁 / 饥饿Lock / Condition 使用不当线程 / 进程同步理解锁粒度阻塞任务无限等待多线程缓存更新
  • 进程资源隔离、适合 CPU 密集型任务

  • 线程解决阻塞问题、适合 I/O 密集型任务

  • 协程轻量高并发、适合大量 I/O 操作

  • 为什么线程防止阻塞?

    • 线程之间可以并行执行,某个线程阻塞不会影响其他线程。

  • 为什么协程能提高并发?

    • 协程在用户态调度,切换开销小,可以在单线程中管理成千上万 I/O 任务。

  • 为什么进程要隔离?

    • 每个进程独立内存空间,CPU 密集型任务不会被 GIL 限制,同时一个进程崩掉不会影响其他进程。

4️⃣ Python 并发设计选择

在多线程 + 协程组合下:

  • 线程隔离:一个线程阻塞不会影响其他线程,当一个任务被阻塞时,别的任务还能继续

  • 协程协作调度:线程内部阻塞会影响该线程内的协程,协程不是为了“少用线程”,而是为了“不浪费线程”

  • 整体执行:100 个任务最终都会执行完,只是被阻塞的协程可能稍微延迟

题目核心答案原理解析面试加分点典型坑实战案例
I/O 密集型使用多线程或 asyncio网络/文件 I/O 阻塞asyncio 高并发阻塞操作仍会堵塞爬取航班信息
CPU 密集型使用多进程充分利用多核 CPUmultiprocessing + pool进程间通信开销大航班票价计算任务
混合任务async + 线程池/进程池I/O 协程 + CPU 并行异步+并行组合混合模式复杂高并发查询 + 票价计算
死锁 / 饥饿Lock / Condition 使用不当线程 / 进程同步理解锁粒度阻塞任务无限等待多线程缓存更新

5️⃣ Python 并发实战案例

协程和线程解决的问题不一样,协程不能完全替代线程。

协程适合的是高并发 I/O 场景,它通过 await 在等待时主动让出执行权;
线程解决的是阻塞问题,当一个任务阻塞时,其他任务还能继续执行。

在 Python 里,由于 GIL 的存在,线程本身就不适合 CPU 密集任务;
而协程如果遇到阻塞代码或非 async 库,会直接把整个事件循环卡死。

所以在实际场景中,协程通常是用来减少线程数量,而不是取代线程,协程解决的是并发等待,线程解决的是阻塞执行
常见的做法是:

  • I/O 用协程

  • 阻塞或 CPU 任务交给线程或进程

场景实现方式面试加分点典型坑说明
批量航班 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)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。

当一个内嵌函数引用其外部作作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点:

  1. 必须有一个内嵌函数
  2. 内嵌函数必须引用外部函数中的变量
  3. 外部函数的返回值必须是内嵌函数

感觉闭包还是有难度的,几句话是说不明白的,还是查查相关资料.

重点是函数运行后并不会被撤销,就像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 lambdalambda 是匿名函数,闭包可保存状态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() 与 yieldyield 可接收 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 异常处理数据库事务、文件处理

高并发航班订票系统 —— 分层组件与技术选型详解

1️⃣ DNS / 调度层(Global Traffic Management)

这是“系统真正的入口”,不是域名解析那么简单


一、选型组件(What we choose)

1. 智能 DNS(Smart DNS / GSLB)

  • 本质是 全局流量调度系统(Global Server Load Balance)

  • 常见实现:

    • 云厂商:阿里云 / 腾讯云 / AWS Route53

    • 自建:DNS + 健康探测 + 权重系统

2. Geo DNS

  • 基于:

    • 用户 IP → 地理位置

    • 运营商(电信 / 联通 / 移动)

  • 返回 不同机房 / 不同 VIP

3. 健康检查系统

  • HTTP / TCP 探活

  • 机房级、集群级健康状态


二、作用(What)

用户请求真正进入任何服务器之前 完成:

  • 跨地域调度

  • 就近接入

  • 机房级容灾

  • 大促 / 春运期间的流量削峰前置

这是 唯一一个不依赖后端服务的调度层


三、为什么一定要在 DNS 层调度(Why)

1️⃣ 调度越前,代价越小

  • DNS 层调度 = 请求还没产生服务器资源消耗

  • LB 层调度 = 已经进机房了

2️⃣ 机房级故障只能靠 DNS

  • LB / 服务治理只能解决「机房内问题」

  • 机房断网、断电、光纤被挖 → 只有 DNS 能切

3️⃣ DNS 是天然的“全局视角”

  • LB 只能看到自己

  • DNS 可以看到 全球所有机房


四、技术能力(How)
  • 基于权重返回 IP

    • 70% → 主机房

    • 30% → 灰度机房

  • 基于健康状态动态摘流

  • 结合 TTL 控制切流速度

  • 运营商调度

    • 电信 → 电信机房

    • 联通 → 联通机房


五、不这样做的后果(Risk)

  • 所有流量压到一个机房

  • 跨地域 RTT 飙升(抢票直接失败)

  • 机房级事故 = 全站事故


六、航班订票系统中的价值(Business)

春运抢票场景下
同一秒进来的百万请求,被 DNS 在全球范围内拆散


2️⃣ 安全防护层(WAF / DDoS)

这层的目标只有一个:让“脏流量”死在业务之外


一、选型组件

1. WAF(Web Application Firewall)

  • 防的不是黑客

  • 防的是:

    • 非正常行为

    • 非人类请求

    • 非业务流量

2. DDoS 高防

  • SYN Flood

  • UDP Flood

  • HTTP Flood

3. 行为风控系统

  • IP / UA / Cookie / 行为轨迹

  • 设备指纹


二、作用(What)

  • 业务逻辑之前

  • 缓存之前

  • 限流之前

直接把攻击流量 剪掉


三、为什么必须独立一层(Why)

  • 攻击流量:

    • 不遵守业务规则

    • 不关心返回结果

  • 如果让它进入业务层:

    • Redis 会被打满

    • DB 会被拖死

    • 限流形同虚设

👉 安全是基础设施能力,不是业务能力


四、技术能力(How)
  • CC 攻击识别(频率 + 行为模式)

  • 人机识别(JS Challenge)

  • IP 动态封禁

  • 请求特征建模


五、不做的后果
  • 黄牛脚本 24h 不间断刷接口

  • 正常用户永远抢不到票

  • 系统看起来“没挂”,但已经“死了”


六、航班系统价值

抢票系统的第一竞争力:
不是并发高,而是“刷子进不来”


3️⃣ CDN / LB 层(流量承载层)

负责“扛流量”,不是做业务


一、选型组件

CDN

  • 静态资源

  • 半静态页面

  • 热点查询接口(可选)

LVS(四层)

  • 高性能转发

  • 无业务逻辑

Nginx(七层)

  • 路由

  • Header 处理

  • 反向代理


二、作用(What)

  • 最廉价的请求挡在最外层

  • 昂贵请求均匀分发


三、为什么这么组合(Why)

  • CDN:最便宜的算力

  • LVS:最强的转发

  • Nginx:最灵活的控制

👉 各司其职,不越界


四、技术能力(How)
  • 四层负载(TCP)

  • 七层路由(URL / Header)

  • 动静分离

  • 后端健康检查

策略原理面试加分点典型应用场景实战案例
轮询 (Round Robin)每个请求按顺序分配到后端服务器,简单公平适合请求处理能力相近的服务器,简单高效小型 Web 服务、API 服务,服务器性能差异不大Python 航班查询 API,每个实例处理能力相同,平均分发请求
最少连接 (Least Connections)将新请求分配给当前连接数最少的服务器适合请求处理时间长、服务器性能不同的场景文件上传、长连接 API、WebSocketPython 文件上传服务,长请求分发给负载较轻的实例
源地址哈希 (IP Hash)根据客户端 IP 哈希计算分配到固定后端服务器保证同一客户端请求分配到同一台服务器(会话保持)会话依赖场景(未使用 Redis Session)航班预订系统,用户操作需要保持会话状态,避免跨实例丢失缓存

五、不这样做的后果
  • 应用服务器被 JS / 图片 拖死

  • 单点 LB 成为瓶颈

  • 扩容成本极高


六、航班系统价值

图片、航班列表、静态配置
永远不进应用层

4️⃣ 接入层 / API Gateway(系统“城门”)

这一层不是为了“转发请求”,而是为了“控制系统生死”


一、选型组件(What we choose)

1️⃣ Nginx + Lua(OpenResty 体系)

  • Nginx:成熟、稳定、高性能

  • Lua:可编程、可热更新、低侵入

👉 适合:

  • 超高 QPS

  • 简单但极其关键的逻辑(限流、鉴权)


2️⃣ Kong / APISIX(现代 API 网关)

  • 基于 Nginx + Lua

  • 插件化能力极强

  • 天然支持:

    • 限流

    • 鉴权

    • 灰度

    • 监控

👉 适合:

  • 微服务规模较大

  • 接口数量多

  • 需要统一治理


二、这一层到底干什么(What)

只干三件事,而且每一件都“决定系统能不能活”:

  1. 谁可以进来(鉴权)

  2. 进来多少(限流)

  3. 往哪去(路由 / 灰度)

🚫 不写任何业务逻辑


三、为什么必须独立一层(Why)

1️⃣ 这是“系统边界”

  • 所有外部请求 只能从这里进

  • 一旦绕过,后果不可控

2️⃣ 横切能力不属于业务

  • 鉴权

  • 限流

  • 灰度

  • 黑白名单

👉 如果散落在服务中:

  • 策略不一致

  • 改一处要改 N 个服务

  • 必然出事故


四、技术能力(How)
① 鉴权机制

JWT / OAuth

  • Token 在网关校验

  • 后端服务 不再关心用户身份

优势:

  • 减少服务复杂度

  • 防止未授权请求进入核心链路


② 限流(⚠️ 极其重要)

常见限流维度

  • IP

  • 用户 ID

  • 接口

  • 全局 QPS

常用算法

  • 令牌桶(允许突发,适合下单)

  • 漏桶(平滑流量,适合写操作)

👉 限流一定在业务之前


③ 路由 & 灰度
  • 按 Header / Cookie 路由

  • 新版本只接 1% 流量

  • 出问题立即回滚


五、如果没有这一层(真实后果)

  • 下单接口被直接打穿

  • 每个服务各自限流 → 策略冲突

  • 无法在事故时“一刀切流量”


六、航班订票系统中的价值

锁座 / 下单接口的并发上限
不是靠数据库扛,而是靠网关“掐住”


5️⃣ 服务治理 & 配置中心(系统“自保机制”)

这层的目标不是“调用成功”,而是“别一起死”


一、选型组件

1️⃣ Nacos / Consul

  • 服务注册

  • 服务发现

  • 配置管理

2️⃣ Sentinel

  • 熔断

  • 降级

  • 流量控制


二、这一层解决什么问题(What)

👉 服务之间的调用不可靠

  • 网络会抖

  • 服务会慢

  • 依赖一定会失败

这一层负责:

  • 控制失败的影响范围

  • 阻断级联故障


三、为什么必须有服务治理(Why)

没有治理的微服务 = 定时炸弹

  • A 调 B

  • B 调 C

  • C 慢了 → B 卡 → A 卡 → 全站卡死

👉 这不是 bug,这是必然


四、技术能力(How)

① 熔断(Circuit Breaker)

触发条件:

  • RT 超过阈值

  • 错误率升高

  • QPS 异常

行为:

  • 直接失败

  • 返回降级结果

  • 不再发请求


② 降级(Degrade)

  • 非核心功能直接关闭

  • 返回兜底数据

例子:

  • 推荐航班不可用

  • 但下单仍可用


③ 动态配置

  • 限流阈值实时调整

  • 熔断策略在线生效

  • 不重启服务


五、不做的后果
  • 一个慢服务拖垮整个系统

  • 高峰期雪崩

  • 无法止血


六、航班系统价值

支付、通知、积分 可以失败
下单主链路必须活


6️⃣ 应用层(Python Async 计算层)

这里才是真正的“业务心脏”


一、选型组件

FastAPI / Tornado

  • 原生异步

  • 高性能

  • 生态成熟

asyncio + uvloop

  • 事件驱动

  • 单线程高并发

  • uvloop 替代默认事件循环

多进程模型

  • 每核一个进程

  • 避开 GIL 影响


二、这一层干什么(What)
  • 下单

  • 锁座

  • 状态流转

  • 业务校验

👉 只关心业务正确性


三、为什么 Python 还能扛高并发(Why)
  • 抢票系统:

    • 90% 时间在等 I/O

    • DB / Redis / MQ

👉 协程 ≠ 线程
👉 一个进程能跑几万协程


四、技术能力(How)
  • 非阻塞 DB / Redis / MQ

  • 协程调度

  • 连接池复用

  • 超时控制


五、不这样做的后果
  • 线程数爆炸

  • 上下文切换耗尽 CPU

  • RT 不稳定


六、航班系统价值

高峰期
查询不拖慢下单,下单不阻塞支付


7️⃣ 缓存层(Redis · 系统真正的“抗压核心”)

如果你只能在整个系统里选一层重点设计,那一定是这一层


一、选型组件(What we choose)

1️⃣ Redis(核心)

为什么是 Redis,而不是别的缓存?

  • 内存级访问(微秒级)

  • 丰富数据结构:

    • String(余票数)

    • Hash(航班信息)

    • ZSet(热门航班排行)

  • 原子操作(INCR / DECR / Lua)

  • 单线程模型(避免锁竞争)

👉 Redis 天然适合“高并发 + 强一致的局部场景”


2️⃣ 布隆过滤器(Bloom Filter)

  • 解决“查不到的数据被反复请求”的问题

  • 本质是:

    • 用很小的内存

    • 判断「某个 key 一定不存在」


3️⃣ 分布式锁(Redis Lock / Lua)

  • 只用于:

    • 热点 key

    • 短时间互斥

  • 不是全局锁


二、这一层的核心职责(What)

缓存层不是“加速器”,而是三件事:

  1. 挡流量

  2. 保数据库

  3. 稳 RT

👉 在高并发系统里:
Redis ≈ 系统的“缓冲垫 + 防爆阀”


三、为什么 DB 前一定要有缓存(Why)

如果没有缓存:

  • 所有查询直打 DB

  • DB QPS 一到阈值 → RT 飙升 → 连接池耗尽

  • 整个系统雪崩

👉 数据库不是为高并发设计的


四、缓存三大致命问题 & 工程解法(How)

⚠️ 1️⃣ 缓存穿透(最基础但最致命)

问题本质

  • 请求一个 DB 中根本不存在的数据

  • 每次都绕过缓存 → 直打 DB

        缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命 中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,产生穿透问题

        缓存穿透问题可能会使后端存储负载加大,由于很多后端存储不具备高 并发性,甚至可能造成后端存储宕掉

工程解法

布隆过滤器

  • 所有合法航班号提前写入

  • 查询前先判断:

    • 不存在 → 直接返回

在访问缓存层和存储层之前,将存在的 key 用布隆过滤器提前保存起来,做第一层拦截,即使发生了缓存穿透,大量请求只会查询 Redis 和布隆过滤器,而不会查询数据库,保证了数据库能正常运行

这种方法适用于数据命中不高、数据相对固定、实时性低(通常是数据 集较大)的应用场景,代码维护较为复杂,但是缓存空间占用少

空值缓存

  • DB 查不到 → 缓存一个短 TTL 的空值

当存储层不命中后,仍然将空对象保留到缓存层中,之后再访问这个数据将会从缓存中获取,这样就保护了后端数据源

当然缓存空对象会有两个问题:

第一,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间(如果是攻击,问题更严重),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除

第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象

缓存空对象与布隆过滤器比较


⚠️ 2️⃣ 缓存击穿(热点 Key 爆炸)

问题本质

  • 某个热点 key(如热门航班余票)

  • 同一时刻失效

  • 大量请求同时打 DB

        如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题

        击穿其实可以看做是雪崩的一个子集,解决方法一般有两种,设置热点数据永不过期和设置互斥锁

工程解法

热点 key 互斥锁

  • 第一个请求回源

  • 其他请求等待或快速失败

        所谓的互斥锁,就是保证同一时间只有一个业务线程更新缓存,对于没有获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值

逻辑过期

  • value 中存:

    • 数据

    • 逻辑过期时间

  • 后台异步刷新


⚠️ 3️⃣ 缓存雪崩(系统级灾难)

问题本质

  • 大量 key 同时过期

  • DB 扛不住瞬时洪峰

        由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会级联宕机的情况

工程解法

TTL 随机化

  • 过期时间加随机偏移

        针对大量缓存同时过期的情况,可以通过缓存 reload 机制,预选去更新缓存,在即将发生大并发访问前手动触发加载缓存不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀

多级缓存

  • 本地缓存 + Redis

        出现服务不可用的情况,我们第一时间想到的肯定是高可用,甚至是异地容灾等机制

限流兜底

  • 缓存异常 → 快速失败

        无论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源。作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部阻塞(hang)在这个资源上,造成整个系统不可用。降级机制在高并发系统中是非常普遍的:比如推荐服务中,如果个性化推荐服务不可用,可以降级补充热点数据,不至于造成前端页面开天窗


五、缓存与一致性(极重要)

原则只有一句话:

缓存永远不作为“事实来源”

工程策略:

  • 写操作:

    • 先写 DB

    • 再删缓存(延迟双删)

  • 读操作:

    • 缓存为主

    • DB 兜底


六、航班系统中的真实用法

  • 航班信息:Redis Hash

  • 余票数:Redis String + Lua 原子扣减

  • 查询 QPS:99% 不进 DB


8️⃣ 消息队列层(MQ · 削峰与解耦的核心)

没有 MQ 的高并发系统,本质是“硬扛”


一、选型组件(What we choose)

Kafka

  • 吞吐极高

  • 适合日志、埋点、异步通知

RocketMQ

  • 事务消息

  • 顺序消息

  • 业务场景友好

RabbitMQ

  • 可靠性高

  • 延迟队列成熟


二、MQ 这一层到底解决什么(What)

  1. 削峰

  2. 异步

  3. 解耦

👉 让系统“慢慢消化”流量,而不是瞬间崩溃。


三、为什么同步链路一定要被切断(Why)

  • 同步链路越长:

    • RT 越高

    • 失败概率越大

  • 高峰期:

    • 下单 ≠ 支付 ≠ 通知

👉 下单成功 ≠ 所有事立刻完成


四、MQ 的工程能力(How)

1️⃣ ACK 机制

  • 消费成功才确认

  • 失败可重试

2️⃣ 幂等消费(必须)

  • 每条消息有唯一 ID

  • 消费前先判断是否处理过

幂等(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,结果不同

3️⃣ 顺序消息(关键场景)

  • 订单状态流转

  • 支付 → 出票 → 通知


五、不使用 MQ 的后果

  • 高峰期下单接口直接被拖死

  • 所有功能互相影响

  • 一处慢,全站慢


六、航班系统中的实际应用

  • 下单成功 → 发 MQ

  • 异步:

    • 发短信

    • 写日志

    • 对账

  • 主链路 RT 稳定


9️⃣ 数据层(关系型数据库 · 最后的底线)

这是系统里“最不能错”的一层


一、选型组件(What we choose)

MySQL / PostgreSQL

  • 成熟

  • 强事务

  • 强一致

👉 所有钱、订单、库存,最终只认这里


二、为什么 DB 一定要“减负”(Why)

  • DB 是:

    • 最慢

    • 最贵

    • 最脆弱

  • 一旦被打爆:

    • 数据丢失

    • 系统不可恢复


三、核心技术能力(How)

1️⃣ 主从复制

  • 主写

  • 从读

2️⃣ 读写分离

  • 查询走从库

  • 写操作走主库

3️⃣ 分库分表

  • 按用户 ID / 订单 ID 分片

  • 控制单表数据量


四、冷热数据分层(非常重要)

  • 热数据:

    • 最近订单

    • 当日航班

  • 冷数据:

    • 历史订单

    • 已完成航班

👉 冷数据可:

  • 归档

  • 降级存储

  • 只读


五、航班系统中的数据设计

  • 库存:

    • DB 兜底

    • Redis 扛并发

  • 订单:

    • DB 强事务

  • 查询:

    • 缓存 + 搜索引擎

🔟 数据一致性 & 防超卖(核心中的核心)

并发系统 90% 的事故,死在这一层


一、问题先说清楚(不然全是空话)

航班订票系统的真实约束

  • 库存是共享资源

  • 下单是强事务

  • 系统是分布式

  • 并发是不可控的

👉 结论只有一个:

强一致 ≠ 高并发下可行方案


二、为什么不能“简单加锁”(Why)

❌ DB 行锁的问题

  • 锁时间长

  • 吞吐极低

  • 高峰期必死

❌ 分布式锁全链路

  • 网络不可靠

  • 锁丢失、误删

  • 容易死锁

👉 锁只能用于“短、快、局部”


三、工程级共识(非常重要)

库存的事实来源是 DB
库存的并发承载在 Redis

这是整个系统设计的基石。


四、真实可落地的“防超卖方案”(How)

✅ 核心思想:两阶段控制


阶段一:Redis 原子预扣库存(高并发阶段)

技术手段

  • Redis + Lua 脚本

  • 单线程执行

  • 原子扣减

执行逻辑

1. 用户请求下单
2. Redis 判断库存 > 0
3. 原子扣减库存
4. 成功 → 进入下单流程
5. 失败 → 直接返回“售罄”

👉 这里不碰 DB


阶段二:DB 最终确认库存(事务阶段)

技术手段

  • 本地事务

  • 乐观锁 / version 字段

执行逻辑

1. 开启 DB 事务
2. 校验库存版本
3. 扣减库存
4. 创建订单
5. 提交事务

阶段三:异步兜底 & 回滚(容错阶段)

失败场景

  • DB 写失败

  • 服务异常

  • 进程崩溃

兜底方案

  • Redis 预扣库存超时回补

  • MQ 补偿消息

  • 定时任务对账


五、完整下单链路(一步不跳)

用户 → API Gateway(限流)
     → 应用层
       → Redis Lua 预扣库存
         → 成功
           → 创建订单(DB 事务)
             → 成功
               → 发 MQ(下单成功)
             → 失败
               → 发送补偿 MQ(回补库存)
         → 失败
           → 返回售罄

六、为什么这是“最终一致性”(Why)

  • Redis 和 DB 不在同一事务

  • MQ 异步

  • 允许短时间不一致

👉 但系统保证:

  • 不会超卖

  • 最终一定对账一致


七、为什么不用 TCC 全链路

TCC 的真实代价

  • 实现复杂

  • 维护成本极高

  • 高并发下吞吐下降明显

👉 在库存这种“高并发资源”场景下,
TCC ≠ 最优解


八、航班系统中的真实价值

春运高峰
10 万人同时抢 1 万张票
系统不超卖、不锁死、不崩溃


1️⃣1️⃣ 可观测性层(没有它 = 瞎飞)

你不是在“跑系统”,
你是在“监控一个随时会失控的复杂系统”


一、选型组件

Prometheus

  • 指标采集

  • 时序数据

Grafana

  • 可视化

  • 大盘

ELK / Loki

  • 日志集中

  • 快速检索

Trace(Jaeger / SkyWalking)

  • 链路追踪


二、为什么监控不是“可选项”(Why)

  • 故障一定会发生

  • 高并发下问题是:

    • 瞬时的

    • 不可复现的

👉 没有监控 = 无法定位 = 只能背锅


三、必须监控的核心指标(How)

① 流量指标

  • QPS

  • 并发数

② 性能指标

  • RT(P50 / P90 / P99)

  • 慢接口

③ 稳定性指标

  • 错误率

  • 超时率

  • 熔断次数


四、日志的工程要求

  • 结构化日志(JSON)

  • 必须包含:

    • trace_id

    • order_id

    • user_id

  • 可跨服务串联


五、航班系统中的价值

抢票高峰
5 秒内定位:
是缓存?DB?MQ?还是网络?


1️⃣2️⃣ 灾备 / 多活 / 高可用

高并发 ≠ 高可用
活着比快更重要


一、选型方案

多机房部署

  • 同城多活

  • 异地灾备

自动切流

  • DNS

  • 网关

  • LB

定期演练

  • 故障注入

  • 真实切换


二、为什么灾备不能“临时想”(Why)

  • 灾难发生时:

    • 没时间讨论方案

    • 没时间写脚本

👉 灾备一定是“平时就跑着的方案”


三、技术能力(How)

  • 数据异步复制

  • 只读降级

  • 非核心功能关闭


四、航班系统中的最终目标

任何单点故障
都不影响用户完成订票

高并发系统分层架构总表(航班订票系统)

层级主要组件 / 技术核心职责(What)技术要点(How)为什么选(Why)关键设计点航班订票系统中的作用
DNS / 调度层智能 DNS、Geo DNS全局流量调度基于地域、运营商、健康检查返回解析结果调度越前置,后端压力越小真正的流量调度从 DNS 开始,而不是 LB用户请求就近接入最近、最健康机房
安全防护层WAF、DDoS 防护拦截恶意流量规则匹配、IP 黑白名单、流量清洗攻击流量比业务流量更致命安全必须与业务彻底解耦防止黄牛刷票、CC 攻击
CDN / LB 层CDN、Nginx、LVS、F5静态缓存、请求分发CDN 边缘缓存 + 四/七层负载均衡大量读请求不应进入后端四层高性能,七层更灵活航班列表页、静态资源就近返回
接入层 / API GatewayNginx + Lua、Kong、APISIX统一入口、鉴权、限流JWT 鉴权、令牌桶 / 漏桶限流、路由避免各服务重复造轮子限流是系统生死线,必须前置控制下单、锁座请求速率
服务治理 & 配置中心Nacos、Consul、Sentinel服务发现、熔断、降级RT / 错误率触发熔断,动态配置防止慢服务拖垮全链路熔断不是失败,而是保护支付、通知异常不影响下单
应用层(计算层)FastAPI、Tornado、asyncio、uvloop核心业务处理多进程 + 协程,I/O 全异步订票系统 I/O 密集Python 也能支撑高并发下单、锁座、订单状态流转
缓存层Redis、Memcached热点数据缓存防穿透:布隆过滤器 + 空值缓存防击穿:热点 key 互斥锁防雪崩:TTL 随机化 + 异步刷新DB 抗压能力有限缓存不是加速器,而是 DB 保护层缓存余票、航班信息
消息队列层Kafka、RabbitMQ、RocketMQ异步解耦、削峰ACK、重试、幂等消费、顺序保证同步链路越长越不稳定MQ 是系统的缓冲垫异步下单、通知、日志
数据层MySQL、PostgreSQL数据持久化主从复制、读写分离、分库分表单库容量和性能有限先拆读写,再拆分片存储订单、用户、支付数据
搜索 & 存储层Elasticsearch、S3、MinIO搜索与对象存储倒排索引、对象 Key 存储DB 不适合复杂搜索DB 管事务,ES 管搜索航班搜索、订单日志
数据一致性 / 事务层本地事务、TCC、Saga一致性保障锁座 + 本地事务 → MQ → 幂等 + 补偿强一致成本极高最终一致性是工程常态防止重复下单、库存超卖
可观测性层Prometheus、Grafana、ELK、Loki监控、日志、告警Metrics / Logs / Trace没有监控就没有运维P99 比平均值更重要监控抢票高峰系统健康
灾备 / 高可用层多活、容灾切换、演练故障恢复多机房、自动切流、演练故障一定会发生高并发 ≠ 高可用单机房故障不影响订票

高并发系统分层架构示意图(航班订票系统)

────────────────────────── 用户 / 客户端 ──────────────────────────┐
│ Web / App / 小程序                                               │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── DNS / 调度层 ──────────────────────────┐
│ 智能 DNS / Geo DNS                                              │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 安全防护层 ────────────────────────────┐
│ WAF / DDoS 防护                                                 │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── CDN / LB 层 ──────────────────────────┐
│ CDN / Nginx / LVS / F5                                          │
│ 静态资源缓存 / 四层 / 七层负载均衡                               │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 接入层 / API Gateway ──────────────────┐
│ Nginx + Lua / Kong / APISIX                                      │
│ 鉴权 / 限流 (令牌桶 / 漏桶) / 路由 / 灰度                        │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 服务治理 & 配置中心 ──────────────────┐
│ Nacos / Consul / Sentinel                                       │
│ 熔断 / 降级 / 超时控制 / 动态配置                                │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 应用层(计算层) ──────────────────────┐
│ FastAPI / Tornado / asyncio / uvloop                             │
│ 核心业务逻辑 / 多进程 + 协程                                     │
│ 下单 → 库存锁 → 本地事务                                         │
│ 幂等 + MQ 异步更新                                              │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 缓存层 ────────────────────────────────┐
│ Redis / Memcached                                               │
│ ┌───────────────┐ ┌───────────────┐                             │
│ │ 热数据缓存    │ │ 冷数据缓存    │                             │
│ │ (实时余票 / 热门航班) │ │ (历史查询)   │                             │
│ └───────────────┘ └───────────────┘                             │
│ 防穿透:布隆过滤器                                              │
│ 防击穿:互斥锁 / 单 key 回源                                      │
│ 防雪崩:随机 TTL / 异步刷新                                       │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 消息队列层 ────────────────────────────┐
│ Kafka / RabbitMQ / RocketMQ                                      │
│ 异步削峰 / 下单通知 / 支付同步 / 幂等处理 / 重试机制             │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 数据层 ────────────────────────────────┐
│ MySQL / PostgreSQL                                              │
│ ┌───────────────┐ ┌───────────────┐                             │
│ │ 主库          │ │ 从库          │                             │
│ │ (写入订单/支付) │ │ (航班信息 / 历史查询) │                             │
│ └───────────────┘ └───────────────┘                             │
│ 分库分表 / 读写分离                                           │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 搜索 & 存储层 ─────────────────────────┐
│ Elasticsearch / S3 / MinIO                                      │
│ 搜索 / 对象存储 / DB 与搜索解耦                                  │
│ 历史订单 / 航班搜索 / 日志存储                                   │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 数据一致性 / 事务层 ────────────────────┐
│ 本地事务 / TCC / Saga                                           │
│ 幂等 / 补偿 / 最终一致性                                        │
│ 下单锁座 → MQ 异步同步 → 校验库存 / 补偿                         │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 可观测性层 ────────────────────────────┐
│ Prometheus / Grafana / ELK / Loki                               │
│ 监控 / 日志 / 链路追踪 / P99 / 错误率                             │
└───────────────┬───────────────────────────────────────────────┘
                │
                ▼
────────────────────────── 灾备 / 高可用层 ────────────────────────┐
│ 多活 / 容灾切换 / 演练 / 快速切换                                 │
└───────────────────────────────────────────────────────────────┘

Q1:为什么查询和下单要拆?

查询是读多写少,适合缓存;下单要求强一致,必须独立。

Q2:Redis 挂了怎么办?

本地缓存兜底 + 降级返回旧数据 + 限流。

Q3:如何保证不超卖?

Redis 原子锁库存 + 订单状态机 + 超时回滚。

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 NoSQLSQL: 关系型,结构化;NoSQL: 非关系型,灵活SQL: 表、行、列、ACID;NoSQL: KV、文档、列族、图分布式事务 vs 弱一致性错选数据库导致性能瓶颈航班信息使用 MySQL,缓存票价用 Redis
主从复制主库写,从库读binlog + replication thread异地备份,提高读吞吐主从延迟导致脏读查询航班库存读从库
事务特性 ACID原子性、一致性、隔离性、持久性InnoDB 事务实现机制面试可结合隔离级别不懂隔离级别导致脏读、幻读下单事务处理
SQL 注入防护参数化查询 / ORMORM 底层生成预编译 SQL提到 Python DB API 规范拼接字符串 SQL 易被注入Django ORM 或 SQLAlchemy
索引类型B-tree、Hash、Full-text、BitmapB-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 SQLN+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/hgetallhash table节省内存,适合存对象hgetall 大 hash 内存占用大航班信息对象存储
Set 操作sadd/srem/sismember/sinter/sunionhash table去重、交集/并集大量交集操作慢用户已选航班去重
Sorted Setzadd/zrange/zscoreskip 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 Clusterslot 分片 + 多节点hash slot + 迁移水平扩展hash slot 不均衡导致热点航班大表分片
分布式锁 RedLock多节点 setnx + expire保证跨节点锁防止单点失效节点网络延迟可能导致误解锁高并发库存扣减

5️⃣ Python 与 Redis 实战

题目核心答案原理解析面试加分点典型坑实战案例
Python 操作 Redisredis-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 / ENTRYPOINTNamespace 隔离进程,Cgroups 限制资源使用 --restart 保持高可用忘记挂载 volume 导致数据丢失Python API 容器化运行
容器网络模式bridge, host, overlay, noneNAT + 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/removeURL 配置错误多仓库协作
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 新分支时加 -upush 被拒绝发布 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

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值