0

我有一个监控应用程序,将指标存储在 MySQL DB 中,并使用 RRDtool 绘制时间序列图。DB 中的表有 3 列:timestamp、RRDKeyID 和 value,即。有一个数据表,如:

ts1 | user2 | some_value
ts1 | user4 | some_value
ts1 | user5 | some_value
ts1 | user7 | some_value
ts2 | user1 | some_value
ts2 | user2 | some_value
ts2 | user3 | some_value
ts2 | user5 | some_value
ts2 | user8 | some_value
ts3 | user3 | some_value
ts3 | user4 | some_value
ts3 | user5 | some_value
ts3 | user7 | some_value
...

数据以 1 分钟的间隔收集,用户(如您所知)自由连接到系统并生成指标,因此在任何给定时刻,系统中的用户数量都低于总数。用户总数约为 1k 并且在线总是有几百个,这意味着在 metric 表中有数百行具有相同的时间戳。当我生成图表时,我不想为每个用户显示该系列,因为由于画布尺寸太小、调色板有限、图例太长等原因,该图表不可读。这就是为什么我改为生成一个图表,其中只有按指标值排名前 10 位的用户单独显示,所有其他用户汇总到一个黑色区域。我就是这样做的:

:1: 我从表中获得前 10 名:

select RRDKeyID, avg(value) as avg
from metric_table
where ts between 't1' and 't2'
group by RRDKeyID
order by avg desc
limit 10

:2: 然后在 Perl 中生成 RRDtool 命令,该命令绘制每个用户 (RRDKeyID) 都有自己的 DEF 的图形,但只有 SQL 查询结果中的用户 DEF 被直接绘制,其余的都被聚合:

CDEF:others=0,user11,ADDNAN,user12,ADDNAN,user13,ADDNAN,...,userN

然后"AREA:others#000000:OTHERS:STACK"显示在图表上。

现在我正在开发应用程序的下一个版本,由于 DB 模式的变化,RRDtool 将被 D3.js 取代,所以我需要另一种方法来生成我的 top10 + 其他人的数据。我不想将原始数据发送到客户端并在他们这边进行处理,因为数据集可能非常大,处理可能需要很多时间和 CPU,所以我宁愿在服务器端进行。我怀疑有可能以以下格式获得我想要的结果:

 ts   | user   | value 
------+--------+-------
 t1   | u1     | v 
 t1+1 | u1     | v 
 t1+2 | u1     | v 
...
 t2   | u1     | v 
 t1   | u2     | v 
...
 t2   | u2     | v 
 t1   | u3     | v 
...
 t2   | u3     | v 
...
...
 t1   | u10    | v 
...
 t2   | u10    | v 
 t1   | others | v 
...
 t2   | others | v 

一次性(顺序不重要)使用嵌套查询,其中有一个子查询在给定的时间范围内选择用户名(RRDKeyIDs)和 avg(值),然后顶部查询打印用户名的结果,如果他们是在子查询结果中找到或以其他方式将它们添加到其他人。我不知道如何用 SQL 表达这个想法,所以如果有人能提出解决方案,我将不胜感激。

4

2 回答 2

1

枚举 MySQL 中的值然后重新聚合怎么样?

select (case when rn <= 10 then RRDKeyId end) as RRDKeyId,
       sum(sumv) / sum(cnt) as avgvalue
from (select (@rn := @rn + 1) as rn, mt.*
      from (select RRDKeyID, sum(value) as sumv, count(*) as cnt
            from metric_table
            where ts between 't1' and 't2'
            group by RRDKeyID 
            order by avg(value) desc
           ) mt cross join
           (select @rn := 0) vars
      ) mt
group by (case when rn <= 10 then RRDKeyId end);

NULL用于额外的组。如果你愿意,你可以换个名字。

于 2015-03-23T18:21:36.073 回答
0

据我了解,你想

  1. 发现前 10 名
  2. 获取数据以绘制它们
  3. 其余的取平均值,制作第 11 个图。

就像是:

$list = 
SELECT  GROUP_CONCAT(RRDKeyID)
    FROM  
      ( SELECT  RRDKeyID, AVG(value)
            FROM  metric_table
            WHERE  ts between 't1' AND 't2'
            group by  RRDKeyID
            order by  AVG(value) desc
            limit  10 
      ) x; 
SELECT  ...              WHERE  RRDKeyID     IN ( $list ) ...; -- top 10
SELECT  ... AVG(...) ... WHERE  RRDKeyID NOT IN ( $list ) ...; -- the rest

很难将这三个语句组合成一个查询。IN 将变成 JOIN / LEFT JOIN,并且需要重复原始查询。

于 2015-03-23T18:14:53.380 回答