简介
本指南面向DouPHP的缓存策略与配置,覆盖页面缓存(静态生成、动态内容缓存、失效策略)、数据缓存(Redis/Memcached接入、数据结构设计)、对象缓存(模型层/服务层/API响应)、分布式缓存(集群部署、一致性、穿透防护)、缓存预热(启动预加载、定时更新)以及监控与调试(命中率统计、性能分析)。文档基于仓库现有实现进行梳理,并给出可落地的扩展建议。
项目结构
- 模板编译缓存:位于 core/web/template/CompileCache.php,负责“源模板→编译产物”的落盘路径、重编判定与原子写入,支持按修订号批量失效。
- Web资源版本世代:位于 core/web/manifest/ManifestCacheGeneration.php,用于lang_js/routes_js等资源的URL版本号管理,配合清空缓存链路触发浏览器强刷。
- 后台缓存清理:位于 admin/service/cache/CacheClearService.php,提供删除缓存目录能力,便于统一清理编译产物与资源缓存。
- 系统常量与路由固定模块:位于 config/system.php,定义前台固定模块与保留段,有助于识别哪些页面适合做静态化或长缓存。
- 应用基础配置:位于 config/config.php,包含数据库、字符集、调试开关等,可作为缓存相关配置的承载位置(如开启/关闭编译检查、调试模式下的行为)。
- 第三方SDK中的缓存抽象:位于 plugin/alipay/sdk/lotusphp_runtime/Cache/*,提供适配器模式(Memcache/Memcached等)与配置构建器,可用于理解如何在DouPHP中接入外部缓存。
- Excel内存缓存:位于 core/library/excel/src/PHPExcel/CachedObjectStorage/Memcache.php,展示在大数据处理场景下使用外部缓存降低内存占用的方式。
graph TB
A["前端请求"] --> B["路由/控制器"]
B --> C["模板渲染<br/>CompileCache"]
B --> D["业务服务/模型"]
D --> E["数据库"]
D --> F["外部缓存<br/>Memcache/Memcached"]
C --> G["编译产物目录"]
B --> H["Web资源版本<br/>ManifestCacheGeneration"]
I["后台管理"] --> J["缓存清理<br/>CacheClearService"]
J --> G
J --> H
核心组件
- 模板编译缓存(CompileCache)
- 职责:计算编译产物路径、判断是否需要重编、原子写入编译产物、读取编译头中的修订号以支持批量失效。
- 关键点:forceCompile、compileCheck、compileRevision、产品版本;通过tempnam+rename保证并发安全。
- Web资源版本世代(ManifestCacheGeneration)
- 职责:维护storage/cache/manifest_generation.txt中的世代值,为静态资源URL附加?v=contentHash-generation,使清空缓存后强制浏览器刷新。
- 后台缓存清理(CacheClearService)
- 职责:删除指定缓存目录(含子目录),用于清理编译产物与资源缓存。
- 第三方缓存适配(LotusPHP SDK)
- 职责:提供统一的缓存接口与适配器(Memcache/Memcached),以及配置构建器,便于多后端接入。
- Excel内存缓存(PHPExcel Memcache)
- 职责:将Excel单元格对象缓存到外部存储,降低大表格处理的内存占用。
架构总览
下图展示了请求从进入路由到模板渲染、资源版本控制与缓存清理的整体流程,以及外部缓存的接入点。
sequenceDiagram
participant U as "用户"
participant R as "路由/控制器"
participant T as "模板引擎"
participant CC as "CompileCache"
participant M as "ManifestCacheGeneration"
participant S as "服务/模型"
participant DB as "数据库"
participant C as "外部缓存"
U->>R : HTTP请求
R->>S : 执行业务逻辑
S->>DB : 读取数据
DB-->>S : 结果
S->>C : 读/写缓存(可选)
C-->>S : 返回缓存或空
S-->>R : 业务数据
R->>T : 渲染视图
T->>CC : 判断是否需重编/写入编译产物
CC-->>T : 编译产物路径
T->>M : 获取资源URL版本
M-->>T : 带generation的版本参数
T-->>U : HTML响应
详细组件分析
模板编译缓存(CompileCache)
- 重编判定逻辑
- 强制重编:当forceCompile为真时总是重编。
- 产物不存在:直接重编。
- 修订号不匹配:当编译头中的rev与当前compileRevision不一致时重编。
- 源模板变更:当compileCheck为真且源模板修改时间晚于编译产物时重编。
- 原子写入
- 使用tempnam创建临时文件,再rename为目标路径,避免并发写入导致半文件。
- 写入头部包含产品版本、编译修订号与编译时间,便于后续失效与诊断。
- 请求级缓存优化
- 对编译头rev进行请求级缓存,减少重复fopen开销。
flowchart TD
Start(["进入needsRecompile"]) --> Force{"forceCompile?"}
Force --> |是| Recompile["需要重编"]
Force --> |否| Exists{"编译产物存在?"}
Exists --> |否| Recompile
Exists --> |是| RevMatch{"rev匹配?"}
RevMatch --> |否| Recompile
RevMatch --> |是| CheckSrc{"compileCheck且源更旧?"}
CheckSrc --> |是| Recompile
CheckSrc --> |否| Skip["跳过重编"]
Recompile --> End(["结束"])
Skip --> End
Web资源版本世代(ManifestCacheGeneration)
- 作用:为静态资源URL附加contentHash-generation,管理员清空缓存时bump generation,强制浏览器打破本地缓存。
- 实现要点:
- current()读取storage/cache/manifest_generation.txt中的世代值。
- bump()原子写入新世代。
- urlVersion()拼接最终版本参数。
sequenceDiagram
participant Admin as "管理员"
participant Clear as "CacheClearService"
participant Gen as "ManifestCacheGeneration"
Admin->>Clear : 触发清空缓存
Clear->>Gen : bump()
Gen-->>Clear : 新世代
Clear-->>Admin : 完成
后台缓存清理(CacheClearService)
- 功能:删除指定目录及其子目录,常用于清理编译产物与资源缓存。
- 扩展点:可在该服务中增加还原、增量清理等功能。
外部缓存适配器(Memcache/Memcached)
- 适配器接口:提供connect/get/add/update/del等方法,封装底层连接与键名空间前缀。
- 配置构建器:支持多主机、主从角色、默认配置继承,便于构建集群与读写分离。
- 键命名:通过getRealKey将表名前缀与业务键组合,避免冲突。
classDiagram
class LtCacheConfigBuilder {
+addSingleHost(hostConfig)
+addHost(groupId, nodeId, role, hostConfig)
+getServers()
}
class LtCacheAdapterMemcache {
+connect(hostConf)
+get(key, tableName, connectionResource)
+add(key, value, ttl, tableName, connectionResource)
+update(key, value, ttl, tableName, connectionResource)
+del(key, tableName, connectionResource)
-getRealKey(tableName, key)
}
LtCacheConfigBuilder --> LtCacheAdapterMemcache : "配置驱动"
Excel内存缓存(PHPExcel Memcache)
- 用途:在处理大型Excel文件时,将单元格对象缓存到外部存储,降低内存峰值。
- 适用场景:报表导出、批量导入等I/O密集任务。
依赖关系分析
- 模板编译缓存依赖文件系统权限与目录结构,确保编译产物可写且原子写入成功。
- Web资源版本依赖storage/cache/manifest_generation.txt的存在与可写权限。
- 外部缓存适配器依赖对应扩展(memcache/memcached)与网络连通性。
- 后台清理服务依赖FileHelper提供的目录删除能力。
graph LR
CC["CompileCache"] --> FS["文件系统"]
MG["ManifestCacheGeneration"] --> ST["storage/cache"]
CS["CacheClearService"] --> FS
ADP["缓存适配器"] --> EXT["memcache/memcached扩展"]
ADP --> NET["网络"]
性能考量
- 模板编译缓存
- 合理设置compileCheck与forceCompile:开发环境建议开启compileCheck以便热更新;生产环境可关闭以提升性能。
- 使用compileRevision进行批量失效:升级模板引擎或Prefilter时提升revision,避免手工清理。
- Web资源版本
- 使用contentHash-generation组合,既保证内容变更自动刷新,又支持全局清空缓存时的强制刷新。
- 外部缓存
- 连接复用与超时:在适配器层配置合理的连接池与超时,避免频繁建立连接。
- 键空间隔离:通过表名前缀或命名空间隔离不同模块的键,防止冲突。
- 序列化成本:对大对象进行压缩或分片,减少网络传输与序列化开销。
- Excel内存缓存
- 在大批量数据处理时启用外部缓存,显著降低内存占用与GC压力。
故障排查指南
- 模板未生效
- 检查CompileCache的forceCompile与compileCheck设置;确认编译产物目录可写;核对compileRevision是否变化。
- 静态资源未刷新
- 检查ManifestCacheGeneration的世代文件是否存在与可写;执行bump后验证URL版本参数是否变化。
- 外部缓存不可用
- 检查memcache/memcached扩展是否安装;确认适配器配置的主机与端口;查看网络连接与认证信息。
- 缓存清理无效
- 确认CacheClearService传入的目录正确;检查FileHelper::delDir的执行权限与返回值。
结论
DouPHP已内置模板编译缓存与Web资源版本管理,结合后台清理服务可实现高效的页面缓存与资源刷新。通过第三方SDK的缓存适配器,可平滑接入Memcache/Memcached等外部缓存,满足数据缓存与对象缓存需求。在生产环境中,建议结合编译修订号、资源世代与外部缓存TTL,形成完整的缓存生命周期管理;同时关注连接池、键空间隔离与序列化成本,以获得稳定高性能表现。
附录
页面缓存配置与实践
- 静态页面生成
- 利用模板编译缓存将视图编译为PHP产物,减少模板解析开销。
- 对于高频访问的固定页面(如首页、公告页),可在服务层增加输出缓存,结合TTL与键空间隔离。
- 动态内容缓存
- 在服务层或模型层对查询结果进行缓存,键名包含查询条件与版本信息,避免脏读。
- 对写操作后的相关键进行失效或短TTL降级,保证一致性。
- 缓存失效策略
- 模板侧:通过compileRevision批量失效;通过ManifestCacheGeneration的bump强制刷新资源。
- 数据侧:采用“发布-订阅”或“事件驱动”的方式,在数据变更后主动失效相关缓存键。
数据缓存实现
- Redis缓存配置
- 参考LotusPHP的配置构建器思路,定义host/port/prefix/timeout等参数,集中管理多实例与主从角色。
- 建议在服务层封装统一的Redis客户端,提供get/set/del/incr等常用操作。
- Memcached使用
- 使用CacheAdapterMemcache/Memcached适配器,统一键命名与连接管理。
- 对大对象进行压缩或分片,避免单条过大导致的性能问题。
- 缓存数据结构设计
- 键命名规范:模块:实体:标识:版本,例如 product:item:123:v1。
- 值结构:JSON或序列化对象,附带更新时间戳与版本号,便于校验与回退。
- 集合型数据:使用哈希或列表存储,注意过期策略与内存占用。
对象缓存策略
- 模型层缓存
- 对频繁读取的实体(如商品、分类)进行缓存,键包含ID与语言、租户等维度。
- 写操作后失效相关实体缓存,或使用短TTL降级。
- 服务层缓存
- 对聚合查询结果、复杂计算结果进行缓存,避免重复计算。
- 引入版本号与时间戳,确保缓存一致性与可追溯性。
- API响应缓存
- 对只读API进行响应缓存,键包含请求参数与用户上下文(如语言、地区)。
- 对写操作后的相关API响应进行失效或短TTL降级。
分布式缓存配置
- 缓存集群部署
- 使用配置构建器添加多个节点,区分主从角色与权重,实现负载均衡与高可用。
- 对关键路径增加重试与降级策略,避免单点故障影响整体可用性。
- 数据一致性保证
- 采用“先更新数据库,再删除缓存”的策略,避免脏读。
- 对强一致性要求高的场景,使用分布式锁或版本号控制。
- 缓存穿透防护
- 对空结果也进行短TTL缓存,避免恶意请求击穿至数据库。
- 对热点键进行限流与熔断,保护后端资源。
缓存预热策略
- 启动时预加载热点数据
- 在应用启动阶段,异步加载热门商品、分类、配置等数据到缓存,缩短冷启动延迟。
- 使用版本号与时间戳标记预热数据,便于后续更新与失效。
- 定时任务更新缓存
- 对时效性较强的数据(如库存、价格)设置较短TTL,并通过定时任务定期刷新。
- 对批量更新场景,采用增量更新与合并策略,减少缓存抖动。
缓存监控与调试工具
- 缓存命中率统计
- 在适配器层埋点记录hit/miss次数,汇总到指标系统,便于观察趋势与定位瓶颈。
- 对低命中率场景进行分析,调整TTL、键粒度或预热策略。
- 缓存性能分析
- 监控连接数、延迟、错误率等指标,及时发现异常。
- 对大对象与高频键进行采样分析,优化序列化与网络传输。
- 调试工具
- 在开发环境开启详细日志,记录缓存读写过程与失败原因。
- 提供管理端工具,支持查看缓存键、手动失效与批量清理。