在具有160M +行的MySQL InnoDB表上选择查询非常慢

时间:2017-04-25 08:53:47

标签: mysql sql innodb sql-optimization amazon-rds-aurora

下面是我创建的表结构和索引。这张表有160多万行。

create table test
(
client_id varchar(100),
user_id varchar(100),
ad_id varchar(100),
attr0 varchar(250),
scp_id varchar(250),
attr1 datetime null default null,
attr2 datetime null default null,
attr3 datetime null default null,
attr4 datetime null default null,
sent_date date null default null,
channel varchar(100)
)ENGINE=InnoDB;

CREATE INDEX idx_test_cid_sd ON test (client_id,sent_date);
CREATE INDEX idx_test_uid ON test (user_id);
CREATE INDEX idx_test_aid ON test (ad_id);

以下是我正在运行的查询:

select 
 count(distinct user_id) as users
 count(distinct ad_id) as ads
, count(attr1) as attr1
, count(attr2) as attr2
, count(attr3) as attr3
, count(attr4) as attr4
from test
where client_id = 'abcxyz'
and sent_date >= '2017-01-01' and sent_date < '2017-02-01';

问题:上面的查询需要花费大量时间超过1小时才能返回结果。当我看到解释计划时,它正在使用索引和扫描仅800万条记录,但奇怪的问题是返回结果需要1个多小时。

有谁可以告诉我这里出了什么问题或有关优化部分的任何建议?

2 个答案:

答案 0 :(得分:1)

您可以只添加covering index包含where - 子句的列,还可以添加结果的选定列。通过这种方式,查询可以从索引中读取整个结果,而不必读取单个行。您在where子句中使用的列必须保留为索引的第一列,以便此索引可用于where限制。

CREATE INDEX idx_test_cid_sd_cover_all ON test 
    (client_id, sent_date, user_id, ad_id, attr1, attr2, attr3, attr4);

但是,此索引将大于现有索引,因为该表的几乎所有数据都将作为索引中的副本存在。

答案 1 :(得分:1)

  • 缩小表格以减少对I / O的需求。这包括标准化(在可行的情况下)。对各种ID使用合理大小的AUTO_INCREMENT - 而不是VARCHAR。如果您可以解释这些变量,我可以评估这是否实用以及您可能获得多少好处。

  • 拥有PRIMARY KEY。 InnoDB不喜欢没有。 (这不会对特定问题有所帮助。如果列的某些组合为UNIQUE,则将其设为PK。如果不是,请使用id INT UNSIGNED AUTO_INCREMENT;直到40亿之后才会耗尽ID。

  • 更改PRIMARY KEY以使查询运行得更快。 (虽然也许并不比Simulant的“覆盖”指数快。)但它不会那么笨重:

假设你添加id .. AUTO_INCREMENT,那么:

PRIMARY KEY(client_id, sent_date, id),
INDEX(id)

数据有多大(GB)?指数?您可能处于“太大而无法缓存”的尖端,支付更多内存可能有所帮助。

  • 摘要表非常适合COUNT,但不适合COUNT(DISTINCT ...)。也就是说,计数可以在几秒钟内完成。对于Uniques,请参阅my blog。唉它很粗略;请求帮忙。它提供的汇总COUNT(DISTINCT...)COUNT一样高效,但错误率为1-2%。

摘要表的要点:PRIMARY KEY(client_id, day)包含每天计数的列。然后获取一个月的值是SUMming 31天的计数。非常快。更多关于Summary Tables