AWS 大数据深度剖析(第 8 部分):在线特征存储 —— DynamoDB、ElastiCache 与 OpenSearch k-NN
推荐系统如何在推理时提供特征服务:用 DynamoDB 存用户特征、用 ElastiCache 做热点缓存、用 OpenSearch k-NN 做向量召回、用 Neptune 做图检索。
为什么需要在线服务层?
一个推荐请求必须在 200ms 内返回。你不可能在这样的延迟下去查询数据仓库或数据湖 —— 你需要一个专门的在线服务层。
本章将介绍四种在线存储系统 —— DynamoDB、ElastiCache(Redis)、OpenSearch k-NN 和 Neptune,说明各自最擅长什么,以及如何在它们之间做选择。
整体职责划分
一个推荐请求(GET /feed?user_id=123)可能会查询四种不同的在线存储:
Recommendation Service (200ms budget)
│
├─[10ms]── DynamoDB : Fetch user features + recall pool cache
├─[2ms]─── Redis : Fetch real-time behavior sequence + last recommendation cache
├─[20ms]── OpenSearch k-NN: User vector → ANN find items
└─[50ms]── Neptune : Second-degree friends / graph recall (on demand)
│
▼
SageMaker Endpoint (ranking model scoring, 30ms)
│
▼
Return Top 10
关键洞察:每种存储都有自己的“主场” —— 避免混淆职责。
Amazon DynamoDB
它是什么
AWS 托管的 KV / 文档数据库。最重要的几个特性:
- 毫秒级读写(P99 低于 10ms)
- 自动扩缩容至数十万 QPS
- 完全 Serverless(按读/写单元和存储计费)
- 无固定 Schema(每一行可以有不同的字段)
数据模型
Table: user_features
Partition Key: user_id (String)
Sort Key: <optional>
Item:
{
"user_id": "12345",
"age": 25,
"city": "Shanghai",
"tags": ["food", "travel"],
"last_5_clicks": ["v_001", "v_002", ...],
"ctr_7d": 0.054,
...
}
每一行称为一个 Item,单个 Item 的最大尺寸为 400 KB。
计费模型
两种容量模式:
| 模式 | 计费方式 | 适用场景 |
|---|---|---|
| On-Demand(按需) | $0.25 每百万 RRU(读请求单元);$1.25 每百万 WRU(写请求单元) | 不可预测或突发流量 |
| Provisioned(预置) | 按预留的 RCU / WCU 计费,支持 Auto Scaling | 稳定流量,最高可便宜 70% |
RRU / WRU 计费细节(常见坑):
- 1 RRU = 一次强一致性读,最多 4 KB;最终一致性读 = 0.5 RRU(每 4 KB)
- 1 WRU = 一次写入,最多 1 KB
- 大对象会向上取整(读 4 KB,写 1 KB):读取一个 5 KB 的对象 = 2 RRU
- 事务性读/写费用翻倍
在估算时,首先要确定你需要强一致性还是最终一致性 —— 仅这一点就能让成本相差 2 倍。对于推荐特征查询,最终一致性通常就足够了,因此按每次读取 0.5 RRU 来估算。
在客户场景中的三种角色
| # | 用途 | Schema | 数据来源 |
|---|---|---|---|
| 1 | 用户特征(user_features) | user_id 映射到 100+ 个特征维度 | 数据仓库 ads_user_features 每日同步 + Flink 实时更新 |
| 2 | 召回池(recall_u2u_cf) | user_id 映射到 top-K 候选列表 | 数据仓库 ads_recall_*_pool 每日或每小时同步 |
| 3 | 实时行为序列(user_realtime) | user_id 映射到最近 N 次点击 | Flink 实时维护(消费自 MSK) |
建模最佳实践
DynamoDB 不是 MySQL —— 它无法做 JOIN 或 GROUP BY。建模时:
- 围绕访问模式设计:先想清楚“我每次会怎么查这份数据?”,再据此设置 PK / SK
- 热点分区是一大陷阱:避免任何单个 key 承接远超平均水平的 QPS(例如某个“news”分区承接了所有查询)
- 单表设计(Single-Table Design)(进阶):将多个相关实体放在一张表中,通过不同的排序键加以区分
局限性
- 单个 Item 限制为 400 KB(大对象必须拆分或存到 S3)
- 不适合复杂查询(聚合、范围扫描代价高昂)
- 不支持全文搜索或向量搜索 —— 那是 OpenSearch 的活儿
官方文档:
- 主页:https://docs.aws.amazon.com/dynamodb/
- 建模最佳实践:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html
ElastiCache (Redis)
它是什么
AWS 托管的 Redis 服务(也支持 Memcached,但生产环境几乎都用 Redis)。
关键特性:
- 亚毫秒级延迟(通常低于 1ms)
- 丰富的数据结构:String / List / Hash / Set / Sorted Set / Stream
- 弱持久化(数据默认存放在内存中;AOF / RDB 持久化会带来开销)
在客户场景中的角色
| 用途 | 说明 |
|---|---|
| 推荐结果缓存 | 同一用户 5 分钟内再次访问 —— 直接返回缓存结果 |
| 行为序列(短期) | List 数据结构天然适合维护“最近 N 次点击” |
| 限流 / 频控 | 计数器、滑动窗口 |
| 会话存储 | 用户当前会话的临时上下文 |
ElastiCache 与 DynamoDB 对比
| DynamoDB | ElastiCache (Redis) | |
|---|---|---|
| 延迟 | 5-10ms | 低于 1ms |
| 持久化 | 强 | 弱 |
| 容量 | 几乎无限 | 受内存限制(节点级 GB 到 TB) |
| 复杂数据结构 | 弱 | 强 |
| 成本 | 按使用量付费 | 按节点付费(7x24 在线) |
实践中:用 DynamoDB 作为主存储,用 Redis 作为热点缓存 + 复杂数据结构(List / Sorted Set)。
部署模式
- Cluster Mode Disabled(关闭集群模式):单主 + 多副本,简单
- Cluster Mode Enabled(开启集群模式):分片以支撑大容量
- Serverless(2023 年起):按使用量付费,零运维
官方文档:https://docs.aws.amazon.com/elasticache/
OpenSearch k-NN(向量召回)
什么是 OpenSearch
AWS 从 Elasticsearch 分叉出来的产品(2021 年基于 ES 7.10 分叉)。它提供 ES 的全部能力:
- 全文搜索
- 日志聚合
- 地理位置查询
- 向量索引(k-NN 插件)
k-NN 用法
向量召回的核心:存储双塔模型产出的物品 embedding,然后用用户向量查询最近邻。
PUT items
{
"settings": {
"index": {"knn": true}
},
"mappings": {
"properties": {
"item_id": {"type": "keyword"},
"category": {"type": "keyword"},
"embedding": {
"type": "knn_vector",
"dimension": 64,
"method": {
"name": "hnsw",
"engine": "lucene",
"parameters": {"ef_construction": 256, "m": 16}
}
}
}
}
}
POST /items/_search
{
"size": 100,
"query": {
"knn": {
"embedding": {
"vector": [0.12, -0.85, ...],
"k": 100
}
}
}
}
ANN 算法与引擎选型
OpenSearch k-NN 支持 3 种引擎(截至 2026 年):
| 引擎 | 状态 | 适用场景 |
|---|---|---|
| Faiss | 生产首选 | 大规模、需要量化(PQ/SQ)、可选 GPU 加速 |
| Lucene | 稳定 | 中小规模、纯 JVM 部署、无原生库依赖 |
| nmslib | 已弃用 | 不再推荐用于新索引 |
算法层:
- HNSW:基于图的索引,兼顾延迟与召回率(首选)
- IVF:基于聚类的倒排索引,需要训练码本,量化可节省内存
- PQ(Product Quantization,乘积量化):将向量压缩 4 到 16 倍
对于亿级规模的向量:Faiss + HNSW,并配置合适的 ef_construction / m 参数。若为超大规模做成本优化,可再叠加 PQ 量化。
OpenSearch k-NN 与 S3 Vectors 对比
S3 Vectors(2025 年预览,2025 年下半年 GA)—— 向量索引直接存储在 S3 上,按存储 + 查询计费,Serverless。
| OpenSearch k-NN | S3 Vectors | |
|---|---|---|
| 延迟 | 10-30ms | 频繁查询约 100ms;冷查询亚秒级(数百 ms) |
| 成本 | 高(节点 7x24 运行) | 低(按用量付费、按存储付费) |
| 规模 | 千万级到亿级 | 亿级到百亿级(为超大规模设计) |
| 多租户 / 隔离 | 索引级 | Bucket / 索引原生隔离 |
| 适用场景 | 推荐热路径(要求毫秒级延迟) | RAG / 冷向量 / 长尾召回 / Bedrock Knowledge Bases 后端 |
官方文档写道:“对于不频繁的查询提供亚秒级延迟,对于更频繁的查询可低至 100 毫秒。”
推荐:实时召回热路径用 OpenSearch k-NN;RAG / Knowledge Base / 大规模冷向量场景用 S3 Vectors。两者可以共存:热向量放在 OpenSearch,长尾向量下沉到 S3 Vectors。
部署
OpenSearch Service(托管),按节点小时计费。从 3 个 m6g.large 节点起步,大约每月 $400。
官方文档:
- OpenSearch k-NN:https://docs.aws.amazon.com/opensearch-service/latest/developerguide/knn.html
- S3 Vectors:https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-vectors.html
Amazon Neptune(图数据库)
它是什么
AWS 托管的图数据库。支持三种查询语言:
- Gremlin(属性图)
- SPARQL(RDF 图)
- openCypher(Neo4j 家族,2022 年起支持)
推荐场景中的图
社交类应用天然是图状结构:
(User A) -[follow]-> (User B)
(User A) -[like]-> (Post 1)
(User B) -[create]-> (Post 1)
(Post 1) -[has_tag]-> (Tag "food")
常见的图召回模式:
- 二度好友:查询 A 的好友的好友,作为候选用户
- 共同兴趣:A 和 B 都互动过同一批帖子 —— 强连接信号
- 关系传播:在图上运行 PageRank / Random Walk(随机游走)
Neptune ML(GNN)
Neptune ML 是 Neptune 内置的图神经网络(GNN)训练能力:
- 基于 DGL(Deep Graph Library)
- 自动从图数据构建训练样本
- 输出节点 / 边的 embedding
- embedding 可以喂给 OpenSearch k-NN 用于召回
Neptune 成本与决策框架
Neptune 的入门成本较高:
- db.r6g.large 实例:约每月 $330
- 增加只读副本:约 $330 x N
- 数据量和 I/O 也要计费
决策:图召回是一项进阶能力(可在 POC 第 3 阶段考虑)。先落地协同过滤 + 双塔模型,验证业务价值,再引入图召回。
官方文档:
- Neptune:https://docs.aws.amazon.com/neptune/
- Neptune ML:https://docs.aws.amazon.com/neptune/latest/userguide/machine-learning.html
离线到在线的同步策略
从数据仓库 ADS 表同步到在线存储,是离线-在线协同的关键。
同步方式
| 方式 | 工具 | 适用场景 |
|---|---|---|
| Glue Job 批量写入 | Spark | 用户特征 / 召回池的每日全量同步 |
| EMR 批量写入 | Spark | 大数据量、复杂转换 |
| Athena UNLOAD | Athena to S3 to DynamoDB Import | 一次性批量加载 |
| DynamoDB S3 Import | 直接从 S3 文件导入 | 初始化 / 全量数据加载 |
| SageMaker Feature Store | SDK | 自动管理离线/在线一致性(见第 9 章) |
同步频率
| 数据 | 频率 |
|---|---|
| 长期用户画像 | 每日 |
| 物品特征(热度) | 每小时 |
| 召回池 | 每日或每小时 |
| 实时行为序列 | Flink 实时(毫秒到秒级) |
一致性考量
数据仓库与在线存储无法保证强一致性 —— 这是设计上不可避免的。在做架构设计时:
- 在线特征可容忍最多 1 天的陈旧度
- 实时特征由 Flink 独立维护
- 在 A/B 测试期间,通过 feature flag 控制特征版本
在线层选型决策表
| 需求 | 选择 |
|---|---|
| 用户特征点查(KV) | DynamoDB |
| 召回池缓存(user 到 list) | DynamoDB |
| 短期推荐结果缓存 | Redis |
| 行为序列(短期) | Redis (List) + DynamoDB(持久化) |
| 向量召回(亿级规模) | OpenSearch k-NN |
| 图召回 / 多跳 | Neptune |
| 全文搜索 | OpenSearch(标准索引) |
| 限流 / 频控 | Redis |
客户场景:最终的在线层架构
Recommendation Service (deployed on ECS / EKS)
│
├──▶ DynamoDB (primary)
│ ├─ user_features (synced daily from data warehouse)
│ ├─ recall_u2u_cf (synced daily from data warehouse)
│ └─ user_realtime (Flink writes in real time)
│
├──▶ ElastiCache Redis
│ ├─ recommend_cache (TTL 5 min)
│ └─ rate_limit_counter
│
├──▶ OpenSearch k-NN
│ └─ item_embeddings (two-tower model output, rebuilt daily in batch)
│
└──▶ SageMaker Endpoint
└─ rank_model (ranking scores)
Optional Phase 3 addition:
└──▶ Neptune
└─ social_graph + Neptune ML embeddings
本章小结
| 服务 | 角色 | 延迟 |
|---|---|---|
| DynamoDB | 用户特征 / 召回池 / 实时序列(持久化) | 5-10ms |
| ElastiCache Redis | 热点缓存 / 复杂数据结构 / 限流 | 低于 1ms |
| OpenSearch k-NN | 向量召回(双塔物品 embedding) | 10-30ms |
| Neptune(+ Neptune ML) | 社交图 / 图召回 / GNN | 30-100ms |
下一章:ML 平台本身 —— 如何使用 SageMaker。
参考资料
- Feast documentation — Feast
- Amazon SageMaker Feature Store — AWS Documentation