Pau*_*jan 27 django google-app-engine bigtable django-models
我基本上拥有经典的多对多模型.用户,奖励以及用户和奖励之间的"多对多"表格映射.
每个用户拥有400个奖项的订单,每个奖励给予大约1/2个用户.
我想迭代所有用户的奖励并总结他们的积分.在SQL中,它将是多对多之间的表连接,然后遍历每个行.在具有MySQL实例的体面机器上,400行应该不是什么大问题.
在应用程序引擎上,我看到大约需要10秒才能完成总和.大部分时间都花在Google的数据存储中.这是cProfile的前几行
ncalls tottime percall cumtime percall filename:lineno(function)
462 6.291 0.014 6.868 0.015 {google3.apphosting.runtime._apphosting_runtime___python__apiproxy.Wait}
913 0.148 0.000 1.437 0.002 datastore.py:524(_FromPb)
8212 0.130 0.000 0.502 0.000 datastore_types.py:1345(FromPropertyPb)
462 0.120 0.000 0.458 0.001 {google3.net.proto._net_proto___parse__python.MergeFromString}
我的数据模型错了吗?我在查找错误吗?这是一个我必须处理缓存和bulkupdating(这将是一个王室痛苦的屁股)的缺点.
Ste*_*sop 20
可能是两者兼而有之;-)
如果你在奖励表上进行了400次查询,那么对于映射表上的查询返回的每个结果都有一个,那么我希望这很痛苦.由于BigTable认为返回1000个结果是在合理时间内运行的极限,因此查询的结果限制为1000.根据架构,我希望400个查询比返回400个结果的400个查询慢(400 log N vs.(log M)+ 400).
好消息是,在GAE上,memcaching包含所有奖项及其积分值的单一散列表非常简单(好吧,当我一会儿关注memcache文档时看起来非常简单.我不需要这样做然而).
此外,如果您还不知道,for result in query.fetch(1000)速度要快for result in query,并且您无论如何都限制为1000个结果.后者的优点是(1)如果你提前纾困可能会更快,(2)如果谷歌将限额提高到超过1000,它就会在不改变代码的情况下获得收益.
删除用户(或奖励)时,您可能也会遇到问题.我在一次测试中发现我可以在时间限制内删除300个对象.这些对象比映射对象更复杂,有3个属性和5个索引(包括隐式索引),而映射表可能只有2个属性和2个(隐式)索引.[编辑:刚才意识到在我知道db.delete()可以获取列表之前我做了这个测试,这可能要快得多].
BigTable不一定能做关系数据库设计好的事情.相反,它可以跨多个节点很好地分配数据.但是几乎所有网站都在单个数据库服务器上运行正常,因此并不严格需要BigTable所做的事情.
另一件事:如果您在单个http请求上执行了400个数据存储区查询,那么您会发现在达到请求固定配额之前,您已达到数据存储区固定配额.当然,如果你在配额范围内,或者如果你先点击其他东西,那么这可能与你的应用程序无关.但两个配额之间的比例大约是8:1,我将此视为谷歌期望我的数据模型的样子.
Nic*_*son 19
我的数据模型错了吗?我在查找错误吗?
是的,是的,我很害怕.
就您的数据模型而言,到目前为止处理此问题的最佳方法是将总和存储在用户记录中,并在用户获得/失去奖励时更新它.在绝大部分时间里,每次计算他们的分数都没有意义,它将保持不变.如果您使"UserAward"实体键入"User"的子实体,您可以更新分数并在单个原子事务中插入或删除UserAward条目,从而确保您的计数始终准确.
onebyone指出你可以记得奖励表.这是个好主意,但鉴于数据量有限,更好的方法是将其存储在本地内存中.全局成员在HTTP请求之间保持不变,并且因为我认为您不经常更新奖励表,所以您不必担心使缓存失效.只需在第一个请求中加载它(甚至将其硬编码到您的源中).如果您确实更改了奖励列表,则部署新的次要更新将重置所有实例,从而导致重新加载.
对于查找,请记住,执行数据存储操作的大量成本是往返时间.一个get()操作,按ID查找一个或多个记录(你可以批量!)大约需要20-40毫秒.但是,查询大约需要160-200ms.因此,非规范化的力量.
| 归档时间: |
|
| 查看次数: |
5548 次 |
| 最近记录: |