下面是我创建的表结构和索引。这张表有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个多小时。
有谁可以告诉我这里出了什么问题或有关优化部分的任何建议?
答案 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。