在Google App Engine上设计可扩展的产品数据库

1 投票
2 回答
599 浏览
提问于 2025-04-16 04:33

我建立了一个产品数据库,这个数据库分成三部分。每个部分又有一个“子”部分,里面包含标签。但是我越用越觉得这个系统不太稳定。每次我添加新内容,所需的代码量也越来越多。

一个产品是由多个部分组成的,每个部分都有一个类型。每个产品、部分和类型都有一个标签,并且每种语言都有对应的标签。

一个产品包含两个部分的列表。一个是默认部分的列表(每种类型各一个),另一个是可选部分的列表。

现在我想在这个系统中加入货币的概念,因此决定重新设计整个处理方式。

我想要的结果是一个包含所有产品对象的列表,这些对象包括名称、描述、价格、所有部分以及与这些部分匹配的所有类型。同时,还要有正确语言的标签。

像这样:

product
    - name
    - description (by language)
    - price (by currency)
    - parts
        - part (type name and part name by language)
        - partPrice (by currency)

我目前的设置有个问题,就是混合使用了很多 db.ReferenceProperty 和 db.ListProperty(db.key)。

获取所有数据有点麻烦,需要多个循环、匹配字典和数据存储的调用。总之,感觉有点乱。

我重新设计的方案(还没测试)看起来是这样的:

class Products(db.model)
    name = db.StringProperty()
    imageUrl = db.StringProperty()
    optionalParts = db.ListProperty(db.Key)
    defaultParts = db.ListProperty(db.Key)
    active = db.BooleanProperty(default=True)

    @property
    def itemId(self):
        return self.key().id()

class ProductPartTypes(db.Model):
    name= db.StringProperty()

    @property
    def itemId(self):
        return self.key().id()

class ProductParts(db.Model):    
    name = db.StringProperty()
    type = db.ReferenceProperty(ProductPartTypes)
    imageUrl = db.StringProperty()
    parts = db.ListProperty(db.Key)

    @property
    def itemId(self):
        return self.key().id()


class Labels(db.Model)
    key = db.StringProperty() #want to store a key here
    language = db.StringProperty()
    label = db.StringProperty()

class Price(db.Model)
    key = db.StringProperty() #want to store a key here
    language = db.StringProperty()
    price = db.IntegerProperty()

这里的主要变化是我把标签和价格分开了。这样它们可以包含任何产品、部分或类型的标签和价格。

我想知道,从架构的角度来看,这样的设计是否稳妥?即使每个模型中有成千上万的条目,这样的设计还能保持有效吗?

另外,关于如何更好地获取数据,任何建议都很欢迎。我现在的做法是先获取所有数据,然后用循环把它们放进字典里,这样虽然能工作,但感觉随时可能出问题。

..fredrik

2 个回答

1

我觉得你的设计大体上是合理的。在看完你的问题描述后,我也想出了一个差不多的设计,只是有一些不同之处。

  • 我把价格和产品、产品部件放在了一起,而不是单独建一个表。
  • 另一个不同之处是部件类型。如果部件类型不多,你可以直接用Python的列表或元组来表示。

part_types = ('wheel', 'break', 'mirror')

这也取决于你预期会有怎样的查询。如果你需要进行很多价格计算的查询(不依赖于其他产品和部件的信息),那么你现在的设计可能更合适。

你提到你会先获取所有数据。那这样就不能查询了吗?如果你把所有数据都放到应用里,然后再用Python进行排序和过滤,那会很慢。你考虑使用哪个数据库?对我来说,mongodb看起来是个不错的选择。

最后,为什么你对1000条记录感到怀疑呢?你可以提前在数据库上进行一些测试。

祝好

3

你需要记住,App Engine 的数据存储方式要求你重新考虑设计数据库的方式。一开始这可能会让人觉得不太直观,但如果你想让你的应用能够扩展,就必须尽量减少数据的规范化。这个数据存储就是这样设计的。

我通常的做法是先考虑在不同的使用场景中需要进行哪些查询,比如我需要同时获取哪些数据?顺序是怎样的?哪些属性需要建立索引?

如果我理解得没错,你的主要目标是获取一份包含完整细节的产品列表。顺便提一下,如果你还有其他查询场景,比如按价格、类型等过滤,也要考虑进去。

为了从一次查询中获取所有需要的数据,我建议你创建一个模型,可能长这样:

class ProductPart(db.Model):
    product_name = db.StringProperty()
    product_image_url = db.StringProperty()
    product_active = db.BooleanProperty(default=True)
    product_description = db.StringListProperty(indexed=False) # Contains product description in all languages
    part_name = db.StringProperty()
    part_image_url = db.StringProperty()
    part_type = db.StringListProperty(indexed=False) # Contains part type in all languages
    part_label = db.StringListProperty(indexed=False) # Contains part label in all languages
    part_price = db.ListProperty(float, indexed=False) # Contains part price in all currencies
    part_default = db.BooleanProperty()
    part_optional = db.BooleanProperty()

关于这个解决方案:

  • ListProperties 被设置为 indexed=False,以避免在不需要过滤时索引爆炸。
  • 为了获取正确的描述、标签或类型,你需要始终以相同的顺序设置列表值。例如:part_label[0] 是英语,part_label[1] 是西班牙语,依此类推。价格和货币也是同样的道理。
  • 从这个模型中获取实体后,你需要在内存中进行一些操作,以便将数据整理成你想要的结构,可能会放在一个新的字典里。

显然,这样的设计在数据存储中会有很多冗余,但没关系,因为这让你可以以可扩展的方式查询数据存储。

此外,这并不是要替代你原本想要的架构,而是为你需要进行的用户查询设计的一个额外模型,也就是获取完整的产品/零件信息列表。

这些 ProductPart 实体可以通过后台任务填充,复制你其他规范化实体中的数据,这些实体才是权威数据源。由于你在 App Engine 上有足够的数据存储,这应该不会成为问题。

撰写回答