Appengine查询偏移 - 不用于分页 - 使用Memcache编辑?

时间:2012-07-06 22:24:49

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

我有一个应用程序,其中有一个我的用户将翻阅的项目列表。我已经通过索引字段处理了分页(无论如何我还需要其它东西,所以我想为什么不这样做。)

我的问题是我想实现一个" goto"特征;用户可以使用提供的导航按钮(下一个和上一个)直接跳到项目而不是通过它们进行分页。例如,他们可以在" goto"中输入1000。框并显示第1000个项目。第n个项目与其索引之间存在脱节 - 保证索引按顺序排列但不保证是顺序的,因此我不能仅按索引进行过滤。我考虑使用offset的{​​{1}}参数,但我记得当我第一次使用appengine开始编程时,由于性能问题,我被告知不要使用它。

fetch会是最好的方式,还是有更好的方法?此外,与之相关的成本是否只是需要更长时间才能获得结果,还是会计入我的数据存储读取/小操作?

编辑:我并不是故意这么做,但是为了避开会告诉我使用游标的人...... :-)我处理分页对我来说比我使用游标更有用的方式。提前感谢您的关注。另外,我想我会说明我在代码中尝试做些什么:

offset

编辑2:根据评论我决定尝试将我的项目存储在memcache中,并在内存中执行所有过滤,排序,偏移等操作。 q = Item.all() #orders it by highest index first which is how client handles items q = q.order('-index') #count is determined automatically but is at least 25 and not greater than 300 q = q.fetch(limit=count, offset=i) Item分组,最多可容纳1500个项目,并将每个Category存储在内存密钥下的内存中。我能想到的唯一问题是每个Category最坏情况下的大小为2kb。 Item中的Category附近有Items,或者Item达到最差情景大小的情况不太可能,但如果确实如此,它将超过1mb memcache限制。关于如何处理的任何建议?此外,可能有大约10 Categories; memcache中的这么多存储会导致它更频繁地刷新吗?最后,当我获取Entities或者memcache是​​一个更好的解决方案时,是否值得使用偏移量(Items将被频繁访问,通常是小组(25-30))?< / p>

编辑3:我现在有一种引用项目的顺序方式。每个项目都有一个id,它可以跨类别唯一地标识它,索引是一种非按顺序排序类别中的项目的方式,num是顺序的,但不是项目隐含的(每次我拉项目在memcache之外我按索引排序,然后遍历项目列表,在给定当前迭代次数的情况下为每个项目分配一个数字)我想这是一种令人费解的说法:

for i in range(0, len(items)):
    items[i]['num'] = i

编辑4:项目模型:

class Item(db.Model):
   item_id = db.IntegerProperty()
   index = db.IntegerProperty()
   #I used StringProperty instead of ReferenceProperty because I'm a cheapo with memory 
   category = db.StringProperty() 

我将num与模型分开,因为在添加和删除时将其更新为顺序相关的成本。因此,我使用index来维护项目的(非顺序)顺序,并且每次表示特定类别的项目的dicts列表被踢出数据存储区时,我会遍历它们并添加顺序&# 34; num&#34;到每个项目。 num实际上只适用于客户端(读取:浏览器),因为我的UI完全是动态的(所有AJAX;没有任何页面重新加载),我缓存了javascript中发送到浏览器的每个项目。服务器端我不一定需要物品的连续订单;在客户端需要它的某些功能,服务器可以很好地使用非顺序索引。

我的问题的主要症结似乎是我应该保留这个模型,即在memcache中存储类别的所有项目,或者直接从数据存储区检索项目。项目将被要求很多(我没有确切的数量,甚至没有估计每秒多少次,但它应该是每秒要求的许多项目)。我知道在被赶出去之前,没有办法准确地确定这些项目在memcache中会有多长时间,但是我可以假设它不会每隔几分钟就会发生一次吗?因为如果其他的话,我觉得最好的方法是使用memcache,但我可能会遗漏一些东西。哦,希望这将是我窃取所有SO磁盘空间之前的最后一次编辑;)

编辑5 对于没有更多编辑这么多......这是我使用内存缓存和数据存储区或数据存储区时计算时间复杂度的图表(由于数据存储区的时间复杂性,因为我不确定它到底是什么。再次阅读BigTable论文以试图弄清楚它已经太晚了所以我只是假设它对于操作来说是一样的哈希表)。这些都是最好的情况。对于memcache解决方案,最糟糕的情况是您需要添加N个数据存储区读取(因为类别中的所有项目都必须读入内存缓存)。此图表为memcache和数据存储区解决方案留下了与存储或检索数据(即排序,过滤器)无关的任何内容。对于仅限memcache的解决方案,num未存储在数据存储区中。对于仅数据存储的解决方案,这就是为什么与添加或删除相关的额外成本(更新每个项目的num)。

n DS = number of DataStore operations
w = write
r = read
N = number of items in category (for Add and Remove this is the number before
    the operation is performed)
c = count of items to read
o = offset

+------------------------------------------------------------------------------+
|                  Memcache             |               Datastore              |
|------------------------------------------------------------------------------|
|       |                               |       |                              |
| Reads |           O(o + c)            | Reads |           c DS r             |
|-------+-------------------------------|-------+------------------------------| 
|       |                               |       |                              |
|Reads w|           O(o + c)            |Reads w|          o + c DS r          |
|Offset |                               |Offset |                              |
|-------+-------------------------------|-------+------------------------------|
|       |                               |       |                              |
| Adds  |         1 DS w + O(N)         | Adds  |   1 + N DS w & N - 1 DS r    |
|-------+-------------------------------|-------+------------------------------| 
|       |                               |       |                              |
|Removes|       1 DS rw + O(o + N)      |Removes|        N - o DS wr           |
|-------+-------------------------------|-------+------------------------------| 
|       |                               |       |                              |
| Edits |         1 DS rw + O(o)        | Edits |          1 DS rw             |
|-------+-------------------------------|-------+------------------------------|

所以问题是,memcache解决方案的时间复杂度是否超过数据存储解决方案带来的更多DS操作,除非memcache驱逐可能导致memcache解决方案中的DS操作多于数据存储解决方案(因为每次这些项目是从mecache中逐出的,我们必须N DS r重新填充内存缓存。这一切都假设读取将比写入更频繁地发生,一旦初始数据加载完成,就会出现这种情况。

2 个答案:

答案 0 :(得分:1)

针对编辑4进行了更新。

您的Item模型看起来合理,最大的问题是如何管理顺序索引。我仍然希望以你描述的方式依赖memcache,因为除非你让数据存储正确地备份你的数据状态,否则缓存驱逐会大大减慢你的读操作(这是常见的和面向用户的)。

所以,随意继续在memcache中存储项目。但是,在插入或删除时,请确保同时更新数据存储区中的num。 (如果你已经在memcache中拥有整套Items,则不需要读取操作。只需更新memcache中的所有项目并同时将它们写入数据存储区。)

最糟糕的情况仍然是我在第4次编辑之前描述的情况。插入元素是1次读取+ 1次写入。删除元素是N次读取+ N次写入,其中N是类别中的项目数。查找项目只需1次阅读。这些场景中的每一个都假定memcache为空。

如果您使用的是偏移量,则每次插入都是1次写入。删除元素将是1次写入。 但是,读取元素是N次读取,其中N是要检索的项目的顺序索引。如果您正在使用内存缓存,但没有在数据存储区中备份num的值,那么您也会遇到这种情况。

在大多数情况下,读取比写入更常见,因此在数据存储中维护num效率更高。

附录:

如果您的数据大小不是太大,则可以使用Cloud SQL。一般情况下,SQL在顺序查询方面要好得多,就像你要尝试的那样,但代价是大型数据集的扩展性很差。

per use pricing相对便宜,如果你怀疑你的用量很少。

答案 1 :(得分:-2)

偏移是在GAE中执行此操作的最佳方式,不用担心配额,它只会计算偏移后的读数。换句话说:首先读取N个项目会消耗与从一些偏移量开始读取N个项目相同的配额。