【问题标题】:MySQL: Very slow query on a huge table with order by timestamp (despite of having indexes)…MySQL:在一个按时间戳排序的大表上查询非常慢(尽管有索引)......
【发布时间】:2018-12-12 04:33:35
【问题描述】:

我有一个包含数百万条记录的 sync_log_lines 表。

CREATE TABLE `sync_log_lines` (
  `uuid` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
  `sync_log_uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `exception_time` timestamp NULL DEFAULT NULL,
  `exception_message` mediumtext COLLATE utf8mb4_unicode_ci,
  `exception_file` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `exception_line` int(10) unsigned DEFAULT NULL,
  `failure_reason` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `csv_file_row_count` int(10) unsigned DEFAULT NULL,
  `csv_file_row_sequence` int(10) unsigned DEFAULT NULL,
  `csv_file_row_content` mediumtext COLLATE utf8mb4_unicode_ci,
  `csv_file_source` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`uuid`),
  KEY `sync_log_lines_sync_log_uuid_index` (`sync_log_uuid`),
  KEY `sync_log_lines_exception_time_index` (`exception_time`),
  CONSTRAINT `sync_log_lines_sync_log_uuid_foreign` FOREIGN KEY (`sync_log_uuid`) REFERENCES `sync_logs` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

单个“同步”操作可能会在此表中插入 150k 条记录。 sync_log_lines 中的每条记录都是单个 CSV 行,无法在其专用表中插入或更新。

所以我使用的以下查询可能会迅速升级。

select `uuid`, `sync_log_uuid`, `exception_time`, `exception_message`, `failure_reason`, `csv_file_row_count`, `csv_file_row_sequence`, `csv_file_row_content` 
from `sync_log_lines` 
where `sync_log_uuid` = '56b0a3b1-dab4-4343-9f9b-a2a8f075c21a' 
order by `exception_time` desc 
limit 100 offset 6000;

总共有大约 15 万条记录,其中 sync_log_uuid = 56b0a3b1-dab4-4343-9f9b-a2a8f075c21a。如果没有order by,则需要几毫秒才能给我第一条100 记录。

当我如上所示添加 order by 时,它会减慢到 30-45 秒。

我知道,我知道。我做了一项研究,我完全理解:

当我在没有 ORDER BY - LIMIT 100 的情况下运行查询时,它可以完美运行 - 它 在前 100 条记录后停止查询,任何 100 条记录

但是

当我添加 ORDER BY 时,MySQL 首先将 所有记录 发送到临时 表,然后对其进行排序,然后返回给我100条正确记录

这绝对是有道理的。在庞大的数据集上,它按预期工作。但是我已经到了不知道如何优化它的地步。我无法缩小日期 (exception_time),因为所有 log linesUUID 都在 2 小时内插入 - 这是大约。同步时间。

我的查询用作分页的一部分,有时用户必须查看此特定同步的页面 212 (!)

还有改进的余地吗?综合指数?还有什么?

【问题讨论】:

  • pagination,或许以exception_time为起点WHERE exception_time > {prev value} LIMIT 100

标签: mysql sql


【解决方案1】:

在 sync_log_uuid 和 exception_time 上创建 1 个索引。

CREATE INDEX my_index ON sync_log_lines (sync_log_uuid, exception_time);

如果不使用 ORDER BY,对 MySQL 来说查找前 100 条记录很快,因为它只能返回找到的前 100 条记录。

如果您按 exception_time 排序,MySQL 必须读取 sync_log_uuid = '56b0a3b1-dab4-4343-9f9b-a2a8f075c21a' 的所有记录以确定哪些是前 100 个。

【讨论】:

    【解决方案2】:

    尽量使用复合索引也称为多列索引。它将提供更好的性能。正如上面提到的创建索引的答案 http://www.mysqltutorial.org/mysql-index/mysql-composite-index/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多