我有一个应用程序,其中有一个我的用户将浏览的项目列表。我已经通过索引字段处理了分页(无论如何我都需要它来处理其他事情,所以我想为什么不这样做)。
我的问题是我想实现一个“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)。这一切都假设读取将比写入发生的频率高得多,在此应用程序中,一旦完成初始数据加载,就会出现这种情况。