【问题标题】:SQL query: Speed up for huge tablesSQL 查询:为大表加速
【发布时间】:2015-07-23 00:43:22
【问题描述】:

我们有一个包含大约 25,000,000 行的表,称为“事件”,具有以下架构:

TABLE events
- campaign_id   : int(10)
- city      : varchar(60)
- country_code  : varchar(2)

以下查询需要很长时间(> 2000 秒):

SELECT COUNT(*) AS counted_events, country_code
FROM events
WHERE campaign_id` in (597) 
GROUPY BY city, country_code
ORDER BY counted_events

我们发现这是因为 GROUP BY 部分。

(campaign_id, city, country_code) 上已有一个索引 idx_campaign_id_city_country_code 已使用。

也许有人可以提出一个很好的解决方案来加快速度?

更新:

'解释'表明在许多可能的索引中,MySql 使用了这个:'idx_campaign_id_city_country_code',对于它显示的行:'471304',对于'Extra',它显示:'Using where;使用临时的;使用文件排序' -

这是 EXPLAIN 的全部结果:

  • id:'1'
  • select_type: '简单'
  • 表:“事件”
  • 类型:'ref'
  • possible_keys: 'index_campaign,idx_campaignid_paid,idx_city_country_code,idx_city_country_code_campaign_id,idx_cid,idx_campaign_id_city_country_code'
  • 键:'idx_campaign_id_city_country_code'
  • key_len: '4'
  • 参考:'const'
  • 行数:'471304'
  • 额外:'使用where;使用临时的;使用文件排序'

更新:

好的,我想已经解决了:

再次查看此处粘贴的查询,我意识到我忘记在这里提到 SELECT 中还有一个名为“country_name”的列。所以当时查询很慢(包括 country_name),但我就把它省略了,现在查询的性能绝对没问题。 对不起那个错误!

因此,感谢您提供的所有帮助 cmets,我会为所有好的答案投票!有一些非常有用的补充,我可能也会应用(比如更改类型等)。

【问题讨论】:

  • 什么解释 SELECT COUNT(*) AS counted_events, country_code FROM events WHERE campaign_id` in (597) GROUPY BY city, country_code ORDER BY counted_events 给出了什么解释?
  • 'Explain' 表明,在许多可能的索引中,MySql 使用了这个:'idx_campaign_id_city_country_code',对于它显示的行:'471304',对于'Extra',它显示:'Using where;使用临时的;使用文件排序'
  • 这里的邪恶是ORDER BY counted_events 导致Using temporary; Using filesort'
  • 要排序的行太多。我认为问题来自数据库设计
  • @TruongHua 有一张表,所以不像有数据库设计。他只需要修复类型和索引。优化后,此查询将运行良好

标签: mysql sql database performance indexing


【解决方案1】:

无论如何,没有看到 EXPLAIN 说这是远距离射击:

  1. 在 (city,country_code) 上建立索引
  2. 看看有没有办法使用分区,你的表越来越大了
  3. 如果国家代码始终是 2 个字符,则将其更改为 char
  4. 将数字索引更改为无符号整数

发布整个 EXPLAIN 输出

【讨论】:

  • 请格式化您的输出并为表添加实际架构。否则很难提供帮助
  • 酷,别忘了改变类型,这很重要。我还要求查看实际的 show create table 结果,因为例如,不需要在 country_code 上使用 utf-8 字符集,一个简单的 latin_general_ci 字符集将非常适合您的需要,它会为每个字符节省一个字节:)跨度>
【解决方案2】:

不要使用IN() - 更好地使用:

WHERE campaign_id = 597
OR campaign_id = 231
OR ....

afaik IN() 非常慢。

更新: 喜欢 nik0lias 评论 - IN() 比连接 OR 条件更快。

【讨论】:

  • 那你就错了。有很多证据表明 IN 实际上更快。这只是一个问题..stackoverflow.com/questions/782915/mysql-or-vs-in-performance
  • 对于 25,000,000 行多个 or 即使使用索引列也不起作用。
  • @northkildonan 这听起来比我想象的更刺耳!
  • @nik0lias nvm,我以为我遇到了这个确切的问题 - 但我没有仔细检查 - 所以我的错。我不知何故将它与一些 PHP 数组搜索行为混淆了。
【解决方案3】:

一些想法:

  • 考虑到表格的性质和大小,它非常适合按国家/地区划分partitioned tables。这样每个国家的事件都会存储在不同的物理表中,即使它表现为虚拟大表

  • 国家代码是字符串吗?可能是您有一个更容易排序的 country_id。 (它可能会强制您创建或更改索引)

  • 你真的在使用group by中的城市吗?

【讨论】:

    【解决方案4】:
    • 分区 - 尤其是按国家/地区不会有帮助
    • column IN (const-list) 不慢,其实是经过特殊优化的情况

    问题是,MySQL 不使用索引进行排序。我不能说为什么,因为它应该。可能是一个错误。

    执行此查询的最佳策略是扫描 event_id=597 的索引的子树。由于索引随后按 city_id 排序,因此 country_code 不需要额外排序,并且可以在扫描时计算行数。

    所以索引对于这个查询已经是最优的了。 MySQL 只是没有正确使用它们。


    我正在离线获取更多信息。看来这根本不是数据库问题,而是

    1. 架构未标准化。该表不仅包含 country_code,还包含 country_name(这应该在一个额外的表中)。
    2. real 查询在选择列表中包含 country_name。但由于该列没有被索引,MySQL 不能使用索引扫描。

    从选择列表中删除 country_name 后,查询将恢复为仅索引扫描(EXPLAIN 输出中的“使用索引”)并且速度非常快。

    【讨论】:

      猜你喜欢
      • 2023-04-08
      • 2021-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-15
      • 2015-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多