1

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

我的问题是我想实现一个“goto”功能;用户可以直接跳到一个项目,而不是使用提供的导航按钮(下一个和上一个)翻阅它们。例如,他们可以在“goto”框中输入 1000 并显示第 1000 个项目。第 n 个项目与其索引之间存在脱节 - 索引保证是有序的,但不保证是连续的,所以我不能只按索引过滤。我考虑过使用 的offset参数fetch,但我记得当我第一次开始使用 appengine 编程时,由于性能问题,我被告知不要使用它。

offset去这里是最好的方式,还是有更好的方式?此外,与它相关的成本仅仅是获得结果需要更长的时间,还是会计入我的数据存储读取/小型操作?

编辑:我的意思不是坏话,而是为了避免那些会告诉我使用光标的人...... :-) 我处理分页的方式对我来说比我会使用更有用游标。预先感谢您的关心。此外,我想我会在代码中说明我想要做的事情:

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) 

编辑 2: 根据评论,我决定尝试将我的项目存储在内存缓存中,并在内存中进行所有过滤、排序、偏移等。Item被分组,Category最多可以容纳 1500 个项目,我将每个项目存储Category在自己的密钥下的 memcache 中。我能想到的唯一问题是,每个Item最坏的情况可能是 2kb 大小。a 的大小不太可能Category接近 1500 Items,或者Item达到最坏情况下的大小,但如果是这样,它将超过 1mb 内存缓存限制。关于如何处理的任何建议?此外,可能有 10 个左右Categories;memcache 中的这么多存储会导致它更频繁地刷新吗?最后,当我获取Entities还是 memcache 是一个更好的解决方案(Items将被非常频繁地访问,通常在小组中(25-30))?

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

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来维护项目的(非顺序)顺序,并且每次代表特定类别的项目的字典列表被踢出数据存储区时,我都会遍历它们并num为每个项目添加一个连续的“”。num实际上仅适用于客户端(阅读:浏览器),因为我的 UI 是完全动态的(所有 AJAX;没有任何页面重新加载),并且我缓存了在 javascript 中发送到浏览器的每个项目。服务器端我不一定需要物品的顺序;客户端有某些功能需要它,而服务器使用非顺序索引就可以了。

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

编辑 5无需更多编辑...这是我计算使用 memcache 和数据存储或仅使用数据存储时的时间复杂度的图表(省略了数据存储的时间复杂度,因为我不确定它到底是什么。现在再去阅读 BigTable 论文来尝试弄清楚已经太晚了,所以我假设它对于哈希表上的操作是相同的)。这些都是最好的情况。对于 memcache 解决方案,最坏的情况需要添加 N 次数据存储读取(因为必须将类别中的所有项目读入 memcache)。该图表留下了与存储或检索数据(即排序、过滤器)无关的任何额外内容,这对于 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重新填充 memcache)。这一切都假设读取将比写入发生的频率高得多,在此应用程序中,一旦完成初始数据加载,就会出现这种情况。

4

2 回答 2

1

为编辑 4 更新。

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

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

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

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

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

附录:

如果您的数据量不太大,Cloud SQL 是另一种选择。一般来说,SQL 在您尝试执行的顺序查询方面要好得多,但代价是对大型数据集的扩展性很差。

如果您怀疑您的使用量很少,则每次使用定价相对便宜

于 2012-07-12T04:32:36.453 回答
-2

offset 是 GAE 中最好的方法,不用担心配额,它只会计算偏移后的读取。换句话说:读取前 N 个项目与读取从某个偏移量开始的 N 个项目消耗的配额数量相同。

于 2012-07-07T15:58:24.790 回答