PAI-Rec 特征缓存

更新时间:
复制 MD 格式

介绍 PAI-Rec 两种本地特征缓存的选型与配置:FeatureViewCache 把视图全量缓存供状态过滤直接读取,FeatureCache 按需共享缓存供场景特征配置复用。

PAI-Rec 特征缓存

PAI-Rec 引擎提供两种本地特征缓存,用于降低特征读取的在线请求量与过滤/取特征的耗时,二者机制不同、适用场景不同,可以同时使用。

  • FeatureViewCache(全量视图缓存):把 FeatureStore 中的一个特征视图全量加载到引擎本地内存并持续同步,当前由状态过滤(ItemStateFilter)消费。

  • FeatureCache(按需共享缓存):按请求的 key 按需从 FeatureStore 取数并缓存,按名字全局定义、可被多个场景的特征配置复用。

如何选择

维度

FeatureViewCache

FeatureCache

缓存内容

指定视图的全部数据行

请求实际用到的那批 key 对应的数据行

加载方式

引擎启动/热加载时全量扫描,之后持续同步

首次访问时按 key 回源,命中后不再回源

内存占用

与视图总行数成正比,常驻不淘汰

受 CacheSize 约束,写满后按 LRU 淘汰

数据时效

Stream 视图秒级增量同步;Batch 视图按 RefreshIntervalMinutes 全量刷新

写入后 CacheTime 内保持不变,TTL 不因命中续期

数据视角

仅按视图的 join id 读取,实际消费方是 item 维度

user 或 item;数据源可以是视图,也可以是"模型 + 实体"

消费方

目前为状态过滤(ItemStateFilter)

特征配置 FeatureConfs / UserFeatureConfs

典型适用

候选集大、状态需秒级生效、视图行数可控

特征读取量大但 key 分散,可容忍 TTL 窗口内的陈旧

不适用

视图行数超出单机内存可承载规模

需要整表本地化,或需要严格读取最新值

选型建议:

  1. 目标是"不再逐 item 在线点查",且需要状态变更秒级生效,视图行数能被单机内存容纳 → 用 FeatureViewCache。

  2. 目标只是"减少重复点查",key 分散、每次请求命中的 item 占比低 → 用 FeatureCache,用 CacheSize + CacheTime 控制内存与陈旧窗口。

  3. 两类需求同时存在时,可以并存:状态过滤走 FeatureViewCache,FeatureConfs / UserFeatureConfs 的特征加载走 FeatureCache。

FeatureViewCache:特征视图全量缓存

功能说明

FeatureViewCache 把 FeatureStore 中的某个特征视图全量加载到推荐引擎本地内存,并持续与源视图保持同步,供状态过滤(ItemStateFilter)等模块直接读取。启用后,状态过滤不再对每个 item 发起在线点查,而是直接命中本地缓存。

适用场景

推荐在以下场景启用:

  • 状态过滤依赖实时表(Stream 特征视图),如下线、失效、库存、审核状态等需要秒级生效的过滤场景。

  • 过滤候选集大、状态字段相对稳定,全量缓存后本地判定,避免大规模在线点查。

  • 希望降低 FeatureStore/FeatureDB 的在线查询压力,把高频状态查询收敛为本地读取加后台同步。

以下场景需谨慎或改用其他方式:

  • 视图数据量极大,远超单机内存可承载的规模。

  • 状态只需离线级时效,可直接使用定时全量刷新的离线视图。

核心能力

能力

说明

全量本地缓存

启动时将特征视图全量加载到本地内存

实时增量同步

Stream 视图的数据变更秒级同步到本地缓存

定时全量校准

周期性全量刷新,兜底纠正删除与数据漂移

按名复用

全局定义一份缓存,可被多处按名引用,避免重复加载

配置热加载

修改配置后自动生效,更新过程不阻塞在线请求

高可用降级

缓存未就绪时状态过滤自动放行,避免推荐结果被清空

数据时效性

  • Stream(实时)视图:源数据变更后,增量更新在秒级同步到本地缓存,状态过滤随即生效,是高时效状态过滤的推荐选择。

  • Batch(离线)视图:按 RefreshIntervalMinutes 定时全量刷新。

  • 全量刷新间隔下限为 60 分钟:全量扫描有一定成本,为避免对 FeatureDB 造成频繁全量扫描压力,配置值小于 60(含未配置)时按 60 处理。该下限只约束定时全量校准,Stream 视图的秒级增量同步不受影响。

配置缓存定义

在 FeatureViewCacheConfs 中按名字定义缓存,一份定义可被多处引用:

{
  "FeatureViewCacheConfs": {
    "item_status_cache": {
      "FeatureStoreName": "pairec_fs",
      "FeatureStoreViewName": "item_status_view",
      "SelectFields": "status,is_valid,category",
      "RefreshIntervalMinutes": 60
    }
  }
}

字段

类型

是否必填

说明

FeatureStoreName

string

是

引用数据源配置(FeatureStoreConfs)中已配置的 FeatureStore 名称

FeatureStoreViewName

string

是

FeatureStore 项目中的特征视图名,实时状态过滤建议使用 Stream 视图

SelectFields

string

否

需要缓存的字段,逗号分隔,默认为 *(全部字段)。视图主键和事件时间字段可直接填写,分区字段不可填写

RefreshIntervalMinutes

int

否

定时全量刷新间隔,单位分钟,默认值与下限均为 60

配置校验说明:

  • FeatureStoreName 在 FeatureStoreConfs 中不存在、FeatureStoreViewName 在项目中不存在,或 SelectFields 包含视图未定义的字段时,该缓存不会被创建,并输出 module=FeatureViewCache ... error=init failed 日志;引用它的状态过滤在构建阶段报错。请先按该日志定位并修正配置。

  • SelectFields 必须覆盖过滤条件(FilterParams)用到的全部字段,否则过滤判定会读到空值。

状态过滤引用缓存

在 ItemStateFilter 的 ItemStateDaoConf 中通过 FeatureViewCacheName 引用缓存:

{
  "FilterConfs": [
    {
      "Name": "item_status_filter",
      "FilterType": "ItemStateFilter",
      "ItemStateDaoConf": {
        "FeatureViewCacheName": "item_status_cache",
        "DefaultFieldValues": { "status": 1 }
      },
      "FilterParams": [
        { "Name": "status", "Domain": "item", "Operator": "equal", "Type": "int", "Value": 1 }
      ]
    }
  ]
}

字段

类型

是否必填

说明

FeatureViewCacheName

string

使用缓存时必填

引用 FeatureViewCacheConfs 中的缓存名

DefaultFieldValues

map

否

视图中不存在该 item 时用于判定的兜底属性值

FilterParams

[ ]object

否

状态过滤条件,语义与直接读取 FeatureStore 的状态过滤一致

说明:

  • 配置了 FeatureViewCacheName 后优先走本地缓存,无需再配置 AdapterType、ItemFieldName、WhereClause、SelectFields。

  • 缓存按 item 维度读取(依据视图对应的特征实体 join id),因此配置的视图应使用 item 实体。其他模块如需读取同一视图,需要单独接入。

可用性与降级

  • 首次加载未完成或 FeatureDB 故障导致缓存未就绪:状态过滤自动放行(不拦截 item),避免缓存不可用期间把推荐结果整体清空;后台以 30 秒间隔自动重试,直到首次全量加载成功。

  • 缓存配置被移除导致引用失效:状态过滤同样放行,并在日志中提示缓存不可用。

  • 缓存就绪后:视图中确实不存在的 item,按 DefaultFieldValues 或空值判定,与直接读取 FeatureStore 的状态过滤行为一致。此时如果 SelectFields 未覆盖 FilterParams 用到的字段,可能出现全部 item 被过滤的情况。

  • 配置热加载期间:在线请求读取不阻塞;新配置会先构建并完成预热,成功后才原子切换到新实例,构建失败则继续使用旧实例。

最佳实践

  • SelectFields 只取必要字段,减少内存占用与加载网络开销。

  • 评估单机内存:占用约为「条目数 × 每条字段规模」,10 万级条目、少量字段通常为几十 MB 量级。

  • 缓存名保持语义化(如 item_status_cache),同一视图只定义一份缓存。

  • 高时效场景优先使用 Stream 视图,实时性由增量同步保证,定时全量刷新只作兜底校准,60 分钟通常足够。

常见问题

Q1:日志报 feature view cache not found, name:xxx?

说明引用方找不到该缓存,依次检查:

  1. FeatureViewCacheConfs 中是否存在该名字(大小写需一致)。

  2. 该缓存的 FeatureStoreName 是否能在 FeatureStoreConfs 中找到。

  3. FeatureStoreViewName 是否为项目中真实存在的视图名。

  4. 向前查找 module=FeatureViewCache ... initial load failed 的具体原因,该条日志才是根因。

Q2:为什么刷新间隔最小只能 60 分钟,会影响实时性吗?

不会。全量扫描有成本,限制下限是为避免频繁全量扫描 FeatureDB。Stream 视图的秒级增量同步与定时全量刷新是两条独立机制。

Q3:缓存还没加载完或 FeatureDB 临时故障时,会不会把 item 全部过滤掉?

不会。缓存未就绪时状态过滤自动放行,后台自动重试直到恢复。

Q4:一次全量扫描返回空结果,会不会被当作"视图为空"而清空缓存?

会。成功返回的空扫描被视为视图真实为空,按有效结果处理:缓存会据此清理过期条目并标记为就绪。若上线时上游数据尚未产出,需确认视图已有数据,或为过滤配置 DefaultFieldValues。

FeatureCache:按需共享特征缓存

功能说明

FeatureCache 是由 FeatureCacheConfs 统一定义的按需缓存:缓存定义同时持有容量/TTL 与取数来源(数据源、视图或模型、读取字段、SDK 参数)。场景侧的特征配置只需要引用缓存名,并在请求时提供查找 key,不再各自重复配置数据源与缓存参数。

该能力主要解决两个问题:

  • 同一份特征被多个场景反复加载,每个场景各自维护一份私有缓存,数据源与字段定义分散、容易不一致。

  • 希望在一个地方统一控制某类特征的缓存容量与过期时间。

适用场景

  • 多个场景(或多个 FeatureLoadConfs)读取同一份 user 或 item 特征。

  • 特征读取量大但 key 分散,逐次点查成本高,且能接受 CacheTime 窗口内的陈旧。

  • 希望统一收敛某类特征的缓存策略(容量、TTL、读取字段)。

不适用于:需要整表本地化、需要严格读取最新值(应使用 FeatureViewCache 或直接读取 FeatureStore)。

配置缓存定义

{
  "FeatureCacheConfs": {
    "user_profile_cache": {
      "FeatureStoreName": "pairec_fs",
      "FeatureStore": "user",
      "FeatureStoreViewName": "user_profile_view",
      "SelectFields": "age,city,level",
      "CacheSize": 100000,
      "CacheTime": 600
    },
    "item_profile_cache": {
      "FeatureStoreName": "pairec_fs",
      "FeatureStore": "item",
      "FeatureStoreModelName": "rank_v1",
      "FeatureStoreEntityName": "item",
      "CacheSize": 50000,
      "CacheTime": 3600
    }
  }
}

字段

类型

是否必填

说明

FeatureStoreName

string

是

引用 FeatureStoreConfs 中已配置的 FeatureStore 名称

FeatureStore

string

是

枚举值 user / item,表示缓存的视角。同名缓存不允许在热加载时改变该值

FeatureStoreViewName

string

二选一

特征视图名,配置后按视图取数

FeatureStoreModelName

string

二选一

模型名,与 FeatureStoreEntityName 一起使用,表示按"模型 + 实体"取该实体的特征

FeatureStoreEntityName

string

二选一

特征实体名,仅在配置模型时使用

SelectFields

string

否

视图投影字段,逗号分隔,默认 *。仅视图模式支持,模型模式不支持

FeatureStoreOptions.DlrmHSTU

bool

否

是否启用 DlrmHSTU 取数。仅 user 视角支持

CacheSize

int

是

缓存条目上限,必须大于 0;写满后按 LRU 淘汰

CacheTime

int

否

写入后的过期时间,单位秒,默认 3600

配置校验规则(不满足时引擎启动/配置加载直接报错):

  • FeatureStoreViewName 与 FeatureStoreModelName / FeatureStoreEntityName 互斥:视图模式不能同时配模型与实体;模型模式必须同时配模型和实体,且不能配 SelectFields。

  • 模型模式下,FeatureStoreEntityName 必须是该模型实际使用的实体;user 视角的模型需要存在 label 表。

  • 视图/模型/实体在 FeatureStore 中不存在时,加载即报错。

场景引用缓存

场景侧在 FeatureDaoConf 中通过 FeatureCacheName 引用,并保留查找键与落点配置:

{
  "FeatureConfs": {
    "scene_name": {
      "FeatureLoadConfs": [
        {
          "FeatureDaoConf": {
            "FeatureCacheName": "user_profile_cache",
            "FeatureKey": "user:uid",
            "FeatureStore": "user"
          }
        }
      ]
    }
  }
}

字段

类型

是否必填

说明

FeatureCacheName

string

是

引用 FeatureCacheConfs 中的缓存名

FeatureKey

string

是

查找键来源,如 user:uid、item:id,含义与普通特征配置一致

FeatureStore

string

是

特征写入的落点,user / item,必须与缓存定义的视角一致

引用时的约束(不满足时构造特征 DAO 阶段直接报错):

  • FeatureCacheName 目前只支持 featurestore 适配器:场景侧必须保留 "AdapterType": "featurestore"。写 hologres、tablestore、redis 等其他值,或省略该字段,都会在构建特征 DAO 阶段报错 FeatureCacheName is only supported by the featurestore adapter。这个字段在该路径下不参与选路(选路由 FeatureCacheName 决定),仅作校验,但仍然必填。
    如果特征存放在 Hologres 等其他数据源,不能使用共享缓存;需要缓存时只能用旧路径的 DAO 私有缓存(在 FeatureDaoConf 上配 CacheSize / CacheTime)。

  • 不允许与 FeatureAppendKey 同时使用。

  • 不允许在场景侧重复配置数据源、读取字段与 SDK 参数(FeatureStoreName、FeatureStoreViewName、FeatureStoreModelName、FeatureStoreEntityName、UserSelectFields、ItemSelectFields、FeatureStoreOptions),这些统一由 FeatureCacheConfs 提供。其中 FeatureStoreName 会被忽略并打印告警。

  • FeatureStore 必须与缓存定义的视角一致,否则报错。

  • LoadFromCacheFeaturesName 在本路径下不生效(只有未识别适配器的空实现才会从 cacheFeatures 回读):两条特征配置要复用同一份数据,请让它们引用同一个 FeatureCacheName,不要用 cacheFeatures 中转。

  • user 视角下的 sequence feature 加载暂不支持共享缓存,请使用视图/模型路径。

在 UserFeatureConfs 中使用

UserFeatureConfs(User 特征预取)同样支持 FeatureCacheName,写法与 FeatureConfs 一致:

{
  "UserFeatureConfs": {
    "scene_name": {
      "AsynLoadFeature": true,
      "FeatureLoadConfs": [
        {
          "FeatureDaoConf": {
            "FeatureCacheName": "user_profile_cache",
            "FeatureKey": "user:uid",
            "FeatureStore": "user"
          }
        }
      ]
    }
  }
}

在该配置块中使用时额外注意:

  • UserFeatureConfs 要求 FeatureStore 必须为 user(否则配置加载报错),因此只能引用 FeatureStore 为 user 的缓存定义,item 视角的缓存无法在此复用。

  • 场景侧不要再保留 FeatureStoreName,共享路径不读取它并会打印 ignoredConfig 告警;保留 FeatureStoreViewName、UserSelectFields 等字段会直接报错。

  • user 视角不回填空值:回源取不到数据时本次加载失败,不会把空结果写入缓存,与 item 视角不同。

  • 热加载按场景整体签名比对(FeatureLoadConfs 全部字段参与),修改 FeatureCacheName 会触发该场景重建。

读取与缓存行为

  • 命中:直接使用缓存值,不再访问 FeatureStore。item 视角下可在 debug 输出中看到命中标记。

  • 未命中:按 FeatureKey 回源取数,取到后写入缓存;同一请求内多个 item 会合并为一次批量查询。

  • item 视角的空结果:批量查询未返回的 item,会以空结果写入缓存,并在 CacheTime 内不再回源。视图数据补齐后需要等待 TTL 过期才能读到新值,CacheTime 不宜设置过大。

  • user 视角的空结果:回源未取到数据时本次加载失败,不会写入空值。

  • 过期与淘汰:条目在 CacheTime 后过期,命中不续期;条目数达到 CacheSize 后淘汰最久未使用的条目。

  • 数据副本:从缓存取出的属性只在顶层做隔离副本,嵌套的 map / slice 为只读共享,消费方不得原地修改嵌套值。

配置热加载

  • 定义完全一致时沿用原实例,已有缓存数据保留。

  • 定义中任意字段变化(含 CacheSize / CacheTime 等缓存策略,以及数据源、视图/模型、读取字段、SDK 参数)都会重建缓存实例,已有缓存数据清空后重新预热。调大容量或调整 TTL 同样会带来一次冷启动,建议在低峰期操作。

  • 缓存定义非法时本次热加载整体失败并保留原配置,可按日志 reload config error 定位。

  • 同名缓存不允许在 user / item 之间切换视角,需要新的缓存名。

  • 某个缓存名从配置中删除后,已构建的特征 DAO 会按构造时的定义直接查询 FeatureStore(不再走缓存);新建的特征 DAO 引用已删除的缓存名时会报错。

  • 热加载期间在线请求不阻塞;新配置构建失败时保留原有缓存实例。

最佳实践

  • 缓存名按数据的业务含义命名(如 user_profile_cache),一个视图/模型对应一份缓存。

  • SelectFields 只取实际用到的字段;模型模式下字段由模型定义决定,无需配置。

  • CacheSize 按常驻内存评估:条目数 × 每条字段规模;CacheTime 按业务对陈旧度的容忍度设置。

  • 场景侧只写 FeatureCacheName、FeatureKey、FeatureStore 与落点相关配置,不要在场景里重复数据源与字段。

  • 需要秒级新鲜度的特征不要用本条路径,改用 FeatureViewCache 或直查 FeatureStore。

常见问题

Q1:启动报 feature cache not found, name:xxx?

引用方找不到该缓存。检查 FeatureCacheConfs 中是否存在该名字(大小写需一致),以及该缓存定义是否校验通过。

Q2:为什么场景里配了 FeatureStoreName 却不生效?

共享缓存的取数来源由缓存定义持有,场景侧的 FeatureStoreName 会被忽略并打印告警。请把数据源配到 FeatureCacheConfs。

Q3:item 特征查不到时,后续请求会一直查不到吗?

item 视角下未返回的 key 会以空结果缓存,并在 CacheTime 内不再回源。如果视图数据会延迟补齐,请把 CacheTime 调小,或确认上游写入是否正常。

Q4:多个场景能共用一份缓存吗?

可以。缓存按名全局定义,多个场景用相同的 FeatureCacheName 引用即可共用同一份数据;FeatureStore 视角必须与缓存定义一致。