如何通过Redis OM Python高效删除Feed数组元素?
问题描述
我正在使用Redis OM for Python,模型定义如下:
from typing import List from pydantic import BaseModel from redis_om import EmbeddedJsonModel, Field, JsonModel, Migrator class FeedItem(EmbeddedJsonModel): id: str = Field(index=True) s_score: str = Field() i_score: str = Field() factors: List[str] class Feed(JsonModel): user_id: str = Field(index=True, primary_key=True) feed_items: List[FeedItem] = Field(default=[])
对应的数据结构示例:
{ "user_id": "john_001", "feed_items": [ { "pk": "01GS2N47G8WK2831GNHMGRVDJT", "id": "63e8c53825e41aca93229eac", "s_score": "0.5082375478927202", "i_score": "0.04626620037029417", "factors": ["2nd"], }, { "pk": "01GS2N557FTV0SCK5TP2KENVAF", "id": "63e8c5d31e033af45abfb64d", "s_score": "0.7", "i_score": "0.37718576424604", "factors": ["2nd", "computer", "laptop"], }, { "pk": "01GS2N63S6VM1HZ6RJVH6M1XQJ", "id": "63e8c743414c482153e332e6", "s_score": "0.5082375478927202", "i_score": "0.24141123225673727", "factors": ["2nd", "thumbdrive", "portables"], }, ], }
当用户查看了pk为01GS2N47G8WK2831GNHMGRVDJT的FeedItem后,需要将其从feed_items数组中删除。目前的实现方式是查询Feed对象、读取列表、移除元素后重新保存:
feed = Feed.find(Feed.user_id == "john_001").first() feed_items = feed.feed_items new_feed_items = [item for item in feed_items if item.pk != "01GS2N47G8WK2831GNHMGRVDJT"] feed.feed_items = new_feed_items feed.save()
但该操作耗时较长,我们有数万用户的Feed数据,且每秒会执行多次此类删除操作,请问是否有更高效的实现方式?
高效实现方案
1. 直接调用Redis JSON命令操作数组(推荐)
Redis OM底层基于Redis的JSON数据类型,你可以绕过ORM封装,直接用JSON.ARRDEL命令删除数组中的指定元素,无需全量读写整个对象:
from redis_om import get_redis_connection redis = get_redis_connection() feed_key = f"Feed:john_001" target_pk = "01GS2N47G8WK2831GNHMGRVDJT" # 获取feed_items数组,定位目标元素的索引 items = redis.json().get(feed_key, "$.feed_items") if items: for idx, item in enumerate(items[0]): if item["pk"] == target_pk: redis.json().arrdel(feed_key, f"$.feed_items[{idx}]") break
该方式仅需两次Redis操作:一次读取数组(仅加载feed_items字段),一次删除元素,相比全量读写开销大幅降低。
2. 调整数据结构,拆分FeedItem存储
若feed_items数量较大,遍历数组找索引仍有开销,可以将每个FeedItem单独存储,并用有序集合维护用户的Feed列表:
- 每个FeedItem的键为
FeedItem:{user_id}:{pk} - 用户Feed列表用有序集合
FeedList:{user_id}存储,成员为FeedItem的pk,分数可用于排序(如时间戳、优先级)
删除操作直接操作有序集合和独立键:
redis = get_redis_connection() user_id = "john_001" target_pk = "01GS2N47G8WK2831GNHMGRVDJT" redis.zrem(f"FeedList:{user_id}", target_pk) redis.delete(f"FeedItem:{user_id}:{target_pk}")
这种方式的删除操作是O(1)级别的,适合高频删除场景,但需要调整数据模型和读写逻辑,不再依赖嵌套的EmbeddedJsonModel。
3. 优化现有ORM操作
如果不想改动数据结构,可以通过投影查询和字段更新减少开销:
# 仅读取feed_items字段,减少数据传输 feed_data = Feed.find(Feed.user_id == "john_001").first(projection=["feed_items"]) feed_items = feed_data.feed_items new_feed_items = [item for item in feed_items if item.pk != "01GS2N47G8WK2831GNHMGRVDJT"] # 直接更新feed_items字段,而非保存整个对象 Feed.update(Feed.user_id == "john_001", feed_items=new_feed_items)
用projection减少读取的数据量,用update方法仅更新目标字段,避免全量序列化和写入整个对象。
内容的提问来源于stack exchange,提问作者knl
相关产品推荐
相关产品推荐

