简介
本文件为 DouPHP 的对象缓存策略提供系统化说明,覆盖模型层与服务层的缓存实践、生命周期管理、一致性保障以及性能优化技巧。文档基于仓库中 ORM 模型、查询构建器、关系加载机制、Excel 计算缓存、API 响应头缓存以及插件侧缓存适配器等现有实现进行归纳与扩展建议,帮助在业务中安全高效地引入对象级缓存。
项目结构
DouPHP 的缓存相关能力分布在多个层次:
- 模型层:ORM 模型与查询构建器负责实体对象与关联数据的“内存级”缓存(实例属性与已加载关系),并通过预加载减少 N+1 查询。
- 服务层:API 控制器通过 HTTP 缓存头对静态资源进行浏览器/CDN 缓存;业务结果可结合外部缓存(如 Memcache/Memcached)进行跨请求共享。
- 基础设施:Excel 计算引擎内置公式计算缓存;插件侧提供通用缓存适配器(Memcache/Memcached)与配置构建器,便于接入分布式缓存。
- 运维工具:后台提供缓存目录清理服务,用于失效与重建。
graph TB
subgraph "模型层"
M["Model<br/>实体对象内存缓存"]
B["Builder<br/>查询构建器<br/>with 预加载"]
R["Relation<br/>关系加载接口"]
BR["BelongsTo<br/>一对一关系匹配"]
end
subgraph "服务层"
API["LangController<br/>HTTP 缓存头"]
SVC["业务服务<br/>可接入外部缓存"]
end
subgraph "基础设施"
XCALC["Calculation<br/>公式计算缓存"]
XSET["Settings<br/>单元格存储策略"]
XMEM["CachedObjectStorage/Memcache"]
XWIN["CachedObjectStorage/Wincache"]
end
subgraph "插件缓存"
CC["CacheConfigBuilder<br/>缓存配置"]
CH["CacheHandle<br/>统一读写入口"]
AM["CacheAdapterMemcache"]
AD["CacheAdapterMemcached"]
end
subgraph "运维"
CLR["CacheClearService<br/>清理缓存目录"]
end
M --> B
B --> R
R --> BR
API --> SVC
SVC --> CH
CH --> AM
CH --> AD
XCALC --> XSET
XSET --> XMEM
XSET --> XWIN
CLR --> SVC
核心组件
- 模型层内存缓存:
- 实体对象属性与关系在 Model 实例内缓存,避免重复读取与计算。
- 关系惰性加载与批量匹配,减少 N+1 查询。
- 查询构建器预加载:
- Builder::with 支持单关系、多关系、嵌套关系与闭包约束,集中执行 eager load。
- Excel 计算缓存:
- Calculation 提供公式计算结果的进程内缓存,Settings 提供单元格存储策略切换。
- API 响应缓存:
- LangController 设置 Cache-Control、ETag 等头部,驱动浏览器/CDN 缓存。
- 插件缓存适配器:
- CacheHandle 统一 add/get/update/del 接口,底层通过 Memcache/Memcached 适配器访问。
- 缓存清理:
- CacheClearService 提供缓存目录删除能力,配合失效策略使用。
架构总览
下图展示从请求到数据返回的缓存路径:模型层内存缓存与预加载降低数据库压力;Excel 计算缓存加速复杂计算;API 层通过 HTTP 缓存头利用浏览器/CDN;插件缓存适配器将热点数据写入分布式缓存;后台清理服务触发失效与重建。
sequenceDiagram
participant C as "客户端"
participant API as "LangController"
participant S as "业务服务"
participant M as "Model/Builder"
participant DB as "数据库"
participant XC as "Excel 计算缓存"
participant PC as "插件缓存(Adapters)"
C->>API : 请求语言包
API->>API : 设置 Cache-Control / ETag
API-->>C : 返回语言包(命中浏览器/CDN缓存)
Note over API,C : 静态资源由浏览器/CDN缓存
C->>S : 业务查询
S->>M : with() 预加载 + get()/first()
M->>DB : 单次批量查询
DB-->>M : 结果集
M->>M : 关系匹配与内存缓存
S->>XC : 计算结果(可选)
XC-->>S : 计算值(命中则直接返回)
S->>PC : 写入热点数据(可选)
PC-->>S : 写入成功
S-->>C : 业务结果
详细组件分析
模型层缓存:实体对象缓存、查询结果缓存、关联数据缓存
- 实体对象缓存:
- Model 维护 attributes 与 relations,getAttribute 优先返回已加载关系或 accessor/cast 后的值,避免重复 IO。
- setAttribute 与 syncOriginal 保证脏字段检测与持久化一致性。
- 查询结果缓存:
- Builder 终结方法 get/first/find/paginate 返回水合后的 Model/Collection,并在一次请求内复用。
- with 预加载将多表关联合并为少量 SQL,显著降低 N+1 问题。
- 关联数据缓存:
- Relation 抽象出批量获取与回填逻辑;BelongsTo 通过字典映射快速匹配父模型与子记录。
classDiagram
class Model {
+attributes
+relations
+getAttribute(key)
+setAttribute(key, value)
+syncOriginal()
+newQuery()
}
class Builder {
+with(relations)
+get()
+first()
+find(id)
+pushAfterHydrate(cb)
}
class Relation {
<<abstract>>
+addEagerConstraints(models)
+getEager()
+match(models, results, name)
+getResults()
}
class BelongsTo {
+match(models, results, name)
+getResults()
}
Model --> Builder : "创建查询"
Builder --> Relation : "预加载/匹配"
Relation <|-- BelongsTo : "继承"
服务层缓存:业务逻辑结果缓存、API 响应缓存、计算结果缓存
- API 响应缓存:
- LangController 设置 Cache-Control 与 ETag,使浏览器/CDN 缓存静态语言包,降低服务端压力。
- 业务逻辑结果缓存:
- 可在服务层调用插件缓存适配器(CacheHandle + Memcache/Memcached)缓存热点聚合结果,按业务键与 TTL 管理。
- 计算结果缓存:
- Excel Calculation 提供公式计算缓存,Settings 可切换单元格存储策略(内存/磁盘/外部存储)。
flowchart TD
Start(["进入服务层"]) --> CheckAPI{"是否API静态资源?"}
CheckAPI --> |是| SetHeaders["设置 Cache-Control / ETag"]
SetHeaders --> ReturnAPI["返回响应(浏览器/CDN缓存)"]
CheckAPI --> |否| CalcCheck{"是否计算密集型?"}
CalcCheck --> |是| UseCalc["使用 Excel 计算缓存"]
UseCalc --> ReturnCalc["返回计算结果"]
CalcCheck --> |否| HotData{"是否热点数据?"}
HotData --> |是| UseExt["写入外部缓存(Memcache/Memcached)"]
UseExt --> ReturnSvc["返回业务结果"]
HotData --> |否| Direct["直接返回业务结果"]
ReturnAPI --> End(["结束"])
ReturnCalc --> End
ReturnSvc --> End
Direct --> End
缓存生命周期管理:创建时机、更新策略、失效机制
- 创建时机:
- 模型层:首次访问属性/关系时惰性加载并缓存至实例;Builder 预加载在一次请求内批量缓存。
- 服务层:热点数据在首次计算后写入外部缓存;API 静态资源由浏览器/CDN 缓存。
- Excel:公式计算结果在进程内缓存,单元格存储策略可切换。
- 更新策略:
- 写操作后应同步失效相关缓存键(如产品详情、列表摘要、统计指标)。
- 对于强一致场景,采用“先更新源数据,再删除缓存”的策略,避免脏读。
- 失效机制:
- 后台提供缓存目录清理服务,可用于模板编译缓存、临时文件等。
- 外部缓存可通过 TTL 或显式删除键实现失效;必要时结合版本号或命名空间隔离。
缓存一致性保证:读写分离、版本控制、冲突解决
- 读写分离:
- 读路径优先命中缓存(模型实例、Excel 计算缓存、外部缓存、浏览器/CDN);写路径确保原子更新源数据并失效相关缓存。
- 版本控制:
- 对外部缓存键加入版本号或时间戳后缀,变更时递增版本,避免旧值残留。
- 冲突解决:
- 并发写场景下,采用“删除缓存 + 延迟填充”模式;必要时加锁或队列串行化写操作。
- 对于强一致要求的数据,可缩短 TTL 或禁用缓存,直接走数据库。
性能优化技巧:预加载热点数据、延迟加载、缓存分层
- 预加载热点数据:
- 使用 Builder::with 预加载常用关联(如商品分类、品牌),减少多次查询。
- 延迟加载:
- 仅在访问关系时触发加载,避免不必要的 IO;Model 的关系惰性加载机制天然支持。
- 缓存分层:
- 第一层:模型实例内存缓存(请求级)。
- 第二层:Excel 计算缓存(进程级)。
- 第三层:外部缓存(Memcache/Memcached,跨进程/节点共享)。
- 第四层:浏览器/CDN(静态资源,如语言包)。
依赖关系分析
- 模型层依赖:
- Model 依赖 Builder 进行查询构造;Builder 依赖 Relation 进行预加载与匹配;BelongsTo 作为具体关系实现。
- 服务层依赖:
- API 控制器依赖 HTTP 缓存头;业务服务可依赖插件缓存适配器。
- 基础设施依赖:
- Excel 计算依赖 Settings 选择存储策略;存储策略可指向内存、磁盘或外部存储。
- 插件缓存依赖:
- CacheHandle 依赖 CacheConfigBuilder 配置连接;底层通过 Memcache/Memcached 适配器访问。
graph LR
Model["Model"] --> Builder["Builder"]
Builder --> Relation["Relation"]
Relation --> BelongsTo["BelongsTo"]
API["LangController"] --> HTTP["HTTP 缓存头"]
Service["业务服务"] --> CacheHandle["CacheHandle"]
CacheHandle --> Config["CacheConfigBuilder"]
CacheHandle --> AdapterMC["Memcache 适配器"]
CacheHandle --> AdapterMD["Memcached 适配器"]
Excel["Excel 计算"] --> Settings["Settings"]
Settings --> Storage["存储策略"]
性能考虑
- 合理使用预加载:
- 在列表页使用 with 预加载必要关联,避免 N+1 查询。
- 控制缓存粒度:
- 小对象可直接放入模型实例缓存;大对象或跨请求共享数据放入外部缓存。
- 合理设置 TTL:
- 热点数据设置较短 TTL 以平衡新鲜度与性能;静态资源使用长 TTL 与 ETag。
- 避免缓存穿透:
- 对不存在的数据可缓存空值并设置短 TTL,防止恶意请求打爆后端。
- 监控与度量:
- 关注缓存命中率、延迟与错误率,及时调整策略。
故障排查指南
- 缓存未命中:
- 检查键名与命名空间是否正确;确认 TTL 是否过短;验证外部缓存连接是否正常。
- 数据不一致:
- 确认写路径是否先更新源数据再删除缓存;检查是否存在并发写导致的竞态条件。
- 性能回退:
- 评估预加载是否过度;检查是否有大量冷数据进入缓存;调整缓存分层与 TTL。
- 清理缓存:
- 使用后台清理服务清除模板编译缓存或临时文件;必要时重启服务或清空外部缓存。
结论
DouPHP 在模型层提供了完善的内存级对象缓存与预加载机制,在服务层具备 HTTP 缓存与外部缓存接入能力,辅以 Excel 计算缓存与后台清理工具,形成多层次、可扩展的缓存体系。通过合理的生命周期管理、一致性保障与性能优化策略,可在保证数据正确性的前提下显著提升系统吞吐与响应速度。
附录:实现示例与配置方法
- 模型层预加载示例:
- 在查询中使用 with('category', 'brand') 预加载分类与品牌,减少多次数据库访问。
- 参考路径:Builder.php:174-199
- 关系匹配示例:
- 使用 BelongsTo 的 match 方法将子记录回填到父模型集合,提升遍历效率。
- 参考路径:BelongsTo.php:59-89
- API 响应缓存示例:
- 设置 Cache-Control 与 ETag,启用浏览器/CDN 缓存静态语言包。
- 参考路径:LangController.php:40-58
- Excel 计算缓存示例:
- 启用公式计算缓存,减少重复计算开销;可切换单元格存储策略。
- 参考路径:Calculation.php:2694-2713, Settings.php:150-170
- 外部缓存接入示例:
- 使用 CacheHandle 统一读写缓存,底层通过 Memcache/Memcached 适配器访问。
- 参考路径:CacheHandle.php:17-53, CacheAdapterMemcache.php:1-34, CacheAdapterMemcached.php:1-36
- 缓存清理示例:
- 调用清理服务删除指定缓存目录,用于失效与重建。
- 参考路径:CacheClearService.php:27-45