【问题标题】:Optimize MySQL query using date between on large table在大表上使用日期优化 MySQL 查询
【发布时间】:2023-01-19 23:41:56
【问题描述】:

我的表结构:

CREATE TABLE `jobs_view_stats` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `job_id` int(11) NOT NULL,
  `created_at` datetime NOT NULL,
  `account_id` int(11) DEFAULT NULL,
  `country` varchar(2) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_D05BC6799FDS15210` (`job_id`),
  KEY `FK_YTGBC67994591257` (`account_id`),
  KEY `jobs_view_stats_created_at_id_index` (`created_at`,`id`),
  CONSTRAINT `FK_YTGBC67994591257` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE SET NULL,
  CONSTRAINT `job_views_jobs_id_fk` FOREIGN KEY (`job_id`) REFERENCES `jobs` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=79976587 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='New jobs views system'

这是查询:

SELECT COUNT(id) as view, job_id
from jobs_view_stats
WHERE jobs_view_stats.created_at between '2022-11-01 00:00:00' AND '2022-11-30 23:59:59'
GROUP BY jobs_view_stats.job_id

执行计划:

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE jobs_view_stats null range IDX_D05BC6799FDS15210,jobs_view_stats_created_at_id_index jobs_view_stats_created_at_id_index 5 null 1584610 100 Using index condition; Using MRR; Using temporary; Using filesort

此查询需要 4m 才能完成我想减少它以花费最短的时间。

【问题讨论】:

标签: mysql


【解决方案1】:

在您的执行计划中,您将返回 1584610 行,然后按这些行进行分组,然后使用临时表进行排序和分组(慢)。

jobs_view_stats_created_at_id_index 还包含“id”,这会使键基数过大,可以尝试将 job_id 添加到键中,因为这是您分组的依据。

我认为主要问题是您的 where 子句返回超过 150 万行,这些行必须全部加载到临时表中,然后重新完整读取以进行分组。

你需要咬掉更小的块。

我将假设您正在使用一种编程语言来调用数据库调用(如 PHP),如果是这样您可以尝试

SELECT DISTINCT job_id
from jobs_view_stats
WHERE jobs_view_stats.created_at between '2022-11-01 00:00:00' AND '2022-11-30 23:59:59'

然后当你有 job_id 列表时

循环遍历第一个结果的较小查询

SELECT count(*) FROM jobs_view_stats where job_id = *theid*

或者如果有很多不同的 job_id 对它们进行批处理

SELECT count(*) FROM jobs_view_stats where job_id IN('id1', id2, id3....)

对于纯 MySQL 解决方案,我将创建一个包含所有 job_id 的临时内存表作为内存表,使用

INSERT INTO 'temptable' SELECT DISTINCT job_id
    from jobs_view_stats
    WHERE jobs_view_stats.created_at between '2022-11-01 00:00:00' AND '2022-11-30 23:59:59'

然后

SELECT count(*) FROM jobs_view_stats where job_id = (SELECT job_id from `temptable`)

这一切都完全未经测试,所以可能是拼写错误。

【讨论】:

    猜你喜欢
    • 2023-01-28
    • 2011-07-07
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2016-07-23
    • 2016-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多