如何使用 where 索引优化查询?

Posted

技术标签:

【中文标题】如何使用 where 索引优化查询?【英文标题】:How I can optimize query with where index? 【发布时间】:2020-09-01 16:11:50 【问题描述】:

我有疑问

select `price`, `asset_id` 
from `history_average_pairs` 
where `currency_id` = 1 
  and date(`created_at`) >= DATE_SUB(NOW(), INTERVAL 7 DAY) 
group by hour(created_at), date(created_at), asset_id 
order by `created_at` asc

还有桌子

CREATE TABLE IF NOT EXISTS history_average_pairs (
  id bigint(20) unsigned NOT NULL,
  asset_id bigint(20) unsigned NOT NULL,
  currency_id bigint(20) unsigned NOT NULL,
  market_cap bigint(20) NOT NULL,
  price double(20,6) NOT NULL,
  volume bigint(20) NOT NULL,
  circulating bigint(20) NOT NULL,
  change_1h double(8,2) NOT NULL,
  change_24h double(8,2) NOT NULL,
  change_7d double(8,2) NOT NULL,
  created_at timestamp NOT NULL DEFAULT current_timestamp(),
  updated_at timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  total_supply bigint(20) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE history_average_pairs
  ADD PRIMARY KEY (id),
  ADD KEY history_average_pairs_currency_id_asset_id_foreign (currency_id,asset_id),
ALTER TABLE history_average_pairs
  MODIFY id bigint(20) unsigned NOT NULL AUTO_INCREMENT;

它包含超过 10 000 000 行,并且查询需要

显示第 0 - 24 行(总共 32584 行,查询耗时 27.8344 秒。)

但没有currency_id = 1,大约需要 4 秒。

更新 1

好的,我将密钥从 currency_id, asset_id 更新为 currency_id, asset_id, created_at,它需要

显示第 0 - 24 行(总共 32784 行,查询耗时 6.4831 秒。)

它的速度要快得多,有什么建议可以做得更快吗? GROUP BY 这里每小时只取第一行。 例如:

19:01:10 
19:02:14 
19:23:15

我只需要 19:01:10

【问题讨论】:

在currency_id上放一个键 currency_id + created_at 上创建一个组合索引。 currency_id 将有助于过滤, created_at 将有助于过滤和排序 GROUP BY 语句无效,因为查询中没有聚合函数。你想用它来达到什么目的? 有机会上传表格的转储吗? 更改索引的顺序。尝试currency_id, created_at, asset_id 而不是currency_id, asset_id, created_at。请记住,您的过滤谓词应尽早丢弃行。 【参考方案1】:

您可以改写过滤谓词以避免在列上使用表达式。例如:

select max(`price`) as max_price, `asset_id` 
from `history_average_pairs` 
where `currency_id` = 1 
  and created_at >= date_add(curdate(), interval - 7 day)
group by hour(created_at), date(created_at), asset_id 
order by `created_at` asc

然后,如果您添加索引,此查询可能会更快:

create index ix1 on `history_average_pairs` (`currency_id`, created_at);

【讨论】:

【参考方案2】:

您必须使测试“可判断”;改变

date(`created_at`) >= DATE_SUB(NOW(), INTERVAL 7 DAY)

created_at >= CURDATE() - INTERVAL 7 DAY

那么最优索引是

INDEX(currency_id,   -- 1st because of "=" test
      created_at,    -- 2nd to finish out WHERE
      asset_id)      -- only for "covering"

在设计索引时,通常最好先处理WHERE

GROUP BY 不能使用索引。你真的想要第一个小时吗?

“我只需要 19:01:10”不清楚,所以我没有考虑到这一点。日期在哪里?资产ID在哪里?参见“only_full_group_by”。你需要“groupwise max”吗?

使ORDER BYGROUP BY 具有相同的列可以避免排序。 (在您的查询中,顺序可能略有不同,但可能无关紧要。)

数据类型问题...

BIGINT 占用 8 个字节; INT 只占用 4 个字节并且通常足够大。缩小表格提供了一些速度。 double(8,2) 占用 8 个字节 -- 不要在 FLOATDOUBLE 上使用 (m,n);它增加了额外的舍入。也许您的意思是 DECIMAL(8,2),它需要 4 个字节。

【讨论】:

以上是关于如何使用 where 索引优化查询?的主要内容,如果未能解决你的问题,请参考以下文章

mysql 优化策略(如何利用好索引)

Sql查询优化

SQL通用优化方案(where优化索引优化分页优化事务优化临时表优化)

慢查询问题常见的优化方法

MySQL索引优化经验总结

第 2 章 查询条件优化之等值查找