大表上的慢 MySQL SELECT

Posted

技术标签:

【中文标题】大表上的慢 MySQL SELECT【英文标题】:Slow MySQL SELECT on large table 【发布时间】:2014-08-22 10:22:04 【问题描述】:

我有一个表格,用于在 2 周内每 15 分钟存储约 35,000 件商品的价格。大约转换为表格中的大约 3500 万行。我正在尝试执行最简单的查询:

SELECT buy_price, sell_price, created_at FROM price_archive WHERE item_id = X

该查询的第一次未缓存运行大约需要 4-7 秒才能返回约 1300 行(每个项目)。对于数据库来说如此微不足道的事情,这似乎慢得离谱,尤其是考虑到item_id 列上有索引。

该表每 15 分钟插入 35k 行,并且每天运行一个任务以删除 created_at 上进行分区以删除旧数据是否会更好?

1306 rows in set (8.32 sec)

mysql> explain select * from price_archives where item_id = 743;
+----+-------------+----------------+------+---------------------------------+---------------------------------+---------+-------+------+-------------+
| id | select_type | table          | type | possible_keys                   | key                             | key_len | ref   | rows | Extra       |
+----+-------------+----------------+------+---------------------------------+---------------------------------+---------+-------+------+-------------+
|  1 | SIMPLE      | price_archives | ref  | index_price_archives_on_item_id | index_price_archives_on_item_id | 5       | const | 1305 | Using where |
+----+-------------+----------------+------+---------------------------------+---------------------------------+---------+-------+------+-------------+

【问题讨论】:

我建议您运行 OPTIMIZE 命令,然后运行上面的 SELECT 查询,看看它是否运行得更快。如果是,那么您知道碎片整理导致它变慢。此外,您当前使用的索引可能不是针对此特定查询最优化的索引。尝试使用 item_id、buyprice、sellprice、created_at 创建索引。 我不会运行优化并将表锁定到新的插入,而是克隆表,然后尝试相同的查询。 【参考方案1】:

这是您的查询:

SELECT buy_price, sell_price, created_at
FROM price_archive
WHERE item_id = X;

此查询的最佳索引是复合索引:price_archive(item_id, buy_price, sell_price, created_at)。这是一个可以满足查询的“覆盖”索引。不过,它有一个缺点。此索引可能会减慢您在表中执行的插入操作。每小时 140k 行是很多数据,但是维护这个索引应该不会那么糟糕。

您在数据库中面临着一个不常见的挑战。您的查询的问题是返回的 1300 左右行都在不同的数据页上。很可能,该表不适合您机器上的内存,因此这会导致大约 1300 次访问磁盘上的文件。这就解释了为什么您会看到几秒钟的延迟时间。

另一个解决方案是确保数据表本身适合内存。虽然第一次未缓存的查询会花费一些时间,但后续查询应该很快。

【讨论】:

如果这些字段是表本身的全部内容,覆盖索引是否会占用与表在磁盘上占用的内存一样多的 RAM(即 2GB)? @Supremacy 。 . .它实际上会占用更多的空间。但是数据的结构将完全按照查询需要的方式进行。优点是给定item_id 的所有数据都将位于索引中,因此无需转到许多不同的数据页面来收集信息。 嗯,我明白了。我真的负担不起这么大的索引,所以我决定将旧数据聚合到较低的分辨率。【参考方案2】:

您可以为表格列添加索引。

一百万多条记录出现此问题,时间从 50 秒缩短到 10 秒。

更新表的 SQL 查询:

ALTER TABLE price_archives ADD INDEX (item_id);
ALTER TABLE price_archives ADD INDEX (buy_price);
ALTER TABLE price_archives ADD INDEX (sell_price);
ALTER TABLE price_archives ADD INDEX (created_at);

【讨论】:

以上是关于大表上的慢 MySQL SELECT的主要内容,如果未能解决你的问题,请参考以下文章

如何在低规格系统上的大表上提高 MySQL 性能?

在 Django 中的大表上的内存效率(常量)和速度优化迭代

bigquery - 小表上的慢查询

通过大表上的操作加速组

MySQL DROP TABLE操作以及 DROP 大表时的注意事项

Oracle - 未使用大表上的索引