大表上的慢 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的主要内容,如果未能解决你的问题,请参考以下文章
在 Django 中的大表上的内存效率(常量)和速度优化迭代