简介
本技术文档聚焦 DouPHP 后台服务层的缓存策略,围绕缓存层级、键生成、失效机制、一致性保障、容量与内存优化、监控与故障恢复等主题展开。结合仓库中现有实现(如 URL 字段缓存预热、模板缓存清理、可插拔的缓存适配器抽象),给出面向生产环境的最佳实践与落地建议。
项目结构
DouPHP 在后台侧提供了两类与缓存相关的实现:
- 运行时数据缓存:通过可插拔的缓存适配器抽象(LtCacheAdapter)与连接管理(LtCacheHandle/LtCacheConnectionManager)提供 get/add/update/del 能力,适配 Memcache/Memcached 等后端。
- 文件系统缓存:模板编译产物、静态资源等以文件形式落盘,并提供统一的清理入口(CacheClearService)。
graph TB
A["业务服务<br/>控制器/服务层"] --> B["URL 字段缓存预热<br/>UrlBuilder::warmupFieldCache()"]
A --> C["模板/文件缓存清理<br/>CacheClearService::clearCache()"]
A --> D["可插拔缓存接口<br/>LtCacheAdapter"]
D --> E["Memcache 适配器<br/>CacheAdapterMemcache"]
D --> F["Memcached 适配器<br/>CacheAdapterMemcached"]
A --> G["数据库访问门面<br/>DB Facade"]
核心组件
- 缓存适配器抽象:定义统一接口 add/get/update/del/connect,屏蔽底层存储差异。
- 缓存句柄:封装连接管理与适配器选择,对外暴露标准操作。
- 配置构建器:支持多组节点、主从角色、默认配置合并,便于扩展不同环境。
- 模板缓存清理:提供删除指定目录的能力,用于发布/升级后清理编译产物。
- URL 字段缓存预热:批量回填 slug/created_at 等字段,减少重复查询。
架构总览
下图展示后台服务层与缓存相关组件的交互关系:业务服务调用 URL 字段缓存预热与模板缓存清理;同时可通过 LtCacheAdapter 系列进行通用缓存读写;数据库访问通过 DB 门面完成。
sequenceDiagram
participant Svc as "后台服务"
participant Url as "UrlBuilder"
participant Cache as "LtCacheHandle"
participant Adapter as "LtCacheAdapter"
participant DB as "DB Facade"
participant FS as "文件系统"
Svc->>Url : 触发字段缓存预热
Url->>DB : 批量查询缺失字段
DB-->>Url : 返回字段值
Url->>Url : 写入进程内缓存映射
Svc->>Cache : get/add/update/del(key, table)
Cache->>Adapter : 转发请求
Adapter-->>Cache : 返回结果
Svc->>FS : 清理模板缓存目录
FS-->>Svc : 完成
详细组件分析
组件A:URL 字段缓存预热(进程内缓存)
- 目标:避免对同一批 ID 的字段(如 slug、created_at)重复查询,提升渲染与路由组装效率。
- 流程要点:
- 计算当前批次缺失的 ID 集合。
- 若存在缺失,发起一次 IN 查询批量获取字段。
- 将结果回填到进程内缓存映射,并对未命中的 ID 填充默认值,防止后续重复穿透。
- 复杂度:时间 O(N+M),空间 O(M),其中 N 为请求 ID 数,M 为缺失 ID 数。
- 适用场景:列表页、详情页批量渲染、路由 slug 解析等读多写少场景。
flowchart TD
Start(["进入 warmupFieldCache"]) --> CheckLocal["检查进程内缓存是否已命中"]
CheckLocal --> |全部命中| End(["结束"])
CheckLocal --> |存在缺失| BuildMiss["构造缺失ID集合"]
BuildMiss --> QueryDB["批量查询缺失字段"]
QueryDB --> FillCache["回填进程内缓存并补默认值"]
FillCache --> End
组件B:模板/文件缓存清理(文件系统缓存)
- 目标:在主题/模块更新或发布后,清理编译后的模板缓存目录,确保新模板生效。
- 行为:递归删除指定目录及其子目录内容。
- 使用点:主题初始化流程中调用,保证部署后视图正确刷新。
组件C:登录流程中的缓存清理(事件副作用)
- 目标:在管理员登录后执行必要的副作用,包括清理缓存,确保权限与界面状态一致。
- 集成方式:通过依赖注入将缓存清理服务注入登录编排服务,在成功登录后触发。
组件D:可插拔缓存适配器(通用 KV 缓存)
- 抽象:LtCacheAdapter 定义 connect/add/get/update/del 方法,屏蔽底层存储差异。
- 句柄:LtCacheHandle 负责连接管理与适配器选择,对外暴露统一 API。
- 配置:LtCacheConfigBuilder 支持多组节点、主从角色与默认配置合并。
- 适配器实现:
- Memcache 适配器:基于 memcache_connect 建立连接,key 前缀采用“表名-键”形式。
- Memcached 适配器:基于 Memcached 扩展,同样以“表名-键”作为真实 key。
classDiagram
class LtCacheAdapter {
+connect(hostConf)
+add(key, value, ttl, tableName, connectionResource)
+del(key, tableName, connectionResource)
+get(key, tableName, connectionResource)
+update(key, value, ttl, tableName, connectionResource)
}
class LtCacheHandle {
+configHandle
+group
+node
+role
+connectionManager
+connectionResource
+init()
+add(key, value, ttl, tableName)
+del(key, tableName)
+get(key, tableName)
+update(key, value, ttl, tableName)
-initConnection()
}
class CacheAdapterMemcache {
+connect(hostConf)
+add(...)
+del(...)
+get(...)
+update(...)
-getRealKey(tableName, key)
}
class CacheAdapterMemcached {
+connect(hostConf)
+add(...)
+del(...)
+get(...)
+update(...)
-getRealKey(tableName, key)
}
LtCacheHandle --> LtCacheAdapter : "委托"
CacheAdapterMemcache ..|> LtCacheAdapter
CacheAdapterMemcached ..|> LtCacheAdapter
组件E:配置构建器(多节点/主从)
- 功能:按 group/node/role 组织缓存服务器配置,支持默认配置继承与覆盖。
- 用途:便于在不同环境(开发/测试/生产)切换不同的缓存集群与角色。
依赖关系分析
- 业务服务依赖:
- URL 字段缓存预热依赖数据库访问(DB Facade)进行批量查询。
- 模板缓存清理依赖文件系统工具。
- 通用 KV 缓存依赖 LtCacheAdapter 及其具体实现。
- 耦合度:
- 通过 LtCacheAdapter 解耦业务与具体缓存后端,降低耦合。
- URL 预热与 DB 访问通过门面隔离,便于替换与测试。
- 外部依赖:
- Memcache/Memcached 扩展需在生产环境启用。
- 文件系统路径需具备读写权限。
graph LR
Svc["后台服务"] --> U["UrlBuilder"]
Svc --> H["LtCacheHandle"]
H --> A1["CacheAdapterMemcache"]
H --> A2["CacheAdapterMemcached"]
U --> D["DB Facade"]
Svc --> F["文件系统"]
性能考量
- 热点数据缓存:
- 优先使用进程内缓存(如 URL 字段缓存)减少跨进程/网络开销。
- 对于跨进程共享的热点数据,使用 Memcache/Memcached 作为二级缓存。
- 查询结果缓存:
- 针对高并发读、低频率写的查询结果,设置合理 TTL,并结合版本号或时间戳作为键的一部分,避免脏读。
- 配置信息缓存:
- 系统配置变更时主动失效对应缓存键,或在配置加载时附带版本标识。
- 批量操作:
- 使用 IN 查询批量回填字段,减少往返次数。
- 内存优化:
- 控制进程内缓存大小,避免无限增长;对大对象考虑序列化压缩。
- 对 KV 缓存设置合理的过期时间与最大内存限制。
故障排查指南
- 模板缓存未刷新:
- 确认是否在主题/模块更新后调用了缓存清理服务。
- 检查目标目录是否存在及是否有写入权限。
- 登录成功后缓存不一致:
- 检查登录流程是否正确触发缓存清理逻辑。
- 缓存键冲突:
- 确认键生成策略包含足够区分维度(如表名前缀、业务域、版本号)。
- 缓存不可用:
- 检查 Memcache/Memcached 扩展是否启用、连接参数是否正确。
- 观察适配器返回的错误码与日志。
结论
DouPHP 后台服务层在缓存方面提供了进程内预热、文件系统缓存清理以及可插拔的 KV 缓存抽象,能够满足常见的热点数据、查询结果与配置信息的缓存需求。结合合理的键生成策略、失效机制与一致性方案,可在保证数据正确性的前提下显著提升性能。建议在关键路径引入监控指标与告警,持续优化缓存命中率与内存占用。
附录
- 缓存层级建议:
- L1 进程内缓存:短生命周期、高热度数据(如 URL 字段、小字典)。
- L2 分布式缓存:跨进程共享的热点数据(如商品详情、配置快照)。
- L3 持久化存储:数据库与文件存储,作为最终一致性来源。
- 键生成策略建议:
- 采用“域:实体:标识:版本”的结构,便于局部失效与灰度。
- 失效机制建议:
- 写扩散:更新时主动删除或更新相关缓存键。
- 读扩散:读取时发现过期则回源并重建缓存。
- 一致性保障:
- 先更新数据库,再删除缓存;或采用延迟双删策略。
- 对强一致场景,增加版本号校验或分布式锁。
- 监控与恢复:
- 记录命中率、延迟、错误率;异常时自动降级至直连数据库。
- 定期巡检缓存集群健康与内存水位。