繁体   English   中英

MySQL慢查询优化国家百分比查​​询

[英]MySQL slow query optimize countries percentage query

此查询需要100秒才能运行。 我已将每个用于条件或连接的列编入索引,但运行时间太长。 如何以有效运行的方式编写此查询?

SELECT e.earning_country, c.country_name, COUNT(e.earning_id) AS views, ROUND(100 * COUNT(e.earning_id)/b.total, 2) AS percentage
FROM earnings AS e
CROSS JOIN (
SELECT COUNT(earning_id) AS total
FROM earnings
WHERE earning_paid = 1 AND earning_ad_id = 1 AND earning_referral_id = 0) AS b

INNER JOIN countries as c
ON c.country_id = e.earning_country_id

WHERE earning_paid = 1 AND e.earning_ad_id = 1 AND earning_referral_id = 0
GROUP BY e.earning_country
ORDER BY percentage DESC

解释结果:

"id",   "select_type",  "table",        "type",         "possible_keys",                                                                                "key",                                                              "key_len",  "ref",                          "rows", "Extra"
1,      "PRIMARY",      "<derived2>",   "system",       NULL,                                                                                           NULL,                                                               NULL,       NULL,                           1,      "Using temporary; Using filesort"
1,      "PRIMARY",      "e",            "index_merge",  "earning_referral_id_index,earning_country_id_index,earning_paid_index,earning_ad_id_index",    "earning_referral_id_index,earning_paid_index,earning_ad_id_index", "4,1,4",    NULL,                           362698, "Using intersect(earning_referral_id_index,earning_paid_index,earning_ad_id_index); Using where"
1,      "PRIMARY",      "c",            "eq_ref",       "PRIMARY",                                                                                      "PRIMARY",                                                          4,          "site.e.earning_country_id",    1,      NULL
2,      "DERIVED",      "earnings",     "index_merge",  "earning_referral_id_index,earning_paid_index,earning_ad_id_index",                             "earning_referral_id_index,earning_paid_index,earning_ad_id_index", "4,1,4",    NULL,                           362698, "Using intersect(earning_referral_id_index,earning_paid_index,earning_ad_id_index); Using where; Using index"

这不是一个真正的答案,但请尝试运行以下查询以了解您以简单的方式处理此数据的速度:

SELECT
  e.earning_country
  ,c.country_name
--  ,COUNT(e.earning_id) AS views
FROM earnings AS e
INNER JOIN countries as c
        ON c.country_id = e.earning_country_id
WHERE earning_paid = 1 AND e.earning_ad_id = 1 AND earning_referral_id = 0
GROUP BY e.earning_country_id
;

尝试使用views行进行注释和运行来运行它,并查看性能上的差异,注意:我注意到您在原始查询中通过earning_country而不是earning_country_id进行分组。

PS - 如果此查询运行得更快,您可以在内存中完成剩余的计算以获得总计,百分比并对其进行排序。

如果您想了解三向索引的大小,请尝试运行查询:

SELECT 
  COUNT(DISTINCT earning_paid, earning_ad_id, earning_referral_id)
FROM earnings;

索引大小应基于数据的可变性,而不是表的大小。

如果earning_id永远不是NULL (并且主键不应该是),那么您可以通过使用COUNT(*)而不是COUNT(earning_id)来提升性能。

MySQL每个表只使用1个索引。 所以你有4列的索引用于where子句和连接只会使用其中一个索引。 MySQL会选择它认为最好的索引,但这可能远非完美。

使用您的查询我怀疑earning_paid是一个标志,所以它本身可能对索引很少使用(平均一半的记录将具有每个值)。 使用earning_ad_id和earning_referral_id,您似乎正在检查0,这是我假设每个的默认值,并且每个可能再次覆盖大量行。 将这3个组合在一起可能确实有一些用作索引。

earning_country可能作为聚合函数的索引很有用,但无法缩小行数。

如果您有一个涵盖所有4列的索引,那么可以使用它

设置覆盖earning_paid,earning_ad_id,earning_referral_id和earning_country(按此顺序)的索引。

编辑

小解释

假设你有一本电话簿。 要查找名称,这是按姓氏排序的(实际上是一个索引)。 滚动浏览直到找到所需的名称,按名称顺序这很容易。

如果你想找一个名叫史密斯的人,你可以快速跳到那里。

如果您知道他们的名字,那么您可以在史密斯列表中轻松找到它。 所以可以找到约翰史密斯(无疑很多)。

但是,如果你想找一个叫史密斯的医生而你不知道他们的名字,你就可以在姓氏和头衔上找到一个索引。 如果它是一个罕见的姓氏和一个共同的头衔,最好是姓氏第一和头衔第二,如果姓氏是常见的并且标题很少,那么最好将头衔和姓氏列为第二。

在这种情况下,索引只是每个姓氏和标题的列表,并带有指向记录其余部分的指针。

如果你想要一个名为史密斯的所有医生的计数,那么你可以只需查看索引而不需要查看记录的其余部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM