【问题标题】:BugZilla, best (fastest) way to get bug status (etc) on a given dateBugZilla,在给定日期获取错误状态(等)的最佳(最快)方法
【发布时间】:2013-07-28 03:25:22
【问题描述】:

我正在开发一个从各种来源获取数据并生成报告的应用程序。目前,我正在将其更改为根据历史上给定日期的数据进行报告,以前它仅显示来自今天事物状态的数据。

我的数据源之一是 Bugzilla,因此我需要获取历史上给定日期的 Bugzilla 数据。我有一个到 Bugzilla 数据库的只读连接,但没有简单的方法对服务器执行任何其他操作(例如安装插件或将程序放入数据库中)。报告服务器和 Bugzilla 服务器之间的连接也很慢,所以我想在服务器上进行计算,而不是在报告服务器上获取数据并解决问题。

我实际上以几乎可以接受的速度运行它,但我不确定我是否做得最好或“正确”的方式,我担心随着我们添加更多速度可能不再可以接受数据库的问题。

所以,我的解决方案如下 - 你会怎么做。

作为背景知识,Bugzilla 将所有 bug 的当前状态存储在一个表中(称为“bugs”)以及表中每个字段的更改历史记录(“bugs_activity”),如下所示:

fieldid   INTEGER,   -- References the fielddefs table
bug_when  TIMESTAMP, -- Time the change happend
added     TEXT,      -- New text for the field
removed   TEXT,      -- Old text for the field

Bugzilla 数据库是 MySQL。我认为正确的方法是使用存储过程或临时表,但我没有可用的任何选项。我知道还有用于 Bugzilla 的报告工具,但我无权安装它们,我生成的报告也与来自其他来源的数据相关联(并且具有特定格式)。

报告服务器上有一个本地 PostgreSQL 数据库,所以我可以定期将所有数据镜像到那里,但我真的不想这样做,因为在两个地方存储相同的数据似乎有点浪费。

我的解决方案是在子选择中构建一个看起来像正常错误表的表(对于给定报告我感兴趣的数据),然后将此选择用作正常选择的源作为基于今天数据的报告的查询。

SELECT bug_status, bug_id, op_sys, resolution, rep_platform   
  FROM (SELECT bug_id, 
        IFNULL((SELECT removed FROM bugs_activity a, fielddefs f  
                 WHERE a.fieldid = f.id   
                   AND bug_id = b.bug_id AND f.name = 'bug_status' 
                   AND bug_when >= '2012-01-01 00:00:00'  
                 ORDER BY bug_when DESC LIMIT 1), bug_status) AS bug_status,
    -- Repeat IFNULL clause for op_sys, resolution and rep_platform
        FROM bugs b 
        WHERE b.creation_ts <= '2012-01-01 00:00:00' ) bug_subselect
        -- Some other filters to reduce the bugs (specific product, ect)
      )
    -- More filters based on the new values that have been derived
     ;

然后我将其用作计算不同状态等的选择的输入。

这个查询结果太慢了,我假设是因为它获取了内部选择的全部结果,所以它可以订购然后给我最上面的。

我确实尝试通过将 bugs_activity 表多次左连接到 bugs 表上,然后对结果进行 IFNULL 查询来做到这一点,这很快但在生成代码中维护起来有点复杂,因此对其进行了调整:

SELECT bug_status, bug_id, op_sys, resolution, rep_platform   
  FROM (SELECT bug_id,
    IFNULL((SELECT removed FROM bugs_activity a, fielddefs f 
             WHERE a.fieldid = f.id AND bug_id = b.bug_id AND f.name = 'bug_status'
               AND bug_when = (
                     SELECT MIN(bug_when) FROM bugs_activity a, fielddefs f 
                      WHERE a.fieldid = f.id 
                            AND bug_id = b.bug_id 
                            AND f.name = 'bug_status'
                        AND bug_when >= '2012-01-01 00:00:00' 
                          LIMIT 1 
                        )
             LIMIT 1), bug_status) AS bug_status,
        -- Repeat IFNULL clause for op_sys, resolution and rep_platform
        FROM bugs b 
        WHERE b.creation_ts <= '2012-01-01 00:00:00' ) bug_subselect
        -- Some other filters to reduce the bugs (specific product, ect)
      )
    -- More filters based on the new values that have been derived
      ;

您需要两个 LIMIT 1(我认为),因为某些字段设法在同一个时间戳上进行了两次更改(数据库故障,可能来自升级,或者两个用户编辑相同的错误 - 我是不确定,我只知道它在那里,我需要处理它)。

在没有过滤器的情况下运行大约 3 秒以减少错误列表(这是最坏的情况,几乎永远不会发生),并且使用过滤器运行得更快。 LEFT JOIN 版本的运行速度大致相同(稍慢),所以我选择了上面的那个。目前还可以,但我可以看到它将来会变慢——我将在 GUI 中添加一条加载消息,并且已经有一条消息说这些报告可能需要更长的时间才能生成,我只是想知道我是否'我错过了一些让它更快的技巧。

【问题讨论】:

  • “我只是想知道我是否错过了一些让它更快的技巧” - 是的。它们被称为索引...
  • bugs_activity 表没有索引,还是你的意思是别的。
  • 我的意思正是我所说的 - 索引可以加快查询速度。
  • 最好的办法是找人创建索引。您应该可以访问 MySQL 数据库,以便您执行此操作,以便您知道谁有权访问。问他们。
  • @Ben 是的,由于各种原因,在生产服务器上获得批准和实施的更改将是非常重要的,但你是对的,我应该开始这个过程。我想我要问的是“没有索引我能做到吗?”。

标签: sql report bugzilla


【解决方案1】:

如果我没听错的话,你可以试试这个..

SET @tdate = '2012-01-01 00:00:00';

SELECT  
  b.bug_id
  ,CASE 
    WHEN s.removed IS NULL THEN b.bug_status
    ELSE s.removed
  END AS statusAtDate
  ,CASE 
    WHEN o.removed IS NULL THEN b.op_sys
    ELSE o.removed
  END AS apSysAtDate
FROM
  bugs AS b 
  LEFT OUTER JOIN (
    SELECT 
      a.bug_id
      ,a.bug_when
      ,a.removed
      ,a.bug_when
      ,@row_num := IF(@last=a.bug_id,@row_num+1,1) AS rnk
      ,@last:=a.bug_id
    FROM 
      bug_activity AS a
      INNER JOIN fielddefs AS f
        ON a.fieldid = f.id
          AND f.name = 'bug_status'
    WHERE
        a.bug_when <= @tdate
    ORDER BY 
      a.bug_id
      ,a.bug_when
    ) AS s
      ON b.bug_id = s.bug_id
      AND s.rnk=1
  LEFT OUTER JOIN (
    SELECT 
      a.bug_id
      ,a.bug_when
      ,a.removed
      ,a.bug_when
      ,@row_num := IF(@last=a.bug_id,@row_num+1,1) AS rnk
      ,@last:=a.bug_id
    FROM 
      bug_activity AS a
      INNER JOIN fielddefs AS f
        ON a.fieldid = f.id
          AND f.name = 'op_sys'
    WHERE
        a.bug_when <= @tdate
    ORDER BY 
      a.bug_id
      ,a.bug_when
    ) AS o
      ON b.bug_id = o.bug_id
      AND o.rnk=1

--repeat for resolution and rep_platform

对不起,我这里没有数据库来验证代码,如果有拼写错误或类似情况,请见谅。

我不知道您之前是否这样做过左外连接,但如果您使用会话变量进行重用,这是否有帮助/工作?

不确定这是否有帮助/p>

顺便说一句,我不是优化专家(远非如此).. 只是说除了在旅途中获取一些索引的好建议之外我会尝试什么。

编辑:

你可以尝试的另一件事..我认为这应该可行..

SELECT
  bug_id
  ,bug_status
  ,op_sys
  ,max(old_status)
  ,max(old_opSys)
(
SELECT  
  *
FROM
  bugs AS b 
  LEFT OUTER JOIN (
    SELECT 
      a.bug_id
      ,a.bug_when
      ,if(f.name = 'bug_status',a.removed,NULL) AS old_status
      ,if(f.name = 'op_sys',a.removed,NULL) AS old_opSys
      ,a.bug_when
      ,@row_num := IF(@last=a.bug_id AND@lastField=f.name ,@row_num+1,1) AS rnk
      ,@last:=a.bug_id
      ,@lastField:=f.name
    FROM 
      bug_activity AS a
      INNER JOIN fielddefs AS f
        ON a.fieldid = f.id

    WHERE
        a.bug_when <= '2012-01-01 00:00:00'
        AND f.name in( 'bug_status','op_sys')
    ORDER BY 
      a.bug_id
      ,f.name
      ,a.bug_when
    ) AS s
      ON b.bug_id = s.bug_id
      AND s.rnk=1
) AS T
  GROUP BY
    bug_id
    ,bug_status
    ,op_sys

我在这里省略了外部选择中的 case 或 if 语句。我在想无论哪种解决方案都值得测试它是如何在代码而不是数据库中进行最终检查的?即使可行,您也可能不会选择它,但它可能值得一试。

就像这样:

<%= row->old_status ?: row->bug_status %>

(对不起,如果我的 PHP 关闭了.. 并没有真正使用它)

似乎它应该工作? http://sqlfiddle.com/#!2/eff8c/1

【讨论】:

  • 您可能会做一些时髦的事情,您可以为所有字段加入一次活动表,并在每次 bug_id 或字段名更改时更改 rnk 列。然后将整个批次按 bug_id 和 max 包装在一个组中。不知道它是否会更快,并且不记得是否可以在 mysql 中最大化这样的文本字段。
  • 感谢您花时间回答。这是一种有趣的方法,我将不得不稍微摆弄一下。但目前它比我的方法慢了大约六倍。有趣的是,如果@tdate 为NULL(因为我忘了设置它),它会快得多,我猜服务器正在做一些巧妙的优化(或者因为内部没有返回结果)。我会做一些测试和分析,它至少给了我更多探索的途径。 :)
  • 速度差这么大!嗯,您可以尝试将 case 语句换成您拥有的 if。我只使用 case,因为它们是我的默认选项(由于它们增加了灵活性和可读性),但在这种情况下,它们可能比 if 慢得多? row_number 部分也可能很慢,也许只值得做一次。
  • 添加了一种不同的方法......不确定它会更好,它可能不起作用!此外,如果您要设置索引,则可能值得保存几个不同工作版本的副本,以便在创建索引后再次测试
  • 谢谢,它可以工作(有一些调整),但它仍然很慢。我做了一些调查,它肯定是慢的 JOIN,内部查询足够快但返回 26,000+ 行,并且错误表中有 10,000 ish。服务器之间的连接不是很好,所以在应用程序中工作并不理想,但我认为我可以混合这些想法,让工作更快。我得花点时间考虑一下——但是谢谢,你发布的内容给了我更多的选择。
【解决方案2】:

我建议使用 Bugzilla REST API 接口而不是直接访问 Bugzilla DB。 这是一个示例 API,用于检索在特定日期创建的错误。

https://api-dev.bugzilla.mozilla.org/test/1.3/bug?creation_date=2008-03-31

参考资料:
https://wiki.mozilla.org/Bugzilla:REST_API
https://wiki.mozilla.org/Bugzilla:REST_API:Objects
https://wiki.mozilla.org/Bugzilla:REST_API:Search

【讨论】:

  • 我确实看过 REST API,但我读到它有很多错误(虽然这可能只是在我们的旧版本上,但我们很快就会更新)。此外,似乎很难问“2012 年 1 月 1 日错误 1234 的状态是什么”,看起来我仍然必须下载并解析更改集。我想可能有一个复杂的布尔查询可以做到这一点。当我有机会时,我会对其进行测试,但看起来它会通过慢速链接传输太多信息以比 SQL 查询更快(因为我在子表上进行了 COUNTs 并且只返回原始总数) .
  • 您从哪里得知 REST API 有很多错误?作为它的维护者,我可以向您保证,当前版本非常稳定,Mozilla 项目每周用于数十万次查询。它很快就会被原生 REST API 取代,该 API 将具有相同的接口(带有兼容性填充程序),但速度更快,并且对返回的数据的限制更少。
猜你喜欢
  • 2015-12-07
  • 1970-01-01
  • 1970-01-01
  • 2020-08-01
  • 1970-01-01
  • 2018-08-26
  • 1970-01-01
  • 1970-01-01
  • 2014-03-24
相关资源
最近更新 更多