如何在 3GB 表上优化 Mysql Select

Posted

技术标签:

【中文标题】如何在 3GB 表上优化 Mysql Select【英文标题】:How to optimize Mysql Select with count on 3GB table 【发布时间】:2019-03-12 14:10:40 【问题描述】:

我有一个包含 37,000,000 行和 3gb 数据的表。

CREATE TABLE `stats_raw` (
    `user_id` INT(11) NOT NULL,
    `date` DATETIME(6) NOT NULL,
    `ip` VARBINARY(16) NULL DEFAULT NULL,
    INDEX `stats_raw_user_id_index` (`user_id`),
    INDEX `stats_raw_date_index` (`date`),
    INDEX `stats_raw_user_id_data_index` (`user_id`, `date`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
;

当我尝试执行以下查询时(由 laravel 生成,这就是为什么它看起来很奇怪):

select count(ip) as total, INET6_NTOA(ip) as ip
from `stats_raw`
where `user_id` = 1 and date(`date`) >= '2019-02-10'
group by `ip`
order by `total` desc
limit 10

返回结果大约需要 40 秒。

如何在 mysql 上进行优化?

【问题讨论】:

使用物化视图? 嗨@RedaMeskali 我第一次听说它,这很容易做到吗? 是的,您可以在这里阅读它们:fromdual.com/mysql-materialized-views。 【参考方案1】:

首先,我将查询写成:

select count(ip) as total, INET6_NTOA(ip) as ip
from `stats_raw`
where `user_id` = 1 and `date` >= '2019-02-10'
group by `ip`
order by `total` desc
limit 10;

其次,您想要在stats_raw(user_id, date, ip) 上建立索引。

也就是说,目前尚不清楚正在处理多少数据。我认为没有办法绕过group byorder by 的排序,所以如果你有大量数据,你可能无法加快这个查询,没有更多的英勇努力(例如维护汇总表使用触发器)。

【讨论】:

感谢 Gordon,我更新了总行数,我在写问题时添加了 xxx 占位符并忘记更新它。将检查您的建议并更新 根据您的建议,查询时间缩短到 15 秒,这是一个很大的改进。会等着看我是否得到任何其他答案并奖励你。再次感谢 只做COUNT(*),而不是COUNT(ip) 嗨@RickJames 试过了,但我看不出几秒钟的区别。谢谢【参考方案2】:

Gordon 对这个查询非常满意,所以我的回答假设您将使用他的查询。

您可以尝试的一个小改进是将date 列数据类型更改为DATE,而不是DATETIME(假设您实际上不需要时间部分。)

通过这样做,您可以减少storage on that column from 5 bytes (or 8, if using a version of MySQL older than 5.6) to 3,这将允许一次加载更多数据,从而减少一些开销。

当然,如果您需要时间部分做其他事情,那么这不是一个选择。

【讨论】:

好吧,然后为用户建议一个较小的INT。并使其成为UNSIGNED。并摆脱stats_raw_user_id_index

以上是关于如何在 3GB 表上优化 Mysql Select的主要内容,如果未能解决你的问题,请参考以下文章

如何在大表上优化这个 mysql 连接?

如何告诉 MySQL 优化器在派生表上使用索引?

带有子查询的 CTE 查询在小型索引表上很慢;如何在 MySQL 上进行优化?

Explain结果解读与实践

大表上的慢 MySQL SELECT

具有多个表的联结表上的 MySQL SELECT 查询