【问题标题】:Any way to get 95th percentile and sum in the same query?有什么方法可以在同一个查询中获得第 95 个百分位数并求和?
【发布时间】:2013-11-04 00:39:30
【问题描述】:

我有一个大型 MySQL 表,即使索引正确,每个查询也可能需要 1 秒(听起来并不多,但它运行在数千台服务器上)。现在,我有四个查询要进行,以获得第 95 个百分位的入站、第 95 个百分位的出站以及两者的总和。

查询 1:获取行数以获取第 95 个百分位行

SELECT round(count(*)*.95 FROM traffic WHERE server_id = 1;

查询 2&3 得到第 95 个百分位

SELECT inbound FROM traffic WHERE server_id = 1 ORDER BY inbound ASC LIMIT {95th},1
SELECT outbound FROM traffic WHERE server_id = 1 ORDER BY outbound ASC LIMIT {95th},1

查询 4 ​​获取流量总和

SELECT sum(inbound+outbound) FROM traffic WHERE server_id = 1; 

你能想出什么方法可以结合这些吗?我面临着想办法的挑战,因为我需要得到第 95 个百分位数,这是通过根据计数选择特定行来计算的。例如,如果有 10000 行,则按升序排列并选择第 9500 行。

【问题讨论】:

    标签: mysql sql database performance percentile


    【解决方案1】:

    如http://planet.mysql.com/entry/?id=13588 中所述:

    SELECT
        SUBSTRING_INDEX(
                SUBSTRING_INDEX(
                    GROUP_CONCAT( 
                        t.inbound
                        ORDER BY t.inbound
                        SEPARATOR ','
                    )
                ,   ','
                ,   95/100 * COUNT(*) + 1
                )
            ,   ','  
            ,   -1  
            )                 AS `Inbound95`
        ,
        SUBSTRING_INDEX(
                SUBSTRING_INDEX(
                    GROUP_CONCAT(  
                        t.outbound
                        ORDER BY t.outbound
                        SEPARATOR ','
                    )
                ,   ','         
                ,   95/100 * COUNT(*) + 1 
                )
            ,   ','                       
            ,   -1                          
            )                 AS `Outbound95`
    FROM   traffic AS t WHERE t.server_id = 1
    

    会给你两个百分位数

    注意:您可能需要增加group_concat_max_len

    【讨论】:

    • 从技术上讲,这可以在单个查询中给出答案。但是,这将是该死的慢
    • @mvp 您不能以有效的方式将两个 95% 的百分位组合到一个查询中,因为查询排序顺序不同(苹果和橙子)。
    • 好吧,即使您将第 95 个百分位替换为第 5 个百分位(请参阅我的回答中的方法),它也会使您的查询速度大大加快。
    • 这实际上要快得多,但不幸的是它没有显示正确的第 95 个百分位值。不过,我对 SUBSTRING_INDEX 的了解还不够,无法对其进行分析。
    • @Nirk 我发现问题与 group_concat_max_len 有关。提高这个限制后,它解决了这个问题。感谢您的帮助!
    【解决方案2】:

    如果您愿意放弃一些精度,您可以使用估算来计算行数,而不是精确的行数。如果您的数据库使用 InnoDB,SELECT count(*) 可能会非常慢。换句话说:

    1. 要获得估计,您可以使用SHOW TABLE STATUS 命令。这将是闪电般的速度,但不一定是 100% 准确。

    2. 替换你的陈述:

      SELECT inbound FROM traffic WHERE server_id = 1 ORDER BY inbound ASC LIMIT {95th},1
      

      与

      SELECT inbound FROM traffic WHERE server_id = 1 ORDER BY inbound DESC LIMIT {5th},1
      

      结果应该相同,但速度要快 20 倍。只需确保在 (server_id, inbound) 上创建复合索引。

    3. 见 2.

    4. 别管这个了。

    我希望获得必要数字的总时间将减少到几毫秒。

    【讨论】:

    • 如何结合任何查询?
    • @Nirk:不是。 OP 抱怨性能问题,并认为合并到一个查询中会加快速度。我提出的解决方案让它变得非常快,这确实是困扰 OP 的问题。
    • 对每个入站、出站使用复合查询,而不仅仅是 server_id 似乎极大地增加了索引大小,并且只减少了大约 2% 的时间。有什么我应该做的不仅仅是:ADD INDEX compound USING BTREE (server_id ASC, inbound ASC);
    • @Devon:您是否将其更改为使用第 5 个百分位数而不是第 95 个百分位数?关于索引:您需要有 2 个索引:(server_id ASC, inbound DESC) 和 (server_id ASC, outbound DESC) - ASC 和 DESC 必须与查询中使用的顺序匹配。请务必通过 EXPLAIN ANALYZE 确认 MySQL 使用正确的索引。
    • 我仍然在查询中使用 ASC 和 95th 来比较索引。 Nirk 的建议似乎很有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 2011-10-10
    • 1970-01-01
    • 2021-10-24
    • 2018-09-02
    相关资源
    最近更新 更多