在Google App Engine(Python)上实现高可扩展性标签

5 投票
2 回答
1228 浏览
提问于 2025-04-16 07:37

我有很多帖子,这些帖子上会标记一个或多个标签。帖子可以被创建或删除,用户也可以根据一个或多个标签进行搜索(这些标签之间是逻辑上的“与”关系)。

我想到的第一个方案是一个简单的模型:

class Post(db.Model):
  #blahblah
  tags = db.StringListProperty()

创建和删除操作的实现很简单。但搜索就复杂多了。要搜索N个标签,它需要执行N个GQL查询,比如“SELECT * FROM Post WHERE tags = :1”,然后用游标合并结果,这样的性能非常差。

第二个想法是把标签分开成不同的实体:

class Post(db.Model):
    #blahblah
    tags = db.ListProperty(db.Key) # For fast access

class Tag(db.Model):
    name = db.StringProperty(name="key")
    posts = db.ListProperty(db.Key) # List of posts that marked with tag

这样可以通过键从数据库中获取标签(比通过GQL获取要快得多),然后在内存中合并。我觉得这个实现的性能比第一个方案要好,但经常使用的标签可能会超过单个数据存储对象允许的最大大小。而且还有另一个问题:数据存储每秒只能修改一个对象,所以对于经常使用的标签,我们在修改时也会遇到延迟瓶颈。

有什么建议吗?

2 个回答

0

可能的解决办法是,拿你的第二个例子,稍微改一下,让它在处理大数据集时更高效。有一个想法是,为一个标签使用多个数据库实体,并把它们分组,这样你就不需要一次性获取太多组。如果默认的排序方式(我们就称之为唯一允许的排序方式)是按帖子日期排序,那么就按照这个顺序来填充标签组。

class Tag(db.Model):
    name = db.StringProperty(name="key")
    posts = db.ListProperty(db.Key) # List of posts that marked with tag
    firstpost = db.DateTimeProperty()

在给一个组添加或移除标签时,先检查一下这个组里有多少帖子。如果你要添加的帖子让这个组的帖子数量超过,比如说100个,那就把它分成两个标签组。如果你要移除一个帖子,让这个组的帖子数量少于50个,那就从前一个或后一个组里借一些帖子过来。如果相邻的组也有50个帖子,那就把它们合并在一起。当你按标签列出帖子(按帖子日期排序)时,你只需要获取少量的组。

不过,这样并不能真正解决高需求标签的问题。

想想看,插入操作可以稍微大胆一些。获取最新的标签组条目,把它们合并,然后放一个新的标签组。交易的延迟可能并不是真正的问题。

1

为了进一步回答Nick的问题。如果在查询中使用多个标签进行逻辑与操作,可以这样写:tags = tag1 AND tags = tag2……在一个查询中设置多个成员是数据存储的一个亮点功能。你可以在一个查询中就得到你想要的结果。

http://code.google.com/appengine/docs/python/datastore/queriesandindexes.html#Properties_With_Multiple_Values

撰写回答