计算机基础面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

计算机基础阅读 05月29日 00:12

TCP 可靠传输靠哪些机制保证?RTO 和 SACK 是怎么工作的?

TCP 可靠传输靠六个机制保证:序列号+确认应答(保证有序、检测丢包)、超时重传+快重传(丢包恢复)、校验和(检测数据损坏)、流量控制(防接收方溢出)、拥塞控制(防网络过载)。其中序列号和重传是核心,其他都是辅助。追问超时重传的 RTO 怎么算?为什么不能固定值?网络延迟波动大,固定值不靠谱——设小了正常延迟就触发多余重传,设大了真丢包要等很久才恢复。RTO 基于实测 RTT 动态计算:RTO = SRTT + 4 × RTTVAR,SRTT 是平滑 RTT(历史加权平均),RTTVAR 是 RTT 波动幅度。首次测量 SRTT = RTT,后续每次 SRTT = 7/8 × 旧SRTT + 1/8 × 新RTT。Linux 还有个下限(tcprtomin,默认 200ms),防止 RTO 过小导致过度重传。累积确认有什么问题?SACK 怎么解决的?累积确认只告诉你"到这个序号之前的都收到了",中间丢了哪个说不清。比如发了 1-10 号包,5 号丢了,ACK 会一直回 5(表示期望收到 5),6-10 号收到了但不确认。发送方不知道 6-10 号到底到了没有,可能重传 5-10 全部。SACK(Selective ACK)在 TCP 选项里额外报告已收到的非连续块,发送方就知道只重传 5 号就行。Linux 默认开启 SACK(tcp_sack=1),对高延迟高丢包链路很重要。序列号为什么是按字节编号而不是按包编号?因为 TCP 是字节流协议,应用层不关心包的边界——你写 1000 字节,TCP 可能拆成两个 500 字节的段,也可能合成一个。按字节编号让接收方能精确知道每个字节的位置,不管怎么拆分合成都能正确重组和确认。也方便滑动窗口按字节数控制流量。面试追问:ISN(初始序列号)为什么不用 0 或 1?防止旧连接的报文段被新连接误接收——ISN 基于时钟递增,每隔 4 微秒加 1,不同连接的序列号空间错开。校验和能检测所有错误吗?不能。校验和是 16 位反码求和,只能检测单比特错误和大部分多比特错误,但两个错误恰好互相抵消时校验和不变。以太网的 CRC 能检测更多错误,但端到端路径上中间设备可能修改数据(如 NAT 改 IP 地址),TCP 校验和是端到端验证的最后防线。如果需要更强的完整性保证,要用 TLS 的 MAC 或应用层哈希。写段代码# 模拟 TCP 序列号和累积确认def simulate_seq(): sent = 0 # 下一个要发的字节号(ISN=0 简化) acked = 0 # 已确认的字节号 segments = [(0, 100), (100, 200), (200, 300)] # (起始, 结束) for start, end in segments: print(f"发送: seq={start}, 数据长度={end-start}") # 模拟中间段丢失 print(f"收到: ACK={100} (第1段确认)") print(f"收到: ACK={100} (第2段丢失,重复ACK)") print(f"收到: ACK={100} (第3个重复ACK→快重传)") print(f"重传: seq={100}, 数据长度=100") print(f"收到: ACK={300} (累积确认到第3段)")simulate_seq()
计算机基础阅读 05月29日 00:11

TCP 四次挥手的过程是什么?为什么不能三次挥手?

TCP 四次挥手是断开连接的过程:主动方发 FIN,被动方回 ACK,被动方再发 FIN,主动方回 ACK。之所以要四次而不是三次,是因为 TCP 全双工——每个方向必须单独关闭。第二次挥手时被动方可能还有数据没发完,不能把 FIN 和 ACK 合并,只能先确认,等数据发完再关自己的方向。追问TIME_WAIT 为什么要等 2MSL?两个原因。第一,确保最后的 ACK 能到——如果 ACK 丢了,被动方会重传 FIN,TIME_WAIT 状态的主动方可以重新回 ACK。如果主动方直接 CLOSED,收到重传的 FIN 只能回 RST,被动方会报连接异常。第二,让网络中属于这个连接的旧报文段全部消亡——MSL 是报文最大生存时间(RFC 793 建议 2 分钟,Linux 实际是 60 秒),等 2MSL 确保来回两个方向的旧包都过期,不会干扰后续新连接。TIME_WAIT 过多怎么办?短连接场景下(比如 HTTP/1.0 每个请求一个连接),主动关闭方会积累大量 TIMEWAIT,每个占一个五元组(源IP、源端口、目的IP、目的端口、协议),端口耗尽后新连接无法建立。解决方法:启用 tcp_tw_reuse(允许 TIMEWAIT 端口给新连接用,依赖时间戳判断旧包)、改用长连接(HTTP/1.1 Keep-Alive)、让客户端主动关闭(服务端被动关闭不产生 TIME_WAIT)。注意 tcp_tw_recycle 在 NAT 环境下会导致连接失败,Linux 4.12 后已移除此选项。为什么不能三次挥手?假设第二次和第三次合并——被动方收到 FIN 后立刻发 FIN+ACK。问题是被动方可能还有数据没发完:它收到主动方的 FIN 只是说"我不发了",不代表"你也不能发了"。被动方需要继续发送剩余数据,发完才能关自己的方向。如果提前发 FIN,这些数据就丢了。只有在被动方恰好也没有数据要发时,才能合并成三次挥手——这就是延迟 ACK 场景下偶尔观察到三次挥手的原因。CLOSE_WAIT 很多是什么问题?CLOSEWAIT 是被动方收到 FIN 后、还没发 FIN 之前的状态。大量 CLOSEWAIT 说明应用层没有调用 close()——通常是因为代码 bug:忘了关 socket,或者线程池满了来不及处理。一个典型的坑是:HTTP 服务端在请求处理异常时没关连接,客户端超时断开后,服务端停留在 CLOSEWAIT。排查方法:netstat 统计 CLOSEWAIT 数量,配合 lsof 找到对应进程,检查代码里的 socket 关闭逻辑。写段代码# 查看系统 TCP 连接状态分布import subprocessresult = subprocess.run(['netstat', '-tan'], capture_output=True, text=True)states = {}for line in result.stdout.splitlines()[2:]: parts = line.split() if len(parts) >= 6: state = parts[5] states[state] = states.get(state, 0) + 1for state, count in sorted(states.items(), key=lambda x: -x[1]): print(f"{state:20s} {count}")
计算机基础阅读 05月29日 00:10

TCP 拥塞控制的四个算法是什么?超时重传和快重传怎么区分?

TCP 拥塞控制有四个算法:慢启动、拥塞避免、快重传、快恢复。核心思想是"先试探再加码"——不清楚网络能承受多少时就少发,确认没问题再加速;一旦发现拥塞就立刻降速。控制对象是 cwnd(拥塞窗口),和 rwnd 取较小值作为实际发送窗口。追问慢启动为什么叫"慢"?明明指数增长很快"慢"是相对没有拥塞控制的情况——TCP 早期一上来就发满窗口,容易打爆网络。慢启动从 cwnd=1 MSS 开始,每收到一个 ACK 就 +1 MSS,一个 RTT 内翻倍(1→2→4→8→16)。看起来快,但初始只有 1 MSS,比直接发满窗口保守得多。cwnd 到达 ssthresh 后切换到拥塞避免的线性增长(每 RTT +1 MSS),不再翻倍。ssthresh 的初始值通常很大,第一次丢包后才设为 cwnd/2。3 个重复 ACK 为什么就重传?不等超时吗?等超时太慢了。RTO 通常是几百毫秒到几秒,而 3 个重复 ACK 说明后续包已经到了,只是中间那个丢了——网络还在工作,只是丢了一个包。快重传收到第 3 个重复 ACK 就立即重传,不用等 RTO,恢复速度差了一个数量级。为什么是 3 个不是 1 个?因为偶尔乱序也会产生重复 ACK,1 个就重传太激进,3 个基本确认是丢包。超时重传和快重传的处理有什么不同?超时重传意味着网络可能严重拥塞——连 ACK 都回不来了,所以处理更激进:ssthresh = cwnd/2,cwnd 直接降到 1 MSS,重新慢启动。快重传说明网络还通着(后续包到了),只是丢了一个包,所以处理温和:ssthresh = cwnd/2,cwnd 降到 ssthresh 而不是 1,进入快恢复。这就是为什么快重传后不回慢启动——丢一个包不代表网络崩溃。BBR 和传统拥塞控制有什么不同?传统算法(Reno/Cubic)基于丢包判断拥塞——丢包就降速,不丢就加速。问题是在高延迟高丢包的网络(无线、跨国)里,丢包不一定是拥塞,可能是链路本身的特性,传统算法会白白降速。BBR(Google 2016 年提出)不看病包,直接测量带宽和 RTT,把发送速率调整到带宽-延迟积(BDP),目标是把管道填满但不溢出。实测:YouTube 启用 BBR 后跨洲链路吞吐量提升 10-100 倍。写段代码# 模拟慢启动和拥塞避免的 cwnd 变化def simulate_cwnd(ssthresh=16, max_rtt=20): cwnd = 1 # 初始 1 MSS for rtt in range(1, max_rtt + 1): if cwnd < ssthresh: cwnd *= 2 # 慢启动:指数增长 phase = "慢启动" else: cwnd += 1 # 拥塞避免:线性增长 phase = "拥塞避免" print(f"RTT {rtt:2d}: cwnd={cwnd:3d} MSS [{phase}]")simulate_cwnd()
计算机基础阅读 05月29日 00:09

TCP 滑动窗口机制是怎么工作的?零窗口和糊涂窗口是什么?

TCP 流量控制靠滑动窗口:接收方在 ACK 里通告自己还能接收多少数据(rwnd),发送方据此控制发送量,不让接收方缓冲区溢出。发送窗口 = min(rwnd, cwnd),rwnd 是接收方给的流量控制信号,cwnd 是发送方自己根据拥塞状况算的。这里只说 rwnd 的部分。追问零窗口是怎么回事?怎么恢复?接收方缓冲区满了,在 ACK 里通告 rwnd=0,发送方就必须停。但接收方应用层读完数据释放缓冲区后,会发一个窗口更新报文告诉发送方可以继续了。问题在于:这个窗口更新报文丢了怎么办?发送方会一直等,死锁。解决方法是发送方启动持续计时器,定期发零窗口探测报文(只含 1 字节数据),迫使接收方回 ACK 并带上最新窗口值。这就是为什么零窗口不会永远卡住。糊涂窗口综合征是什么?接收方应用层每次只从缓冲区读 1 字节,就通告 rwnd=1;发送方就发 1 字节数据——一个 41 字节的包只装了 1 字节有效载荷,带宽利用率 2.4%。这就是糊涂窗口综合征(Silly Window Syndrome)。接收端的解决方法叫 Clark 方案:缓冲区空闲不到 MSS 或总缓冲区的 35% 时不通告新窗口;发送端配合 Nagle 算法,攒够数据再发。两端一起防才能根治。rwnd 和 cwnd 有什么区别?rwnd 是接收方告诉发送方"我还能收多少",防止接收方溢出;cwnd 是发送方自己算的"网络还能承受多少",防止网络拥塞。流量控制(rwnd)是端到端的保护,拥塞控制(cwnd)是全局性的保护。实际发送窗口取两者最小值——任何一个窗口满了都不能多发。面试中经常把这两个混在一起考,要分清楚哪个是对方给的,哪个是自己算的。滑动窗口的"滑动"体现在哪?窗口把发送缓冲区的数据分成四段:已确认 ← 窗口左边界 | 已发送未确认 | 可发送未发送 | 窗口右边界 → 不可发送。收到 ACK 后左边界右移(滑过去),接收方通告新窗口后右边界右移(窗口打开)——这就是"滑动"。如果右边界不变就是窗口关闭,左边界追上右边界就是零窗口。写段代码# 模拟滑动窗口发送def sliding_window_send(data, window_size): sent = 0 # 窗口左边界 acked = 0 # 已确认 while acked < len(data): # 发送窗口内的数据 while sent < min(acked + window_size, len(data)): print(f"发送 data[{sent}]") sent += 1 # 模拟收到 ACK print(f"收到 ACK={acked+1}") acked += 1 # 左边界滑动sliding_window_send(list(range(8)), window_size=3)
计算机基础阅读 05月29日 00:02

TCP Keep-Alive 机制是什么?为什么还需要应用层心跳?

TCP Keep-Alive 是操作系统提供的连接存活检测机制:连接空闲一段时间后,内核自动发探测包,根据对端响应判断连接是否还活着。三个核心参数控制行为——空闲多久开始探测(tcpkeepalivetime,默认 7200 秒)、探测间隔(tcpkeepaliveintvl,默认 75 秒)、探测几次放弃(tcpkeepaliveprobes,默认 9 次)。最差情况下,从连接断开到被检测出来要 7200 + 75×9 = 7875 秒,超过 2 小时。追问为什么默认 2 小时这么长?RFC 1122 建议至少 2 小时,是出于对网络风暴的担忧——如果全网所有连接都以短间隔发探测包,本身就是一场 DDoS。2 小时在服务器间稳定网络里够用了,问题出在移动端:运营商 NAT 设备的连接跟踪表有限,空闲 5 分钟(移动 2/3G)到 28 分钟(电信 3G)就淘汰条目,连接就被静默丢弃了,2 小时探测根本来不及救。既然有 Keep-Alive,为什么还要应用层心跳?三个原因。第一,Keep-Alive 只能检测连接是否可达,不能检测对端进程是否卡死——进程死锁时 TCP 连接还活着,Keep-Alive 照样通过。第二,Keep-Alive 的探测包不带业务数据,服务端对探测无感知,无法在应用层做状态同步。第三,参数是系统级的,改了影响所有连接,不如应用层心跳可以按业务精细控制间隔。微信的心跳从 30 秒到 300 秒动态调整,Keep-Alive 做不到。Keep-Alive 探测包长什么样?一个不包含数据的 ACK 包,序列号设为对端期望的序列号减 1,这样对端会发现序列号不匹配,回一个 ACK 带上正确的期望序列号——探测就成功了。如果对端回复 RST,说明进程已崩溃重启;如果连续 9 次无响应,内核判定连接死亡,关闭 socket 并返回 ETIMEDOUT。什么场景下 Keep-Alive 就够了?服务器之间的稳定内网连接。比如微服务间 gRPC 长连接、数据库连接池,网络环境可控,不存在 NAT 超时问题,Keep-Alive 配合较短的探测间隔(比如 60 秒)就能及时清理僵死连接。这些场景不需要应用层心跳的灵活性。写段代码import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) # 60秒后开始探测sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) # 每10秒探测一次sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3) # 探测3次放弃
计算机基础阅读 05月29日 00:00

什么是 TCP Nagle 算法?为什么会造成 40ms 延迟?

Nagle 算法的核心规则只有一条:连接上有未被确认的小包时,不再发新的小包,等 ACK 回来再把缓冲区里的数据攒一起发。目的是减少小包数量——Telnet 按一个键就产生一个 41 字节的包,其中 40 字节是 TCP+IP 头部,有效载荷只有 1 字节,带宽利用率不到 3%。1984 年 John Nagle 在 RFC 896 里提出这个方案,解决的就是交互式应用疯狂发小包导致的广域网拥塞。算法默认开启(RFC 1122 推荐),通过 TCP_NODELAY 选项关闭。追问Nagle 和延迟 ACK 怎么会互相卡死?Nagle 在发送端等 ACK,延迟 ACK 在接收端等更多数据再确认,两者同时启用就形成僵持:发送方写了一个小包,等 ACK;接收方收到后不马上回 ACK,等 40ms(Linux 默认)或 200ms(Windows 默认)看还有没有后续数据。典型场景是 write-write-read 模式:第一次写直接发出,第二次写被 Nagle 挡住,接收端延迟 ACK 等 40ms,发送端就卡在这 40ms 上。腾讯云有实际案例,营销平台 10% 的请求耗时稳定卡在 38-42ms,根因就是这对组合。什么时候必须关掉 Nagle?实时交互场景:游戏同步、远程桌面、WebSocket 推送。这些场景宁可多发几个小包也不能容忍额外延迟。Redis 3.x 的主从同步曾因 Nagle 导致从库延迟飙升,后来在源码里给同步 socket 加了 TCP_NODELAY 才解决。Nagle 和 TCP_CORK 有什么区别?Nagle 是"有小包没确认就不发",侧重减少小包数量;CORK 是"攒够一个 MSS 再发",侧重提高吞吐量。CORK 更激进——把数据一直攒到 MSS 或超时(通常 200ms)才放行,适合 HTTP 流水线这类一次要发大量数据的场景。Nginx 在发送响应头时用 CORK,等响应体也凑齐了一起发,减少系统调用次数。怎么确认线上问题是 Nagle 引起的?抓包看时序:发送端有小包发出后,超过 40ms 才收到 ACK 或才发下一个包,大概率是 Nagle + 延迟 ACK。也可以临时在客户端设 TCP_NODELAY 做对比——如果 40ms 档消失了就确认了。注意要区分是 Nagle 的问题还是单纯的网络延迟,对比开关前后的延迟分布比看绝对值更可靠。写段代码// 禁用 Nagle 算法int flag = 1;setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));// Linux 上启用快速 ACK(接收端)setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag));
计算机基础阅读 05月28日 09:26

什么是 CDN 边缘计算?有哪些应用场景?

CDN 边缘计算是什么?CDN 边缘计算是将计算能力从中心化源站下沉到 CDN 边缘节点,在靠近用户的网络边缘执行计算任务的一种架构模式。传统 CDN 只做静态内容缓存,而边缘计算让边缘节点具备了运行业务逻辑的能力——请求不必回源,直接在边缘节点处理并返回结果。两者的核心区别:CDN 解决的是"内容离用户更近"的问题,边缘计算解决的是"计算离用户更近"的问题。当两者结合,边缘节点既能缓存静态资源,又能执行动态计算,形成完整的边缘服务能力。为什么需要边缘计算?延迟:从数百毫秒到数十毫秒传统架构下,动态请求必须回源处理:用户 → 边缘节点 → 源站(可能跨区域)→ 返回,端到端延迟通常 200-500ms。边缘计算将计算逻辑部署到边缘节点,请求在本地完成处理,延迟可降至 10-50ms。对于实时交互场景(在线游戏、金融交易、视频直播),这几十到几百毫秒的差异直接影响用户体验和业务指标。回源压力与带宽成本没有边缘计算时,所有动态请求都打到源站,源站需要为每个请求分配 CPU、内存和网络资源。引入边缘计算后,大部分请求在边缘节点就地处理,只有少数需要中心数据的请求才回源。以 Cloudflare Workers 为例,官方数据显示边缘处理可节省至少 30% 的回源流量成本。数据合规与隐私各国数据本地化法规(如欧盟 GDPR、中国《数据安全法》)要求用户数据在本地处理。边缘计算让数据在产生地就近处理,减少跨境传输,既满足合规要求,又降低了数据泄露风险。核心应用场景1. API 网关与请求路由在边缘节点部署路由规则,根据 URL 路径、请求头、用户地域等信息将请求分发到不同的后端服务。好处是路由决策在离用户最近的位置完成,避免了不必要的回源往返。// Cloudflare Workers: 边缘 API 路由export default { async fetch(request) { const url = new URL(request.url); if (url.pathname.startsWith("/api/v1")) { return fetch("https://api-v1.example.com" + url.pathname + url.search); } if (url.pathname.startsWith("/api/v2")) { return fetch("https://api-v2.example.com" + url.pathname + url.search); } return new Response("Not Found", { status: 404 }); }};2. A/B 测试与个性化内容在边缘节点根据用户特征(地域、设备、Cookie 等)决定返回哪个版本的页面或接口数据。相比在源站做 A/B 分流,边缘分流避免了回源延迟,用户感知几乎为零。这也是电商、媒体平台常用的策略。// 边缘 A/B 测试export default { async fetch(request) { const country = request.cf?.country; if (country === "CN") { return fetch("https://cn-version.example.com" + new URL(request.url).pathname); } return fetch("https://global-version.example.com" + new URL(request.url).pathname); }};3. 图片与视频实时处理在边缘节点对图片进行缩放、裁剪、格式转换(WebP/AVIF),对视频流做转码或画质优化。请求到达边缘时,如果缓存中已有处理后的版本直接返回;没有则从源站取原始文件,在边缘处理后缓存并返回。这样源站只需存储一份原图,由边缘按需处理不同尺寸和格式。4. 认证鉴权将 JWT 验证、API Key 校验、权限检查等逻辑下沉到边缘节点。无效请求在边缘就被拦截,不会浪费源站资源。这对于高并发场景(秒杀、抢购)尤其重要,可以在边缘挡住大量未授权请求。5. 限流与防爬在边缘节点基于 IP、用户标识、请求频率等维度实施限流策略,识别并拦截爬虫和恶意请求。相比在源站做限流,边缘限流在请求进入内部网络前就完成过滤,防护效果更好。// 边缘限流示意(基于 IP)const rateLimiter = new Map();export default { async fetch(request) { const ip = request.headers.get("CF-Connecting-IP"); const now = Date.now(); const record = rateLimiter.get(ip); if (record && now < record.resetTime && record.count >= 100) { return new Response("Too Many Requests", { status: 429 }); } rateLimiter.set(ip, { count: (record?.count || 0) + 1, resetTime: now + 60000 }); return fetch(request); }};6. 数据聚合与智能缓存多个后端服务的响应可以在边缘节点聚合后统一返回给前端,减少前端的请求数量。同时,边缘节点可以对聚合结果设置智能缓存策略,后续相同请求直接返回缓存,无需再次回源。主流平台对比| 平台 | 运行时 | 语言支持 | 特点 ||------|--------|----------|------|| Cloudflare Workers | V8 隔离 | JS/TS/WASM | 全球节点最多(300+),免费额度慷慨 || AWS Lambda@Edge | Node.js/Python | JS/Python | 与 CloudFront + AWS 生态深度集成 || Fastly Compute@Edge | WASM | Rust/C++/JS | 基于 WebAssembly,冷启动极快 || Vercel Edge Functions | V8 隔离 | JS/TS | 与 Next.js 无缝配合 |选型建议:如果你需要全球覆盖和独立部署,选 Cloudflare Workers;如果技术栈在 AWS 生态内,选 Lambda@Edge;如果追求极致性能和冷启动速度,考虑 Fastly;如果是 Next.js 项目,Vercel Edge Functions 是最省事的选择。边缘计算的设计原则无状态优先:边缘节点随时可能被销毁或迁移,不要依赖本地变量或文件系统存储状态数据,应使用 KV 存储(如 Cloudflare KV)或外部数据库。优雅降级:边缘计算可能出现超时、节点不可用等情况,必须设计降级策略——返回缓存数据、展示兜底内容、或降级到源站处理,而不是直接报错。控制执行时间:各平台对边缘函数的执行时间有严格限制(Cloudflare Workers 免费版 CPU 时间 10ms,付费版 50ms;Lambda@Edge 超时 5-30s)。代码要尽量轻量,避免在边缘做重计算。冷启动问题:边缘函数在首次请求时会经历冷启动(加载代码 → 初始化运行时 → 执行函数)。保持代码体积小、减少外部依赖,可以有效缩短冷启动时间。面试回答要点回答"什么是 CDN 边缘计算"时,按以下逻辑组织:先给定义:CDN 边缘计算是把计算逻辑从中心化源站下沉到 CDN 边缘节点,在靠近用户的位置处理请求说清价值:降低延迟(200ms → 50ms)、减少回源流量(节省 30%+)、满足数据合规要求列举场景:API 路由、A/B 测试、图片处理、认证鉴权、限流防爬、数据聚合提到平台:Cloudflare Workers / Lambda@Edge / Fastly,能说出各自的适用场景指出挑战:无状态设计、执行时间限制、冷启动、调试困难可能的追问及应对:边缘计算和 Serverless 有什么区别? Serverless 是一种部署和计费模式,边缘计算是一种架构模式。边缘计算通常采用 Serverless 的方式部署,但 Serverless 不一定运行在边缘。边缘计算适合所有场景吗? 不适合。需要访问中心数据库的复杂查询、涉及大量数据的批处理、对一致性要求高的事务型操作,仍然应该放在源站或云端处理。边缘节点之间数据如何同步? 通常通过分布式 KV 存储(如 Cloudflare KV)实现最终一致性,不适合强一致性场景。
计算机基础阅读 05月28日 09:24

CDN 的负载均衡策略有哪些?如何实现 CDN 的高可用?

面试核心结论CDN 负载均衡的核心策略分为两层:全局调度层(GSLB/DNS)决定用户访问哪个边缘节点,本地均衡层(L4/L7 LB)决定节点内部请求分发到哪台服务器。主要策略包括地理位置路由、就近性路由、轮询、加权轮询、最少连接和一致性哈希。实现高可用的关键在于:健康检查 + 故障自动转移 + 熔断降级 + 多活冗余,缺一不可。追问方向:GSLB 的 DNS 调度流程是怎样的?一致性哈希在 CDN 缓存中为什么重要?Anycast 和 DNS 调度有什么区别?CDN 负载均衡的两层架构理解 CDN 负载均衡,首先要分清两层调度:全局调度(GSLB):当用户发起请求时,DNS 解析通过 CNAME 指向 CDN 的 GSLB,GSLB 根据用户 IP、节点负载、网络质量等选择最优边缘节点,返回该节点的 IP。这一层决定"用户去哪个机房"。本地负载均衡(SLB):请求到达边缘节点后,节点内部的负载均衡器(Nginx、HAProxy 等)将请求分发到后端的缓存服务器集群。这一层决定"请求去哪台机器"。面试时很多候选人只讲本地负载均衡,忽略了 GSLB 这一层,这是不够的。CDN 的核心优势——就近接入,正是由 GSLB 实现的。六种核心负载均衡策略地理位置路由(Geo-based Routing)根据用户 IP 所属地域,将请求调度到最近的边缘节点。这是 CDN 最基础也最常用的全局调度策略。实现机制:GSLB 维护一张 IP 地址段到地理位置的映射表(GeoIP 数据库),用户 DNS 请求到达后,查询映射表确定用户所在区域,返回该区域对应节点的 IP。局限性:地理位置近不等于网络延迟低。比如跨运营商访问时,物理距离近但网络绕行严重。因此通常和就近性路由配合使用。就近性路由(Proximity-based Routing)不依赖地理数据,而是通过实际网络测量选择最优节点。测量方式:主动探测:GSLB 定期从各节点向探测点发送 ICMP/TCP 探测,收集 RTT 数据被动测量:分析实际用户请求的响应时间,统计各节点的真实服务质量混合模式:主动探测提供基线数据,被动测量做实时修正实际生产中混合模式最常见。纯主动探测有探测盲区,纯被动测量冷启动阶段没数据。一致性哈希(Consistent Hashing)在 CDN 场景中,一致性哈希的核心价值是提高缓存命中率。为什么重要:如果用轮询策略,同一 URL 的请求可能落在不同缓存服务器上,导致重复回源。用 URL 做一致性哈希,相同 URL 始终路由到同一台服务器,缓存只需存一份。关键参数:虚拟节点数。虚拟节点越多,数据分布越均匀,但管理开销也越大。通常设置 100-200 个虚拟节点。节点变更影响:当节点增减时,一致性哈希只影响相邻虚拟节点上的数据,不会全盘重新分配。相比取模哈希(节点变化时全部重分布),这是巨大优势。# Nginx 一致性哈希配置upstream cdn_cache { hash $request_uri consistent; server 10.0.0.1:8080; server 10.0.0.2:8080; server 10.0.0.3:8080;}加权轮询(Weighted Round Robin)为不同性能的服务器分配不同权重,高性能服务器承载更多请求。upstream cdn_nodes { server 10.0.0.1:8080 weight=5; # 32核 64G 高配 server 10.0.0.2:8080 weight=3; # 16核 32G 中配 server 10.0.0.3:8080 weight=1; # 8核 16G 低配}权重不是一成不变的。成熟的 CDN 系统会根据服务器实时负载(CPU、内存、连接数)动态调整权重,这叫动态权重调整。最少连接(Least Connections)将新请求分发给当前活跃连接数最少的服务器。相比轮询,它适合请求处理时间差异大的场景——轮询只看"分了几个",最少连接看"还剩多少余力"。upstream cdn_nodes { least_conn; server 10.0.0.1:8080; server 10.0.0.2:8080; server 10.0.0.3:8080;}IP 哈希(IP Hash)用客户端 IP 做哈希,保证同一用户的请求始终落到同一台服务器。主要用于会话保持场景,但在 CDN 中不如 URL 哈希常用,因为 CDN 主要是无状态的内容分发。健康检查:高可用的基础没有可靠的健康检查,负载均衡就是瞎指挥。健康检查分两种:主动健康检查LB 定期向后端发送探测请求,判断服务是否正常:# Nginx Plus 主动健康检查upstream cdn_nodes { zone health 64k; server 10.0.0.1:8080; server 10.0.0.2:8080; health_check interval=5s fails=3 passes=2 uri=/health;}关键参数:interval:探测间隔,正常节点 5-10s,异常节点缩短到 1-2sfails:连续失败几次标记为不健康passes:连续成功几次恢复为健康被动健康检查基于实际业务请求的响应判断,不额外发探测:upstream cdn_nodes { server 10.0.0.1:8080 max_fails=3 fail_timeout=30s; server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;}max_fails=3 fail_timeout=30s 表示 30 秒内失败 3 次,该节点被标记不可用 30 秒。生产实践:两种方式配合使用。主动检查发现硬故障(进程挂了、端口不通),被动检查发现软故障(响应变慢、错误率升高)。高可用架构的关键机制故障自动转移当节点被标记不健康后,负载均衡器自动将流量切到健康节点。关键问题是切换速度:DNS 级别的 GSLB 切换依赖 TTL,即使设置很短的 TTL(如 30s),仍有本地 DNS 缓存问题,实际切换可能需要数分钟Anycast 方式切换更快,因为 BGP 路由变化可在秒级生效本地 SLB 切换最快,通常在 1-2 个探测周期内完成熔断与降级当后端持续异常时,快速失败比慢慢等更好:熔断:错误率超过阈值(如 50%),直接断开,不再向该节点发请求,等一段时间后半开探测降级:返回缓存内容(stale-while-revalidate)、返回简化版本、或者直接拒绝(比超时等待体验更好)# Nginx 熔断配置思路proxy_connect_timeout 2s;proxy_read_timeout 5s;proxy_next_upstream error timeout http_502 http_503;proxy_next_upstream_timeout 10s;proxy_next_upstream_tries 2;proxy_next_upstream 实现了请求级别的自动重试和故障转移。多活与冗余单点永远不可靠。CDN 的高可用需要多层次的冗余:节点级冗余:每个区域至少部署 2 个边缘节点,互为备份服务器级冗余:节点内部至少 2 台缓存服务器网络级冗余:多运营商接入(BGP 多线),避免单运营商故障DNS 级冗余:多组权威 DNS,避免 DNS 服务本身成为单点Anycast 与 DNS 调度的对比这是面试中经常被追问的进阶问题:| 维度 | DNS 调度(GSLB) | Anycast ||------|-------------------|---------|| 切换速度 | 分钟级(受 TTL 和缓存影响) | 秒级(BGP 路由收敛) || 精度 | 基于 IP 库,有偏差 | 基于网络拓扑,更精准 || 运维复杂度 | 相对简单 | 需要 ASN 和 BGP 配置 || 适用场景 | 大多数 CDN | 对延迟敏感、需快速故障转移 || 缓存问题 | 本地 DNS 缓存导致调度不准 | 无此问题 |大型 CDN(Cloudflare、Akamai)通常两种方式结合:Anycast 做全局入口,DNS 调度做精细化区域分配。监控指标与告警负载均衡不是配置完就完事了,需要持续监控:核心指标:P50/P95/P99 响应时间:关注长尾延迟,P99 比 P50 更能反映用户体验下限错误率:5xx 比例超过 0.1% 就需要告警缓存命中率:低于 80% 说明负载均衡策略或缓存策略有问题节点可用性:目标 99.99%(年停机约 52 分钟)告警分级:P1:节点完全不可用 → 立即处理,自动故障转移P2:错误率 > 1% 或 P99 > 5s → 5 分钟内响应P3:缓存命中率下降 > 10% → 工单跟进面试回答要点回答这个问题的正确姿势:先说两层架构:全局调度(GSLB/Anycast)+ 本地均衡(L4/L7),展示你对 CDN 整体架构的理解策略选讲 3-4 个:地理位置路由、一致性哈希、最少连接、加权轮询,每个说清楚适用场景和取舍高可用讲机制链:健康检查 → 故障转移 → 熔断降级 → 多活冗余,形成完整链路举实际例子:比如"我们线上遇到过 DNS 缓存导致 GSLB 切换慢的问题,后来加了 Anycast 做补充",比纯理论更有说服力提 Anycast:很多人会忽略这个,提了就是加分项
计算机基础阅读 05月28日 09:23

什么是CDN回源?如何减少回源请求?

CDN 回源是什么?回源(Origin Pull)是指 CDN 边缘节点没有缓存用户请求的内容时,向源站(Origin Server)请求资源的过程。简单来说:用户请求 → CDN 没有缓存 → CDN 去源站拿 → 缓存一份再返回给用户。回源是 CDN 机制中不可避免的环节,但回源率过高会直接拖慢响应速度、压垮源站、推高成本。面试中考察这个点,核心是看你能不能从"理解机制"到"控制回源"形成完整闭环。回源在什么情况下触发?缓存未命中最常见的回源原因,包含三种典型场景:首次访问:资源从未被任何边缘节点缓存过,冷启动必然回源缓存过期:资源超过 TTL(Time To Live)过期时间,CDN 必须重新验证或拉取缓存被清除:主动刷新(Purge)或被动淘汰(LRU 内存不足)导致缓存消失缓存键不匹配URL 相同但参数不同,CDN 可能将其视为不同资源:https://example.com/api/data?version=1https://example.com/api/data?version=2// 若未配置忽略 version 参数,两次请求各自缓存,回源翻倍生产环境中,UTM 参数、时间戳、随机 token 是导致缓存键膨胀的三大元凶。请求类型或头部触发回源POST/PUT/DELETE:写操作通常不缓存,直接回源携带 Authorization/Cookie:CDN 默认不缓存带鉴权头的响应动态内容:业务规则标记为不可缓存的路径(如 /api/、/user/)回源对系统有什么影响?延迟:从毫秒到秒级的跨越缓存命中:用户 → 边缘节点,通常 < 50ms回源请求:用户 → 边缘节点 → 源站 → 边缘节点 → 用户,延迟 200ms~1s+回源率每升高 10%,P99 延迟可能翻倍。高并发场景下,回源还会引发排队效应,延迟雪崩式恶化。源站压力:回源风暴的风险当热点资源同时过期,多个边缘节点会同时向源站发起请求,这就是回源风暴(Thundering Herd)。极端情况下,10 个边缘节点 × 每秒 1000 次 = 源站瞬间承受 10000 QPS,可能导致源站宕机。成本:回源带宽是真金白银CDN 回源流量通常按带宽计费,价格高于普通 CDN 出流量源站需要更高配置承受回源压力超出套餐配额的回源流量费用更高如何减少回源?六大核心策略策略一:合理设置缓存 TTL不同类型资源设置不同的过期时间:// 静态资源:长 TTL + immutableCache-Control: public, max-age=31536000, immutable// 半静态内容:中等 TTLCache-Control: public, max-age=3600// 动态 API:短 TTL + stale-while-revalidateCache-Control: public, max-age=10, stale-while-revalidate=60stale-while-revalidate 是一个容易被忽视的关键指令:它允许 CDN 在后台异步刷新缓存的同时,先返回过期的旧内容给用户。既保证响应速度,又保证内容最终一致。配合 URL 版本化更新静态资源,彻底避免因内容更新导致的缓存失效:// 版本化文件名,内容变更即更换 URL/app.v1.a1b2c3.js/app.v2.d4e5f6.js策略二:缓存预热(Prefetch)在用户访问前,主动将内容推送到 CDN 节点:# 通过 CDN API 预热 URLcurl -X POST "https://cdn-api.example.com/prefetch" \ -H "Authorization: Bearer <token>" \ -d "{\"urls\": [\"https://example.com/new-page.html\", \"https://example.com/bundle.js\"]}"预热适用场景:新版本发布前预热静态资源大促活动前预热商品页面热门内容预测性预热(基于历史访问模式)策略三:配置缓存键优化忽略无关查询参数,让更多请求命中同一缓存:// 配置忽略 utm_source、timestamp 等参数https://example.com/data?utm_source=wechat&ts=123https://example.com/data?utm_source=weibo&ts=456// 忽略参数后,两个请求命中同一份缓存规范化请求头:统一 Vary 头,避免因 Accept-Encoding 差异产生重复缓存。策略四:多级缓存架构CDN 本身就有多级缓存结构,合理利用可以大幅减少回源:边缘缓存(L1):离用户最近,容量小,响应最快区域缓存(L2):覆盖一个地理区域,容量中等中心缓存 / Origin Shield(L3):所有边缘节点的回源汇聚点Origin Shield 是 AWS CloudFront 的叫法,Akamai 称为 Origin Shielding,阿里云叫集中回源。核心原理:在源站前再加一层缓存,即使所有边缘节点都 miss,也只需一次回源到 Shield 层,避免多节点直接打源站。Akamai 的数据显示,Origin Shield 可以减少 95% 的源站请求。策略五:请求合并(Request Coalescing)当多个用户同时请求同一份未缓存的资源时,CDN 只向源站发一次请求,其余请求排队等待同一份响应。这个机制叫做请求合并或 Request Collapsing。这是解决回源风暴的核心手段。没有请求合并,1000 个并发 miss 请求 = 1000 次回源;有了请求合并,= 1 次回源。策略六:边缘计算将简单逻辑下沉到 CDN 边缘节点执行,避免回源:A/B 测试分流:在边缘节点根据 Cookie 分配实验组URL 重写:边缘节点直接改写 URL,无需回源简单 API 聚合:边缘节点拼接多个缓存片段返回地理定向:根据用户 IP 返回不同区域的缓存内容面试追问与回答追问 1:缓存穿透、缓存击穿、缓存雪崩分别是什么?和回源有什么关系?穿透:请求的数据源站也不存在,每次都回源且永远无法缓存。解决:缓存空值或用 Bloom Filter 拦截击穿:热点 key 过期瞬间,大量请求同时回源。解决:请求合并 + 互斥锁回源雪崩:大批 key 同时过期,回源量暴增。解决:TTL 加随机偏移 + stale-while-revalidate追问 2:如何监控回源率?合理范围是多少?核心指标:回源率 = 回源请求数 / 总请求数,目标 < 5%回源带宽占比 = 回源流量 / 总出流量回源延迟 P99通过 CDN 控制台或日志分析工具(如 ELK)监控,设置回源率 > 10% 的告警阈值。追问 3:CDN 回源和源站宕机如何处理?配置 stale-if-error 指令,源站不可用时返回过期缓存设置源站健康检查,自动切换到备用源站多源站负载均衡 + 故障自动摘除关键业务配置 Origin Shield 作为缓冲层
计算机基础阅读 05月28日 08:26

CDN 成本过高如何优化?

CDN 账单为什么这么高?一家中型互联网公司,业务量稳定增长,但 CDN 月费从 8 万涨到了 28 万,涨幅远超业务增速。复盘后发现:缓存命中率只有 72%,图片未做格式转换,视频全部用 H.264 编码,没有做任何成本管控。经过一轮系统优化,月费降回了 11 万。CDN 成本优化不是省钱,是花该花的钱。下面从成本构成、六大核心优化策略、监控体系三个层面讲清楚。CDN 成本由哪些部分组成?理解成本结构是优化的前提。CDN 的费用主要来自五个方面:| 费用类型 | 计费方式 | 占比(典型场景) ||---------|---------|--------------|| 流量/带宽 | 按流量(GB)或按带宽峰值(Mbps) | 60%-70% || 请求次数 | 按千次请求计费 | 10%-15% || HTTPS 证书 | 按证书数量或域名数 | 3%-5% || 安全防护(WAF/DDoS) | 按规则数或防护流量 | 5%-10% || 边缘计算/视频处理 | 按调用次数或处理时长 | 5%-10% |流量/带宽是绝对大头,优化重心应该放在这里。计费方式的选择也很关键:流量平稳的业务选按流量计费更划算,有明显峰值波动的(如直播、促销)选按带宽峰值第五计费(95 带宽)更合适。策略一:缓存命中率提升到 95% 以上缓存命中率每提升 1 个百分点,回源流量大约减少 3%-5%。命中率从 72% 提升到 95%,仅此一项就能节省 30% 以上的流量成本。关键配置:# 静态资源:长缓存 + immutableCache-Control: public, max-age=31536000, immutable# API 响应:短缓存,CDN 侧可缓存更久Cache-Control: public, max-age=60, s-maxage=300# 用户敏感数据:禁止缓存Cache-Control: no-store缓存键优化也很容易被忽略。默认缓存键会包含所有查询参数,但 ?utm_source=xxx 这类追踪参数不影响内容,应该忽略:# 忽略不影响内容的查询参数proxy_cache_key "$scheme$request_method$host$uri";资源版本化是避免缓存失效的常用手段。不要用 style.css?v=2,改成 style.v2.css,旧版本自然过期,不需要手动刷新缓存。缓存预热适合内容发布场景。新页面上线前,主动将关键资源推送到边缘节点,避免用户首次访问时回源。大部分 CDN 厂商都提供了预热 API,可以在 CI/CD 发布流程中自动调用。缓存一致性是面试常问的追问点。常见方案有三种:一是 TTL 自然过期(最简单,适合容忍短暂不一致的场景);二是主动 Purge(适合内容更新后必须立即生效的场景,但频繁 Purge 会降低命中率);三是版本化 URL(改 URL 不改内容,最推荐)。实际生产中通常组合使用:静态资源用版本化 URL,动态内容用短 TTL + 关键更新时 Purge。策略二:内容压缩和格式转换图片优化:单此一项可减少 50%-70% 流量| 格式 | 适用场景 | 相比 JPEG 节省 ||-----|---------|-------------|| WebP | 照片、复杂图形 | 25%-35% || AVIF | 对兼容性要求不高的场景 | 40%-50% || PNG → WebP | 含透明通道的图 | 60%-70% |实际操作中,用 CDN 的图片处理服务做实时转换是最省事的方案。Cloudflare 的 Polish、阿里云的图片处理、七牛的 imageView2 都支持按请求参数自动转换格式和压缩质量。这种方式不需要改源站图片,CDN 边缘实时转换,用户请求时自动返回最优格式。<!-- 通过 URL 参数请求 WebP 格式 --><img src="https://cdn.example.com/photo.jpg?format=webp&q=80">也可以通过 Accept 请求头自动协商:浏览器发送 Accept: image/webp,CDN 自动返回 WebP 格式,无需改业务代码。文本压缩:Brotli 比 Gzip 再省 20%-30%gzip on;gzip_types text/plain text/css application/json application/javascript;# Brotli 压缩效果更好,主流浏览器已支持brotli on;brotli_types text/plain text/css application/json application/javascript;brotli_comp_level 6;Brotli 在压缩比上优于 Gzip,但压缩速度稍慢。对于静态资源可以预压缩,动态内容用 Gzip 即可。Cloudflare 默认开启 Brotli,阿里云 CDN 需要在控制台手动开启。视频优化:编码格式选择影响巨大H.264 是兼容性最好的选择,但从成本角度看,H.265/HEVC 比 H.264 节省约 50% 的码率,AV1 节省约 60%。如果目标用户主要在移动端,可以优先推送 H.265 版本。自适应码率(ABR)也是必须做的:根据用户带宽动态选择清晰度,避免 4K 视频推给 3G 网络,既浪费流量又卡顿。HLS 和 DASH 协议都原生支持 ABR,配置好码率阶梯即可。策略三:计费方式优化很多人只关注 CDN 的单价,但计费方式选错了,再低的价格也省不了钱。按流量 vs 按带宽峰值:流量平稳、可预测 → 按流量计费有明显高峰(直播、促销、游戏更新)→ 按 95 带宽峰值计费流量波动大但不确定 → 先按流量计费,积累一个月数据再判断预留带宽/流量包: 各厂商都提供预付费流量包,通常比按量付费便宜 20%-40%。适合流量稳定的业务。阿里云的 CDN 流量包、腾讯云的预付费带宽包都是这个思路。跨区域费用差异: 国内流量和海外流量价格差 2-5 倍。如果海外用户不多,不需要开全球加速。只开需要的区域,能省不少钱。一个常见误区是开了"全球加速"却 90% 的流量来自国内,多花了好几倍的钱。主流 CDN 厂商价格对比(国内流量,2026 年参考价):| 厂商 | 按流量(元/GB) | 按带宽(元/Mbps/天) | 特点 ||-----|-------------|-----------------|-----|| 阿里云 | 0.20-0.24 | 0.96-1.36 | 生态完善,流量包折扣大 || 腾讯云 | 0.20-0.25 | 0.96-1.20 | 免费额度多,适合中小业务 || 七牛云 | 0.18-0.29 | - | 图片处理能力强 || Cloudflare | 免费起步 | Pro $20/月 | 海外节点多,国内速度一般 |策略四:多 CDN 架构单一 CDN 供应商存在两个问题:一是议价空间有限,二是可用性风险集中。多 CDN 不是大公司才需要,日流量超过 10TB 就值得考虑。实现方式:DNS 层面:用 CNAME 指向多个 CDN,做加权轮询。缺点是切换延迟较高(DNS 缓存 TTL)智能调度:根据用户位置、CDN 节点负载、实时成本选择最优路径。DNSPod、NS1 等支持按策略解析按内容类型分配:静态资源走便宜 CDN,动态内容走高性能 CDN,视频走专用 CDN// 简化的 CDN 选择逻辑function selectCDN(contentType, userRegion) { const policy = { 'image': { cdn: 'low-cost-cdn', regions: ['cn-east', 'cn-south'] }, 'video': { cdn: 'video-cdn', regions: ['global'] }, 'api': { cdn: 'high-perf-cdn', regions: ['cn-east'] } }; return policy[contentType]?.cdn || 'default-cdn';}按内容类型分配 CDN 的成本差异示例: 一家视频平台把图片流量切到低成本的通用 CDN(单价低 40%),视频流量留在专用 CDN,整体成本下降约 25%。多 CDN 的难点在于缓存一致性管理和流量调度精细化。面试中如果追问,可以从 DNS 切换的延迟问题、缓存预热策略、流量分配比例调整这几个角度展开。策略五:请求次数优化请求次数费用容易被忽略,但在高并发场景下占比不低。每千次请求 0.01 元,日请求 10 亿次就是每天 1 万元。主要优化手段:资源合并:把多个小文件合并成一个,减少请求数。CSS/JS 打包是基本操作雪碧图:小图标合并为一张大图,用 CSS 定位显示。在 HTTP/1.1 下效果明显,HTTP/2 下收益降低HTTP/2 多路复用:升级到 HTTP/2 后,多个请求可以复用一个连接,但请求次数仍然计费减少无效请求:404 请求也计费,定期清理失效链接和过期资源API 响应合并:多个接口调用合并为一个批量接口,减少 API 请求次数策略六:成本监控与告警不做监控的优化都是盲目的。需要关注的指标:| 指标 | 告警阈值 | 说明 ||-----|---------|-----|| 缓存命中率 | 20% | 短期突增可能是攻击或配置错误 || 带宽峰值/均值比 | > 3:1 | 峰值过高说明可以优化计费方式 || 4xx/5xx 比例 | > 1% | 错误请求也在花钱 |成本归因分析也很重要。用 SQL 分析 CDN 日志,找出流量 Top 10 的 URL,看看是不是某个大文件没做压缩,或者某个接口的缓存 TTL 设置太短。-- 查询流量最大的 URLSELECT url, SUM(bytes) as total_bytes, COUNT(*) as requestsFROM cdn_logsWHERE date >= CURRENT_DATE - INTERVAL 7 DAYGROUP BY urlORDER BY total_bytes DESCLIMIT 10;-- 查询缓存命中率低的 URLSELECT url, COUNT(*) as total, SUM(CASE WHEN cache_status = 'HIT' THEN 1 ELSE 0 END) / COUNT(*) * 100 as hit_rateFROM cdn_logsWHERE date >= CURRENT_DATE - INTERVAL 7 DAYGROUP BY urlHAVING hit_rate < 80ORDER BY hit_rate;优化效果汇总| 优化手段 | 典型节省幅度 | 实施难度 ||---------|-----------|--------|| 缓存命中率提升(72%→95%) | 30%-40% 流量 | 低 || 图片格式转换(JPEG→WebP) | 25%-35% 图片流量 | 低 || 视频编码升级(H.264→H.265) | 40%-50% 视频流量 | 中 || 计费方式调整 | 10%-30% 总成本 | 低 || 多 CDN 架构 | 15%-25% 总成本 | 高 || Brotli 替代 Gzip | 20%-30% 文本流量 | 低 |优化优先级建议:先做缓存命中率和图片优化(投入小、见效快),再做计费方式调整和视频编码升级(需要一定技术投入),最后考虑多 CDN 架构(架构改动大)。面试中回答这个问题,核心要传达三点:一是理解 CDN 成本的结构性差异(流量是大头,优化重心在这里),二是掌握分层优化思路(缓存 → 内容 → 计费 → 架构),三是能给出量化的优化预期(不是笼统的"能省钱",而是"缓存命中率从 X 提升到 Y 可以节省 Z% 的流量成本")。追问方向通常涉及缓存一致性如何保证、多 CDN 的流量调度怎么实现、如何平衡成本和性能——这三个问题想清楚,这个话题基本就过了。