【问题标题】:Pagination with AWS DynamoDB with PHP使用 PHP 使用 AWS DynamoDB 进行分页
【发布时间】:2014-05-03 11:13:47
【问题描述】:

有人知道对表中的记录进行分页。其实我想用 DynamoDb 在 php 中创建一个分页组件。

似乎不可能像 1,2,3,4,5... .

那样进行分页

因为 Dyanmodb 只是为我们提供了 LIMIT 子句,通过它我们可以读取某些数字。的记录,我们可以通过 LastEvaluatedKey 处理下 n 条记录。所以如果我想直接跳到第5页,怎么可能?

据我了解,我们无法在分页中显示页码。我们可以做的就是读取一定数量的记录,并提供 NEXT 链接来检索下 n 条记录。

分页是任何Web应用程序的基本功能,如果迁移到像DynamoDb这样的云数据库,我们如何实现?

请提供您的意见和建议。谢谢

【问题讨论】:

    标签: php pagination amazon-dynamodb


    【解决方案1】:

    是的,你是对的,DynamoDB 中没有OFFSET。但是只使用LimitLastEvaluatedKey,我做了这个函数:

    public function scan($table, $filter = [], $select = null, $limit = 2)
    {
        $page = isset($_GET['page']) ? $_GET['page'] : 0;
        $options = [
            'TableName' => $table,
            'Count' => true,
        ];
    
        if (!empty($limit)) {
            $options['Limit'] = $limit;
        }
    
        if (!is_null($select)) {
            $options['Select'] = $select;
        }
    
        if (!empty($filter)) {
            $options['ScanFilter'] = $filter;
        }
    
        $results = $results = $this->_client->scan($options);
    
        while ($page > 0 && isset($results['LastEvaluatedKey'])) {
            $results = $results = $this->_client->scan($options);
            $options['ExclusiveStartKey'] = $results['LastEvaluatedKey'];
            $page--;
        }
    
        return $results;
    }
    

    $this->_client 指的是 DynamoDb 客户端对象。
    基本上我用LastEvaluatedKey 遍历所有条目,直到我到达需要的页面。
    要获取表格中的总条目,请调用$this->scan($this->tableName(), [], null, null)['Count'];(即 - 没有任何搜索条件且没有分页,就像在正常的分页功能中一样)。

    【讨论】:

    • 如果没有大量缓存,除了前几个页面之外的任何页面的性能都会非常糟糕
    • @MikeDinescu 是的,我知道。有更好的解决方案吗?
    • 不,不是;我并不是说有更好的解决方案只是提出警告;我能想到的唯一改进是确保缓存结果(或至少LastEvaluatedKeys,以便后续调用不会每次都受到扫描的惩罚)
    • @MikeDinescu 为什么要缓存LastEvaluatedKey,如果它在下一次迭代中总是发生变化?
    • 我开始添加评论,但空间不足,因此将其转换为补充您的答案..
    【解决方案2】:

    为了补充@Justinas 的答案,如果需要随机访问(即跳转到任意页面),Dynamo 的分页性能将非常糟糕。但是,如果您只执行下一页和上一页,则可以绕过 LastEvaluatedKey 并将扫描导致的开销降至最低。

    正如 cmets 中所述,您绝对应该尽可能多地缓存结果。至少,LastEvaluatedKey 结果可以被缓存,这样当用户翻阅结果时,不需要为每个分页请求重新计算它们。这是我的意思的一个例子:

    假设您有一个具有这样架构的表,其中CommentID 是哈希键。

     CommentID | Author | Comment | ...
    -----------+--------+---------+------------
        1      | Joe    | Foo     | ...
        2      | Joe    | Bar     | ...
        3      | John   | Baz     | ...
        4      | Joe    | FooBar  | ...
        5      | Jane   | BooBaz  | ...
        6      | Joesie | Blah    | ...
        7      | Johnny | Blahaha | ...
    

    当你开始分页时,假设你每页请求 3 cmets,你会得到第一页结果和LastEvaluatedKey = 3;然后,如果您对第 2 页发出第二次扫描请求,使用 ExclusiveStartKey=3 您将获得 LastEvaluatedKey = 6;要获得第 3 页,您可以使用 LastEvaluatedKey = 6.. 进行另一次扫描。

    您可以看到,在没有任何类型的缓存的情况下,要获取第 3 页,您需要执行 3 次扫描(如果您还请求第 1 页和第 3 页之前的第 2 页,则其中两次扫描会重复)。所以,我建议的优化是为每个页面存储相应的键。你最终会得到这样的地图:

     Page | Hash-Key
    ------+----------
       1  |   null
       2  |     3
       3  |     6
      ..  |    ... 
    

    当您翻阅结果时,这些值会被填写。现在,当用户想要第 3 页时,您只需扫描一次,使用 6 作为 ExclusiveStartKey

    当然,对于每个页面大小,您都需要一个这样的查找表,并且该表只有在添加(或删除)新行之前才是准确的。也就是说,如果您有很多请求,那么存储分页缓存所需的额外内存将是非常值得的。剩下的就是为你的分页缓存设置一个合理的过期时间,这取决于在你的表中添加(或删除)新数据的频率..

    【讨论】:

    • 如果 cmets 被删除了,你现在必须更新很多分页索引号键,这根本不是一个好的解决方案。我确信删除将是首要要求。
    • @P.Prasad - 不确定您的意思,但通常当您对结果进行分页时,您希望通过各种快照分页 - 删除,就像插入一样最终反映在后续分页请求的结果中;根据删除和插入的频率,您可以调整缓存的 TTL,甚至使您的缓存无效
    • 如果项目编号 4,5,6 被删除,则从第 2 页开始的页面键映射中的所有条目都必须修改,使其值增加 3。此操作本身就是资源密集的。
    • @P.Prasad - 我认为您误解了 ExclusiveStartKey 的工作原理以及我的提议;您将缓存值但从不更新它们 - 如果您觉得删除和插入过于频繁;y 那么您可以不缓存或使缓存无效,但您永远不会调整键
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-03
    • 2018-06-16
    • 2012-10-21
    • 2021-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多