【问题标题】:Microservices architecture and MySQL database pagination微服务架构和 MySQL 数据库分页
【发布时间】:2019-01-29 06:11:39
【问题描述】:

想象一下,我想检索一组客户的所有订单。 以下示例中的arrayList 将包含一组客户 ID。

该数组将被传递到下面的get 方法中,并异步处理以检索数组中每个客户 ID 的订单。

这就是我迷路的地方。如何对数据库结果集进行分页,一次只从数据库中提取一小部分记录,而不必通过网络拉取所有记录。

让我感到困惑的是异步性质以及我们不知道每个客户有多少订单?那么如何有效地一次返回设置的页面大小呢?

service.js

function callToAnotherService(id) {
    return new Promise((resolve, reject) => {
        //calls service passing id
    }
}

exports.get = arrayList => Promise.all(arrayList.map(callToAnotherService))
    .then((result) => result);

【问题讨论】:

  • 只要不提取所有将在您的 DBMS 上的记录。 MySQL 可以在结果集上使用LIMIT 来做到这一点。您确实需要一个查询来返回所有这些客户的所有记录,而不是单独获取每个客户的订单。
  • 您还可以保留所需表的卷影副本,同时让其母服务处理数据,并从服务的数据库中查询数据。虽然,当您需要创建关系或经常提取数据时,您会这样做。要跨不同服务同步数据,您可以设置队列。

标签: javascript mysql node.js amazon-web-services microservices


【解决方案1】:

在 MySQL 中,实现此目的的方法不止一种。

您选择的方法取决于许多变量,例如您的实际分页方法(您是否只想拥有“上一个”和“下一个”按钮,或者实际上想要提供从 1...n 的范围,其中 n 是总数匹配记录数除以您的每页记录数);还涉及数据库设计、计划增长、分区和/或分片、当前和预测的数据库负载、可能的硬查询限制(例如,如果您有多年的记录,您可能需要最终用户为查询选择合理的时间范围(上个月,过去 3 个月,去年等等...),因此它们不会因无限制和过于广泛的查询等而使数据库过载。


分页:
  • 使用简单的上一个下一个按钮,您 可以使用简单的 LIMIT [START_AT_RECORD,] NUMBER_OF_RECORDS method,正如 Rick James 建议的那样 以上。
  • 使用(全部)页码,需要知道匹配的个数 记录,因此根据您的页面大小,您会知道总页数 会有的。
  • 混合使用上述两种方法。例如,您可以显示一些可点击的页码(例如前/后 5 个),以及 firstlast 链接/按钮。

如果您选择最后两个选项之一,您肯定需要知道找到的记录总数。

正如我上面所说,实现同一目标的方法不止一种。必须根据情况做出选择。下面我将描述几个更简单的想法:

  • 第一:
    如果您的解决方案是基于会话的,并且您可以保持会话,那么您可以使用 temporary table,您可以在其中仅选择 order_id(假设它是订单表中的主键)。或者,如果您想获取每个客户的计数(或以其他方式过滤),您还可以将第二列添加为订单表中 order_id 旁边的 customer_id

    使用最少数据传播临时表后,您可以轻松地计算临时表中的行数并根据该数字创建分页。 现在,当您开始显示页面时,您只需选择这些行的子集(使用上面的 LIMIT 方法),然后加入相应的记录(其余列) orders 在临时表 order_id 上。

    这有两个好处:
    1) 逐页浏览记录会很快,因为它不再查询(大概)大订单表。
    2)您没有对订单使用聚合查询表,因为取决于记录的数量和设计,这些将具有非常糟糕的性能,并且可能会影响其他并发用户的性能。

    请记住,最初的临时表创建会有点慢查询。但是,如果您不将临时表限制为仅必不可少的列,那么它肯定会更慢。
    不过,仍然建议您设置一个合理的最大硬限制(临时表记录的数量,或某个时间范围)对于初始查询

  • 第二: 这是我最喜欢的,因为使用这种方法,我已经能够不止一次地解决客户的大型数据库(或特定查询)性能问题。我们正在谈论从 50-55 秒的查询时间缩短到毫秒。这种方法特别不受与数据库可扩展性相关的减速的影响。

    主要思想是您可以预先计算各种聚合(即产品的累积总和,或每个客户的订单数量等......)。为此,您可以创建一个附加表来保存聚合(在您的示例中每个客户的订单计数)。

    现在最重要的部分来了:
    必须使用自定义数据库triggers,即在您的情况下,您可以使用ON INSERTON DELETE em> 触发器,它将更新聚合表并增加/减少特定客户的订单计数,具体取决于是否添加/删除了订单。触发器可以在触发表更改之前或之后触发,具体取决于您如何设置它们。
    触发器在数据库上几乎没有开销,因为它们只在每个(插入/删除的)记录中快速触发一次(除非你做了一些愚蠢的事情,例如从某个大表运行 COUNT(...) 查询,无论如何这将完全破坏目的)

    我通常会更加细化,通过每个客户每月计算/总和等...

    如果处理得当,聚合计数几乎不可能与实际记录不同步。如果您的应用程序启用了订单的 customer_id 更改,您可能还需要添加 ON UPDATE 触发器,以便订单的客户 ID 更改将自动反映在聚合表中。

    当然,您可以使用更多方法。但是上面这两个已经被证明是伟大的。一切视情况而定...

    我希望我有点抽象的答案可以引导您走上正确的道路,因为我只能根据您提出的问题提供的少量信息来回答...

【讨论】:

    【解决方案2】:

    在 MySQL 中,使用ORDER BY ... LIMIT 30, 10 跳过 30 行并抓取 10 行。

    最好记住你离开的地方(比如说 $left_off),然后做

    WHERE id > $left_off
    ORDER BY id
    LIMIT 10
    

    您抓取的最后一行是新的“left_off”。

    更好的是,但使用LIMIT 11。然后您可以显示 10,但还可以发现是否还有更多(通过从 SELECT 返回的第 11 行的存在。

    【讨论】:

      猜你喜欢
      • 2020-09-12
      • 1970-01-01
      • 2019-10-08
      • 2019-03-03
      • 2020-07-05
      • 2017-11-30
      • 2017-09-09
      • 2019-04-14
      • 2021-05-17
      相关资源
      最近更新 更多