【问题标题】:MySQL Count Distinct - Very SlowMySQL Count Distinct - 非常慢
【发布时间】:2015-07-28 06:35:27
【问题描述】:

我有一个非常大的 MySQL InnoDB 表,结构如下:

TABLE `whois_records` (
  `record_id` int(10) unsigned NOT NULL,
  `domain_name` varchar(100) NOT NULL,
  `tld_id` smallint(5) unsigned DEFAULT NULL,
  `create_date` date DEFAULT NULL,
  `update_date` date DEFAULT NULL,
  `expiry_date` date DEFAULT NULL,
  `query_time` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

PRIMARY KEY (`record_id`)
UNIQUE KEY `domain_time` (`domain_name`,`query_time`)
INDEX `tld_id` (`tld_id`)

此表当前有 1000 万行。 它存储经常更新的域名详细信息。 所以同一个域名在表中可以有多条记录。

TLD ID 是域扩展类型的数值。

问题是当我尝试计算特定 TLD 的域名总数时。

我尝试了以下 3 个 SQL 查询:

SELECT COUNT(DISTINCT(domain_name)) FROM `whois_records` WHERE tld_id=159
SELECT COUNT(*) FROM `whois_records` WHERE tld_id=159 GROUP BY domain_name
SELECT COUNT(*) FROM ( SELECT 1 FROM `whois_records` WHERE tld_id=159 GROUP BY domain_name) q

这 3 个都非常慢,需要 5 到 10 分钟。它也占用了大量的 CPU 来完成。 TLD ID 列上定义了 INDEX,因此这些查询可能正在执行 FULL INDEX SCAN。它仍然很慢。 159 的 TLD ID 用于“.com”,这是数量最多的。因此,在搜索 159 时,它是最慢的。对于域名少于 100 个的非流行 TLD,相同的查询大约需要 0.10 秒。 TLD ID 159 有大约 600 万条记录,占由 1000 万行组成的整个表的 60%。

有没有办法优化计算?

随着表的增长,当前查询将花费更长的时间。所以请任何人都可以帮助我解决这个问题的未来证明解决方案。是否需要更改表格?请帮忙,谢谢:)

【问题讨论】:

  • 分享您的 my.cnf 配置文件和服务器配置(CPU、内存、驱动器类型、专用机器与否)。这一切对于为您指明正确的方向至关重要。
  • 第二个问题:tld_id 可以为空吗?如果没有,请先更改方案。无效字段(可以为 NULL)显着减慢查找速度。
  • 感谢您的回复。是的,对于无法识别的域扩展,tld_id 可以为 NULL。我应该删除 NULL,并将所有 NULL 更改为 0 吗?
  • 是的,如果应用程序逻辑可以处理的话。但这仅仅是开始。请分享服务器配置
  • 问题是基数,如果你的条件从表中返回大部分行,索引搜索或松散的索引扫描很快就会变得无用。所以你最希望的是索引扫描和如果 mysql 优化器这么认为,即使这样也可能不是一个选项。简而言之,索引的值太多而没有意义

标签: mysql database innodb


【解决方案1】:

扩展索引以包含domain_name

INDEX `tld_id` (`tld_id`, `domain_name`)

这应该使 MySQL 只使用索引而不是表数据来计算结果。如果两个值的组合是唯一的,则添加一个新的唯一索引:

UNIQUE INDEX `new_index` (`tld_id`, `domain_name`)

我怀疑你能不能把它推得更远。如果仍然不够快,请考虑缓存计数器。

【讨论】:

  • tld_id 和 domain_name 的组合不是唯一的,对于像“example.com”这样的域,会有多行值:example.com,159
  • 谢谢,你的方法奏效了。将“domain_name”包含在“tld_id”索引中,总查询时间和 CPU 使用率下降了 75%。另外,MySQL Explain 现在在最后一列中显示“使用索引”。早些时候它显示“NULL”。但有一件事,表大小增加了 5%。这必须用于新的 INDEX。非常感谢您的解决方案:)
  • @SadasivaUlaka 综合指数有帮助,太好了!减少了多少时间?这是可以接受的还是您正在寻找其他优化技术?
  • 您好 Anatoly,每天处理的 TLD 总数为 1000 个。因此,每天,一个 PHP 脚本都会运行并执行此 SQL 1000 次。以前,脚本大约需要 20 分钟才能完成。现在只需要 90 秒。此外,RDS CloudWatch 监控系统不再显示 CPU 峰值,并且当此脚本运行时 CPU 始终低于 5%。这对我们来说是可以接受的。顺便说一句,删除 NULL 并将 NULL 值更改为 0 已完成。但是将列从 NULL 更改为 NOT NULL 需要花费数小时和数小时,因此我们无法完成该更改。现在这些列像以前一样使用 NULL
  • Using index 很好——它表明查询完全在该索引的 BTree 中完成。表大小增加了 5%——这包括索引吗? 159 有多少行?查询时间现在应该与该计数大致成比例。
猜你喜欢
  • 2012-06-30
  • 2017-03-23
  • 2012-10-13
  • 2021-09-17
  • 1970-01-01
  • 2016-04-04
  • 1970-01-01
  • 2015-01-28
  • 1970-01-01
相关资源
最近更新 更多