【问题标题】:Statistics on Query Time (PostgreSQL)查询时间统计(PostgreSQL)
【发布时间】:2010-07-01 18:03:01
【问题描述】:

我有一个包含十亿行的表,我想确定几个表单查询的平均时间和时间标准差:

select * from mytable where col1 = '36e2ae77-43fa-4efa-aece-cd7b8b669043';
select * from mytable where col1 = '4b58c002-bea4-42c9-8f31-06a499cabc51';
select * from mytable where col1 = 'b97242ae-9f6c-4f36-ad12-baee9afae194';

....

我有一千个 col1 的随机值存储在另一个表中。

有没有办法将这些查询中的每一个花费多长时间(以毫秒为单位)存储在一个单独的表中,以便我可以对它们运行一些统计信息?类似于:对于我的随机表中的每个 col1,执行查询,记录时间,然后将其存储在另一个表中。

只要我可以留在 PostgreSQL 中,完全不同的方法就可以了(即,我不想编写外部程序来执行此操作)。

【问题讨论】:

    标签: sql performance postgresql


    【解决方案1】:

    您需要更改 PostgreSQL 配置文件。

    启用此属性:

    log_min_duration_statement = -1        # -1 is disabled, 0 logs all statements                                    
                                           # and their durations, > 0 logs only                                       
                                           # statements running at least this number                                  
                                           # of milliseconds             
    

    之后,执行时间将被记录下来,您将能够准确了解查询的执行情况有多差(或多好)。

    您还可以使用一些 LOG PARSING 实用程序来提供出色的 HTML 输出以供进一步分析,例如 pgfouine

    【讨论】:

      【解决方案2】:

      你知道EXPLAIN statement吗?

      此命令显示 PostgreSQL 计划程序为提供的语句生成的执行计划。执行计划显示语句引用的表将如何被扫描——通过普通顺序扫描、索引扫描等——如果引用了多个表,将使用什么连接算法将每个表中所需的行汇集在一起输入表。

      显示中最关键的部分是估计的语句执行成本,这是计划者对运行语句所需时间的猜测(以磁盘页面获取为单位测量)。实际上显示了两个数字:可以返回第一行之前的启动时间,以及返回所有行的总时间。对于大多数查询,总时间很重要,但在诸如 EXISTS 中的子查询之类的上下文中,计划程序将选择最小的启动时间而不是最小的总时间(因为无论如何,执行程序将在获得一行后停止)。此外,如果您使用 LIMIT 子句限制要返回的行数,则计划器会在端点成本之间进行适当的插值,以估计哪个计划确实最便宜。

      ANALYZE 选项使语句被实际执行,而不仅仅是计划中的。每个计划节点内花费的总经过时间(以毫秒为单位)和它实际返回的总行数被添加到显示中。这对于查看规划者的估计是否接近现实很有用。

      可以很容易地编写一个脚本,对表中每个随机值的查询执行EXPLAIN ANALYZE,并将输出保存到文件/表/等。

      【讨论】:

      • 有没有办法只输出时间,这样我就不必解析文件了?如果必须,我会这样做,但似乎应该有更直接的方法。
      • psql -c "EXPLAIN ANALYZE select * from mytable where col1 ..." | grep "Total runtime"
      • 如果可能的话,我真的在寻找一种完全在 SQL 中执行此操作的方法。似乎我应该能够将它在 psql 交互式 shell 中返回的运行时直接存储为一个值。你的答案是非常正确的,如果没有人能给我一个纯 SQL 的答案,这就是我一直计划做的事情。感谢您的宝贵时间!
      • 您可能可以编写一个 PL/pgSQL 来执行类似的操作,但我不太确定是否诚实。 Pablo Santa Cruz 的回答也不错;您可以将服务器配置为在语句执行时间超过可配置时间时记录。日志将包含语句本身。
      【解决方案3】:

      直接,不,没有。但是,您可以通过检查您对时间感兴趣的查询之前和之后的时间来进行间接且非常接近的估计。

      $sql = "Your Query";
      $bm = "SELECT extract(epoch FROM clock_timestamp())";
      $query = "{$bm}; {$sql}; {$bm};";
      

      函数clock_timestamp() 为您提供语句开始时的服务器实际时间。由于该 SELECT 不涉及表,我们可以预期它几乎是即时的。我猜任何 Pg 驱动程序都提供对多个查询的支持;重要的是这 3 个查询(真正的一个和 2 个额外的)一起使用,否则您还要测量数据传输时间......

      对于 PHP,我有一个函数来处理这个问题。总结起来是这样的:

      <?php
      
      function pgquery($sql, $conn)
      {
          // Prepend and append benchmarking queries
          $bm = "SELECT extract(epoch FROM clock_timestamp())";
          $query = "{$bm}; {$sql}; {$bm};";
      
          // Execute the query, and time it (data transport included)
          $ini = microtime(true);
      
          pg_send_query($conn, $query);
      
          while ($resource = pg_get_result($conn))
          {
              $resources[] = $resource;
          }
      
          $end = microtime(true);
      
          // "Extract" the benchmarking results
          $q_ini = pg_fetch_row(array_shift($resources));
          $q_end = pg_fetch_row(array_pop($resources));
      
          // Compute times
          $time = round($end - $ini, 4);             # Total time (inc. transport)
          $q_time = round($q_end[0] - $q_ini[0], 4); # Query time (Pg server only)
      
          return $resources;
      }
      
      ?>
      

      我只是把基础知识留在那里。 $conn 包含一个指向 Pg 连接的链接,$resources 是一个返回的 pg 资源数组(如果您在 $sql 中发送了多个查询)。

      $time 保存从查询离开到 Pg 服务器到结果到达的总时间。 $q-time 仅保存您想要的实际查询时间(或非常好的近似值)。

      根据自己的喜好添加错误处理和其他处理,我有很多但这与您的问题无关。

      【讨论】:

        【解决方案4】:

        您不能在 SQL 中执行此操作,因为即使您能够在循环中调用每条语句,每次调用 now() 都会返回相同的结果,因为您处于单个事务中。

        可以通过创建一个自己的 volatile now() 函数,在每次调用时返回另一个值。

        【讨论】:

          【解决方案5】:

          这里是@user832146提案的sql版本:

          CREATE OR REPLACE FUNCTION "get_sql_runtime"(
            PAR_sql TEXT
          , OUT sql_runtime REAL
          )
          AS $$
          DECLARE
            run_time_start TIMESTAMP WITH TIME ZONE;
            run_time_end TIMESTAMP WITH TIME ZONE;
          BEGIN
            SELECT clock_timestamp() INTO run_time_start;
            EXECUTE PAR_sql;
            SELECT clock_timestamp() INTO run_time_end;
            SELECT EXTRACT(EPOCH FROM (run_time_end - run_time_start)) INTO sql_runtime;
          END; $$
          LANGUAGE plpgsql
          VOLATILE;
          

          您将其用作:

          SELECT * FROM get_sql_runtime('SELECT * FROM "my_table_name"');
          

          它会产生如下结果:

          sql_runtime
          1.47588

          注意:返回值以秒为单位。

          【讨论】:

            猜你喜欢
            • 2014-04-15
            • 1970-01-01
            • 2019-04-24
            • 1970-01-01
            • 1970-01-01
            • 2019-07-27
            • 1970-01-01
            • 2021-03-24
            • 2021-03-23
            相关资源
            最近更新 更多