Database 皮肤:渲染流程中的缓存
针对
file_system=database的前台 Liquid 渲染,按请求时序说明各层缓存的 Key 格式、存储内容、TTL、失效时机。
皮肤安装 / OSS / 同步链路见 database-theme-flow.md;与 file 皮肤共用的装修数据缓存见 theme-decoration-and-liquid-rendering.md。
一、渲染时缓存命中顺序
一次典型前台页面请求(非 checkout),database 皮肤相关缓存大致按以下顺序参与:
flowchart TB REQ[HTTP 请求] --> FETCH[HomeBaseController::fetch] FETCH --> CTX["Context->themeAssets<br/>(请求级内存)"] CTX --> RA["Redis themeAssets<br/>Hash 全量或命中"] RA -->|miss| DB1[(o_theme_asset 主库)] FETCH --> LAYOUT[读 layout liquid 文本] LAYOUT --> AST["磁盘 Liquid AST<br/>runtime/liquid/cache/{themeName}/"] FETCH --> TD[ThemesService::themeData] TD --> TS["Redis themeSections<br/>积木块列表"] TD --> TF["Redis themeFiles<br/>/config /sections /locales"] TD --> TV["Redis themeView<br/>视角列表"] TD --> TG["Redis themeGroupId<br/>组装修 obj→group_id"] RENDER[Liquid 渲染 TagSection 等] --> LC["Redis liquidcache:*<br/>HTML/CSS/JS 片段"] RENDER --> OSS[OSS public_url<br/>/assets 静态资源<br/>不走 Redis 读内容]
要点:
- Liquid 模板正文(layout / templates / sections / snippets)走
themeAssets→ 请求内Context->themeAssets,不走themeFiles。 themeFiles在渲染中用于 按目录批量读小文件(config、locales、sections schema 解析),与themeAssets是两套 Redis Hash。/assets下 css/js/图片 存 OSS;Redis 里虽有整行o_theme_assetJSON(含public_url),但 不会把 OSS 文件内容载入缓存,URL 由asset_urlfilter 拼出。
二、店铺级 Key 前缀
CacheKeyHelper::store() 返回 {storeId}:,例如店铺 12345 的前缀为 12345:。
下文示例均假设 storeId = 12345、themeId = 99。
三、Database 专属:模板资产缓存
3.1 请求级内存 — Context->themeAssets
| 项 | 说明 |
|---|---|
| 写入时机 | HomeBaseController::fetch() 开头,file_system == database 且非 checkout |
| 代码 | app("Context")->themeAssets = ThemeAssetService::getThemeAssetsByDatabase($themeId) |
| 结构 | 与 Redis themeAssets Hash 相同:field → JSON 字符串 |
| 生命周期 | 单次 HTTP 请求;所有 OemsaasDatabase::readTemplateFile()、getOneThemeAssetContent() 只读此数组 |
3.2 Redis Hash — themeAssets(渲染主路径)
| 项 | 值 |
|---|---|
| Redis Key | {storeId}:themeassets:theme_id:{themeId} |
| Helper | CacheKeyHelper::themeAssets($themeId) |
| Hash Field | md5("{themeId}_{filePath}"),filePath 形如 /layout/theme.liquid、/sections/header.liquid |
| Helper(Field) | CacheKeyHelper::themeAssetsFileKey($themeId, $filePath) |
| Value | o_theme_asset 整行 JSON(含 content、content_oss_bucket、public_url、checksum 等) |
| TTL | 1 天(SysConst::ONE_DAY_SECONDS) |
| 填充 | Key 不存在时:ThemeAssetModel 按 store_id + theme_id 全表查主库 → hMSet + expire |
| 读取 | getOneThemeAssetContent() 只取 JSON 里的 content 字段(liquid/json 文本);OSS 大文件 content 通常为空 |
Field 示例:
Key: 12345:themeassets:theme_id:99
Field: md5("99_/layout/theme.liquid") → 例如 a1b2c3...
Value: {"id":1,"theme_id":99,"folder":"/layout","file_name":"theme.liquid","content":"{% ... %}","public_url":"",...}
失效 / 更新:
| 操作 | 行为 |
|---|---|
| API 新增 asset | 仅 del themeFiles;不动 themeAssets(下次全量 key 过期后重建) |
| API 更新 asset | 若 themeAssets key 已存在:hSet 单 field 更新;并 del themeFiles |
| API 删除 asset | hDel 对应 field;del themeFiles |
| Key 不存在 | 下次 getThemeAssetsByDatabase 从 DB 全量重建 |
排障:改 liquid 后前台仍旧内容 → 查 Redis key 是否仍在 TTL 内且 field 未更新;或强制
DEL 12345:themeassets:theme_id:99。
3.3 Redis Hash — themeFiles(按目录,辅助路径)
| 项 | 值 |
|---|---|
| Redis Key | {storeId}:themes:{themeId}:files |
| Helper | CacheKeyHelper::themeFiles($themeId) |
| Hash Field | 目录名,如 /config、/sections、/locales |
| Value | 该 folder 下所有 o_theme_asset 行的 JSON 数组 |
| TTL | Key 级 1 天(每次 hSet 后 expire) |
| 返回给调用方 | getContentsByFolder() 转为 file_name => content 映射(仅 content 列) |
渲染中的调用点(database 皮肤):
| 场景 | 方法 | Folder |
|---|---|---|
读 settings_schema.json / settings_data.json | ThemesService::getThemeConfigFromDatabase() | /config |
Section liquid 内 {% schema %} 解析 | ThemesService::getThemeSectionsConfigFromDatabase() | /sections |
| 前台语言包 | LangService | /locales |
与 themeAssets 的区别:
themeAssets | themeFiles | |
|---|---|---|
| 粒度 | 单文件 field(md5 路径) | 整目录一个 field |
| 渲染读 liquid 模板 | 是(主路径) | 否(除非走 getContentsByFolder) |
| 含 OSS 行 | 是(整行 JSON) | 是(但返回值只用 content) |
| 更新策略 | 增量 hSet / hDel | 任意 asset 变更 del 整个 Hash |
四、Liquid 解析 AST — 本地磁盘
| 项 | 值 |
|---|---|
| 路径 | runtime/liquid/cache/{themeName}/ |
| 文件名 | md5(liquid 源码字符串) |
| 内容 | 序列化后的 Liquid AST(TagTemplate / TagSection / TagInclude 等) |
| TTL | 90 天(3600 * 24 * 90) |
| 配置 | extend/liquidExtend/file/File.php |
说明:database 与 file 皮肤共用;改 liquid 源码后若 AST 文件仍在,可能继续用旧 AST,直到源码 hash 变化或手动清目录。这与 Redis themeAssets 是独立一层。
五、装修数据缓存(database / file 共用)
渲染 TagGetBlocks / themeData 时会用到以下 Redis 缓存(不区分 file_system)。
5.1 themeSections — 页面积木块列表
| 项 | 值 |
|---|---|
| Redis Key | {storeId}:themes:{themeKey}:sections |
| Helper | CacheKeyHelper::themeSections($themeKey) |
| themeKey | 默认主题非 preview 时用 default;否则为具体 themeId |
| Hash Field | {route} 或 {route}_{routeHandle};组装修时为 {route}:routeGroup:{groupId} |
| Value | sectionsFormat 之后的 blocks JSON 数组 |
| TTL | 1 天 |
| 条件 | 仅 default=1 且非 preview 时写入 |
示例:
Key: 12345:themes:default:sections
Field: index
Field: product_my-handle
Field: index:routeGroup:42
5.2 themeData — 默认主题行缓存
| 项 | 值 |
|---|---|
| Redis Key | {storeId}:themes:{themeKey}:data |
| Value | o_store_theme 行 JSON(含 params 等) |
| TTL | 1 天 |
| 用途 | 取默认主题信息;非每次 render 必 hit |
5.3 themeView — 视角列表
| 项 | 值 |
|---|---|
| Redis Key | themes_view:{storeId}{themeId}(CacheKeyHelper::themeView) |
| Value | o_theme_view 列表 JSON |
| TTL | 有数据 1 天;空列表 1 小时(EMPTY_REDIS_HOLDER) |
| 用途 | ThemeViewService / LiquidCacheService 取 viewId |
5.4 themeGroupIdByObjIdAndType — 组装修 obj → group_id
| 项 | 值 |
|---|---|
| Redis Key | theme_group_id:{storeId}{themeId}:{objId}:{objType} |
| Value | 整数 group_id |
| TTL | 1 天 |
| 用途 | 商品/专辑页匹配装修分组 |
六、渲染结果片段缓存(HTML / CSS / JS)
由 LiquidCacheService 与 section 上 cached="true" 配合;含 themeId、viewId,database 与 file 一致。
6.1 Section 级 — liquidcache:*
| 类型 | Key 格式 | TTL |
|---|---|---|
| HTML | liquidcache:html:{storeId}{name} | 约 2 分钟(header 等) |
| CSS | liquidcache:css:{storeId}{name} | 同上 +1s |
| JS | liquidcache:js:{storeId}{name} | 同上 +1s |
{name} 示例:99:USD:header 或带自定义 cache_key 后缀。
索引 ZSet:liquidcache:keys:{storeId}list,member 为上述 key,score 为过期时间戳。
6.2 首页整页 — home_index_cache:*
| 后缀 | Key 示例 |
|---|---|
| html | home_index_cache:12345:{domainId}:99:{viewId}:USD:html_string |
| css | ...:style_sheet |
| js | ...:java_script |
条件:非主域名、非主题编辑 Cookie、店铺配置开启等(见 LiquidCacheService::getIndexRenderCache)。
6.3 专辑详情页 — home_collection_detail_cache:*
home_collection_detail_cache:{storeId}:{domainId}:{themeId}:{viewId}:{collectionId}:{currency}:{page}:html_string
(另有 style_sheet / java_script 后缀。)
七、当前未启用 / 非渲染路径
| Key | 状态 | 说明 |
|---|---|---|
themeConfigs | 代码中已注释 | section schema 缓存曾计划启用,现每次走 getContentsByFolder('/sections') |
themeDbData | 无引用 | CacheKeyHelper::themeDbData 仅定义,业务未使用 |
themeZipKey | 打包下载 | themeZip() 用,不参与前台 render |
| OSS 对象 | 不走 Redis | /assets 通过 public_url + CDN/OSS 直链 |
八、失效关系速查
| 业务操作 | 建议清理 / 自动行为 |
|---|---|
ThemeTool PUT themeasset | updateAssetsCache 增量更新 themeAssets field + del themeFiles |
| 新增 themeasset | del themeFiles;themeAssets 等 TTL 或手动 DEL |
| 删除 themeasset | hDel themeAssets field + del themeFiles |
改 settings_data.json | 触发 initThemeDataBySettingData;装修缓存见 ThemesService 内 del themeData / themeSections |
| 改 blocks / 设默认主题 | del themeData('default'), del themeSections('default') |
| 改视角 | ThemeViewCacheService::delCache |
| 改组装修 | ThemeRouteGroupCacheService::cleanCacheByObjIdAndType |
| 改 liquid 仍见旧版 | 查 themeAssets + 磁盘 AST 两层 |
九、与 file 皮肤差异(仅缓存层)
| 缓存层 | file | database |
|---|---|---|
| 模板正文来源 | 磁盘 public/theme/{name}/ | Redis themeAssets + DB |
themeAssets / themeFiles | 一般不用 | 渲染必用 |
| Liquid AST 磁盘 | 有 | 有(相同) |
themeSections / 片段 HTML | 有 | 有(相同) |
| 静态资源 | 本地 /theme/.../assets/ | OSS URL(Redis 只存元数据) |
十、代码索引
| 职责 | 路径 |
|---|---|
| 预加载 themeAssets | app/home/HomeBaseController.php — fetch() |
| themeAssets / themeFiles CRUD 缓存 | common/services/ThemeAssetService.php |
| DB 读模板 | extend/liquidExtend/fileSystem/OemsaasDatabase.php |
| Key 定义 | extend/helper/CacheKeyHelper.php |
| 积木块 Redis | common/services/ThemesService.php — getThemeSectionsData |
| 组装修 blocks | common/services/ThemeRouteGroupCacheService.php |
| 视角 | common/services/ThemeViewCacheService.php |
| HTML 片段 | common/services/LiquidCacheService.php |
| AST 磁盘 | extend/liquidExtend/file/File.php |