本文档介绍了系统设计中的缓存机制和性能优化技术。它涵盖了缓存策略、内容分发网络 (CDN)、内存数据库以及性能优化方法。关于特定数据库系统的实现细节,请参阅 数据库和存储系统。
缓存通过将频繁访问的数据存储在快速存储介质中,减少从较慢的主存储系统中获取数据的需求,从而提高系统性能。
缓存系统部署在多个层面,以优化不同类型的操作
| 缓存类型 | 位置 | 典型用例 | 常用技术 |
|---|---|---|---|
| 浏览器缓存 | 客户端 | 静态资源,API响应 | HTTP缓存头 |
| CDN缓存 | 网络边缘 | 静态内容,媒体文件 | Cloudflare, Akamai, Fastly |
| 应用缓存 | 服务器内存 | 应用对象,数据库查询结果 | Redis, Memcached |
| 数据库缓存 | 数据库服务器 | 查询结果,缓冲池 | 集成在数据库中 |
来源: README.md:179-203
根据访问模式、一致性需求和性能需求,采用不同的缓存模式。
| 策略 | 优点 | 缺点 | 最佳用途 |
|---|---|---|---|
| Cache-Aside (旁路缓存) | 简单,只缓存需要的数据 | 缓存未命中代价高 | 通用,读密集型工作负载 |
| Read-Through (读通) | 简化应用程序代码 | 可能缓存未使用的数据 | 面向服务架构 |
| 直写 | 数据一致性 | 写入延迟 | 需要一致性的应用程序 |
| Write-Behind (写回) | 高写入吞吐量 | 有数据丢失风险 | 高流量写入操作 |
来源: README.md:182, README.md:200
缓存空间有限,因此淘汰策略对于保持最佳性能至关重要。
| 策略 | 算法 | 权衡 | 实现复杂度 |
|---|---|---|---|
| LRU (Least Recently Used, 最近最少使用) | 淘汰最长时间未被访问的项 | 对时间局部性有益,但需要跟踪访问时间 | 中等 - 需要队列或链表 |
| LFU (Least Frequently Used, 最不常用) | 淘汰访问频率最低的项 | 对基于流行度的访问模式效果好 | 高 - 需要计数器和排序 |
| FIFO (First In First Out, 先进先出) | 先淘汰最旧的项 | 简单,但不考虑使用模式 | 低 - 只需队列 |
| TTL (Time To Live, 生存时间) | 在指定时长后淘汰项 | 简单且可预测,适用于时间敏感数据 | 低 - 需要时间戳存储 |
| 根据请求属性(IP、URL 等)的哈希值进行路由 | 随机选择要淘汰的项 | 实现简单,避免基于模式的低效率 | 最低 - 无额外数据结构 |
来源: README.md:184, README.md:188
Redis 和 Memcached 是专业的内存数据存储,提供高性能的缓存功能。
| 功能 | Redis | Memcached |
|---|---|---|
| 数据类型 | 丰富的数据结构(字符串、列表、集合、有序集合、哈希表) | 简单的键值对 |
| 持久化 | RDB 快照和 AOF 日志 | 无(纯内存) |
| 复制 | 主从复制 | 未内置 |
| 分区 | 客户端/集群模式 | 客户端 |
| 事务 | 支持 | 不支持 |
| 内存效率 | 对于小值,较低 | 对于小值,较高 |
| 最大项大小 | 512MB | 1MB |
| 用例 | 高级缓存、消息传递、实时分析 | 简单的、高吞吐量的缓存 |
来源: README.md:179, README.md:185, README.md:192-195
CDN 将内容分发到地理位置更靠近用户的边缘服务器,从而降低延迟并提高可用性。
| 机制 | 描述 | 用例 |
|---|---|---|
| 静态缓存 | 缓存具有长 TTL 的静态资源 | 图片、CSS、JavaScript、静态 HTML |
| 动态缓存 | 缓存具有较短 TTL 的动态内容 | API 响应、动态页面片段 |
| 源站保护 (Origin Shield) | 边缘服务器与源站之间的中间缓存层 | 在缓存未命中时减少源站负载 |
| 缓存失效 | 缓存失效或刷新 | 内容更新、部署 |
| 内容版本控制 | 根据 URL 参数提供不同版本 | A/B 测试、渐进式发布 |
来源: README.md:176-177, README.md:178, README.md:191, README.md:197
除了缓存,还可以采用各种技术来优化系统性能。
| 技术 | 描述 | 影响 |
|---|---|---|
| 响应缓存 | 缓存 API 响应 | 降低后端负载和响应时间 |
| 分页 | 分块返回数据 | 减小载荷大小和处理时间 |
| 字段过滤 | 允许客户端仅请求所需字段 | 减小载荷大小 |
| 压缩 | 压缩 API 响应 | 减小载荷大小和传输时间 |
| 连接池 | 重用数据库连接 | 降低连接开销 |
| 请求批量处理 | 合并多个请求 | 降低网络开销 |
| 异步处理 | 异步处理非关键操作 | 提高关键路径的响应时间 |
来源: README.md:198, README.md:201-202
有效的性能优化需要监控关键指标,以识别瓶颈并验证改进效果。
| 指标类别 | 关键指标 | 优化目标 |
|---|---|---|
| 速度 | 页面加载时间、TTFB、API 响应时间 | 越低越好 |
| 可靠性 | 错误率、正常运行时间、可用性 | 越高越好 |
| 效率 | 缓存命中率、CPU/内存使用率 | 命中率越高越好,资源使用率越低越好 |
| 用户体验 | LCP、FID、CLS (核心 Web 指标) | 达到 Google 的“良好”阈值 |
来源: README.md:181, README.md:199, README.md:201
Netflix 采用多层缓存架构来处理数百万并发流。
Netflix 的缓存实现包括
来源: README.md:92-93, README.md:95-96
Pinterest 通过对其存储系统进行一次优化,将克隆时间减少了 99%。关键在于实现了大型不可变 blob 的有效共享,而不是创建完整副本。
来源: README.md:81
尽管缓存有其优点,但它也带来了必须解决的独特挑战
| 挑战 | 描述 | 缓解策略 |
|---|---|---|
| 缓存一致性 (Cache Coherence) | 确保缓存和源之间的数据一致 | TTL、缓存失效、写通 |
| 惊群效应 (Thundering Herd) | 并发缓存未命中导致负载激增 | 缓存预热、过期时间错开、后台刷新 |
| 缓存穿透 | 对不存在键的重复查询 | 负面缓存、布隆过滤器 |
| 缓存穿透 (Cache Breakdown) | 缓存失败导致系统降级 | 熔断器、降级策略、缓存冗余 |
| 缓存污染 (Cache Poisoning) | 缓存中存在恶意或错误数据 | 数据验证、缓存条目签名 |
| 缓存未命中攻击 (Cache Miss Attack) | 故意绕过缓存的尝试 | 速率限制、请求限流、DDoS 防护措施 |
来源: README.md:183, README.md:187, README.md:189
| 类别 | 最佳实践 |
|---|---|
| 设计 | 在正确的级别进行缓存(客户端、CDN、应用、数据库) |
| 大小 | 调整缓存大小以平衡命中率和内存使用 |
| TTL | 根据数据易变性设置适当的过期时间 |
| 监控 | 跟踪缓存命中率、内存使用和淘汰率 |
| 回退 | 当缓存不可用时实现优雅降级 |
| 失效 | 制定清晰的缓存失效策略 |
| 预热 | 为关键路径预填充缓存 |
| 键设计 | 使用一致且高效的缓存键设计 |
来源: README.md:180, README.md:182-183, README.md:200
实施有效的缓存和性能优化需要平衡许多权衡,包括一致性与性能、内存使用与命中率、复杂性与可维护性。最佳方法取决于具体的应用需求、流量模式和资源限制。