【问题标题】:How to layout GAE datastore如何布局 GAE 数据存储
【发布时间】:2013-03-06 09:59:12
【问题描述】:

简介

我是 GAE 的新手,写了一个小应用程序,不幸的是,尽管数据存储区中的数据不多,但它很快就达到了数据存储区每天读取的配额限制。
这个问题应该是关于布局和索引的可能使用(目前我对如何使用它们没有任何线索)。

应用应该做什么

  • 该应用程序应该记录纸牌游戏的分数(Tichu 感兴趣的人^^)。一场比赛包括几轮比赛,当一支球队达到 1000 分时结束。
  • 应用应显示已玩游戏的统计信息

应用的第一个布局

我的第一个布局方法是使用以下实体:

class Player(db.Model):
    Name = db.StringProperty(required = True)

class Game(db.Model):
    Players = db.ListProperty(db.Key)
    Start = db.DateTimeProperty(auto_now_add = True, required = True)
    End = db.DateTimeProperty()

class Round(db.Model):
    Game = db.Reference(Game, required = True)
    RoundNumber = db.IntegerProperty(required = True)
    PointsTeamA = db.IntegerProperty(required = True)
    PointsTeamB = db.IntegerProperty(required = True)
    FinishedFirst = db.ReferenceProperty(Player, required = True)
    TichuCalls = db.ListProperty(db.Key)

正如您在上面看到的,实体已标准化(至少我希望它们是标准化的)。但是,使用这种方法可以进行简单的计算,例如

  • 哪位玩家赢得的比赛最多

这可能看起来像这样

#Untested snippet just to get an idea of what I am doing here
Wins = dict.fromkeys(Player.all().fetch(None), 0)
for r in Round.all():
    wins[r.FinishedFirst] += 1

还有其他统计数据,例如

  • 哪个玩家最常先完成
  • 哪个玩家的胜率最高

产生非常大量的数据集读取操作。在仅显示有限数量统计数据的页面上,仅通过几次刷新就达到了一天的配额,只有 60 轮和满手的游戏。同样使用memcache也没有解决这里的问题。
这导致了我的第二种方法:

应用的第二个布局

class Player(db.Model):
    Name = db.StringProperty(required = True)

class Game(db.Model):
    Players = db.ListProperty(db.Key)
    Start = db.DateTimeProperty(auto_now_add = True, required = True)
    End = db.DateTimeProperty()
    Rounds = db.BlobProperty()

    def GetRounds(self):
        if self.Rounds:
            return pickle.loads(self.Rounds)
        else:
            return []

    def AddRound(self, R):
        Rounds = self.GetRounds()
        Rounds.append(R)
        self.Rounds = pickle.dumps(Rounds, -1)

class Round(object):
    def __init__(self, Game, RoundNumber, PointsTeamA, PointsTeamB, FinishedFirst, TichuCalls):
        self.Game = Game
        self.RoundNumber = RoundNumber
        self.PointsTeamA = PointsTeamA
        self.PointsTeamB = PointsTeamB
        self.FinishedFirst = FinishedFirst
        self.TichuCalls = TichuCalls

现在每个Game 都存储一个不再是db.Model 的回合列表。这大大减少了数据集读取量。

问题

  1. 您将如何设置数据模型? (使用BlobProperty 存储不是db.Model 类型的对象有意义吗?)
  2. 这个模型的索引是什么样子的? (请详细说明,因为我对索引的了解非常有限。)
  3. 随着数据存储中元素数量的增加,第二个方法最终也将达到每天读取的配额。在设计模型时,您会如何考虑这一事实?

【问题讨论】:

  • 考虑使用 ndb.models,因为您在 get_by_key 上免费获得 memcaching(保存数据存储读/写)
  • 并且使用 ndb 属性,您会得到一个重复的属性/结构,它可能会替换整个圆形属性

标签: google-app-engine google-cloud-datastore


【解决方案1】:

简短的回答 - 习惯于不“规范化”您的数据。这就是 NoSQL DBS 的魅力所在。我会在 Player 模型中添加一个列表属性或一堆整数属性(以您的应用程序更有意义的为准),跟踪他们的游戏完成情况。像这样:

class Player(db.Model):
    Name = db.StringProperty(required = True)
    FinishedFirst = db.IntegerProperty(default=0)
    FinishedSecond = db.IntegerProperty(default=0)
    ...

class Player(db.Model):
    Name = db.StringProperty(required = True)
    Finishes = db.ListProperty() # A list of 1s, 2s, 3s, etc... for each finish

关键是这两个都将帮助您避免查询/使用更多资源,然后以编程方式尝试计算用户首先完成了多少次。

当您知道自己将大量使用的数据时,请考虑在主模型中存储冗余属性,以便始终触手可及而无需重新查询。

另外,看看 NDB API https://developers.google.com/appengine/docs/python/ndb/properties 您可以在游戏回合中利用 JsonProperty。

归根结底,规范化是老式的 RDB 东西。

【讨论】:

  • 有没有什么好办法可以保证datastore中的冗余信息是同步的,不相互矛盾?
  • 这是存储冗余信息的一个缺点 - 更多的写入,这肯定会导致更少的读取。第一道防线是确保您知道存储相同数据的所有位置,例如名称,并且每次更新名称时,确保以编程方式更新每个相关实体。第二道防线是创建定期运行的 cron 作业,以自动比较数据,如果数据与某些基本实体不同步,请更新它。你不应该依赖这个。这意味着您的数据不同步的时间段。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-06
  • 2014-08-04
  • 2013-06-21
相关资源
最近更新 更多