2011-10-12 36 views
8

我正在通过HTTP制作API,通过分页从PostgreSQL中获取大型行。在平常情况下,我通常通过天真的OFFET/LIMIT条款实现这样的分页。然而,在这种情况下有一些特殊要求:在网络中用PostgreSQL获取连续列表的最佳方式

  • 很多行都有,但我相信用户无法达到最终结果(想象Twitter的时间表)。
  • 页面不能随机访问,而是按顺序访问。
  • API将返回一个包含指向连续块的页面的游标标记的URL。
  • 光标代币不会永久存在,但会持续一段时间。
  • 其排序频繁波动(如Reddit排名),但连续游标应保持其一致的排序。

我该如何完成使命?我准备改变我的整个数据库模式!

+0

只是为了确定你的问题。你在说很多行还是很宽的行吗? – Kuberchaun

+0

@ StarShip3000谢谢。很多行。 – minhee

回答

6

假设只有结果波动的顺序而不是行中的数据,Fredrik的答案才有意义。使用array类型,而不是在内存

  • 存放在PostgreSQL表ID列表:不过,我建议如下补充。在内存中执行它,除非你认真地使用像redis一样的自动过期和内存限制,正在设置自己的DOS内存消耗攻击。我想这将是这个样子:

    create table foo_paging_cursor (
        cursor_token ..., -- probably a uuid is best or timestamp (see below) 
        result_ids integer[], -- or text[] if you have non-integer ids 
        expiry_time TIMESTAMP 
    ); 
    
  • 你需要决定是否cursor_token和result_ids用户可以减少您的存储需求,并运行每个用户的初始查询所需的时间之间共享。如果它们可以共享,则选择一个缓存窗口,比如说1分钟或5分钟,然后在新的请求中创建该时间段的cache_token,然后检查是否已经为该标记计算了结果ID。如果没有,为该标记添加一个新行。您应该在检查/插入代码周围添加一个锁来处理并发的新令牌请求。

  • 有清除旧的令牌/结果计划后台作业,并确保您的客户端代码可以处理有关过期/无效标记任何错误。

甚至不考虑使用真正的数据库光标。

将结果ID保留在Redis列表中是处理此问题的另一种方法(请参阅LRANGE命令),但是如果沿着该路径行进,请注意过期和内存使用情况。你的Redis键将是cursor_token,id将是列表的成员。

+0

更好的是,制作一个**临时表**。更快,更少的光盘负载。无需担心DOS攻击,临时表只能使用有限的RAM(请参阅手册中的['temp_buffers'](http://www.postgresql.org/docs/9.1/interactive/runtime-config-resource.html #RUNTIME-CONFIG-RESOURCE-MEMORY)并在RAM不足时写入光盘 –

+1

临时表是会话本地的,会话终止时会被删除,因此,这不适用于数据库连接池或http api端点分布在多个节点上,并使用不同的连接,当应用程序服务器重新启动并且必须重新连接到数据库时,它也会导致问题,也就是说,通过将表放入内存支持的tmpfs)tablespace。请参阅http://magazine.redhat.com/2007/12/12/tip-from-an-rhce-memory-storage-on-postgresql/ –

+0

感谢您的建议,我决定使用memcached和store用逗号分隔的ID到具有到期时间的键(它们是光标标记)欧! – minhee

1

我知道绝对没有 PostgreSQL的,但我是一个相当不错的SQL Server开发,所以我想采取射击在这个反正:)

多少行/页你期望用户会最大限度地浏览每个会话?例如,如果您希望用户为每个会话遍历最多10个页面(每个页面包含50行),则可以设置最大值,并设置web服务,以便当用户请求第一页时,缓存10 * 50行(或者只是行的Id:s,取决于你得到多少内存/同时用户)。

这肯定会以更多方式加速您的web服务。而且这很容易实现。因此:

  • 当用户从第1页请求数据时。运行一个查询(完成顺序,连接检查等),将所有的ID:s存储到一个数组中(但最多500个ID)。在位置0-9处返回数组中对应于id:s的数据行。
  • 当用户请求页面2-10时。返回数组中对应于id:s的数据行(第-1页)* 50 - (页)* 50-1。

你也可以碰到数字,一个500个int:s的数组只会占用2K的内存,但这也取决于你想要多快的初始查询/响应。

我在一个实时网站上使用了类似的技术,当用户继续通过第10页时,我只是切换到查询。我想另一个解决方案是继续扩大/填充阵列。 (再次运行查询,但不包括已包含的id:s)。

无论如何,希望这有助于!

相关问题