【发布时间】:2011-08-16 06:38:04
【问题描述】:
我遇到了一个 SQL 查询问题,即当表的记录超过 10 万条时“失败”(耗时太长)。这应该不是问题,我认为我已经涵盖了它,因为它适用于 50k 记录。
我会尽量简洁明了,所以我将从查询开始:
SELECT
V.id
FROM
videos V
LEFT JOIN videos_categories VC ON V.id = VC.video_id
LEFT JOIN categories C ON VC.category_id = C.id
LEFT JOIN users U ON V.user_id = U.id -- irrelevant table. Don't pay attention
WHERE
V.status = 1
AND (C.status = 1 OR C.id IS NULL)
AND (U.status = 1 OR U.id IS NULL) -- irrelevant
GROUP BY V.id
ORDER BY V.id DESC
LIMIT 0, 12
---------------------------------------------
**Query took 10.8771 sec** (very bad! this would take 0.1 max)
我正在使用所有 LEFT JOIN,因为如果类别不存在,我不想限制结果。这意味着还会返回未指定类别的视频。
tables结构的思路如下:
- 'videos'(id PK,+无关字段)表包含 +100k 记录。
- 'videos_categories' (video_id INDEX,category_id INDEX) +600k 记录 - 每个视频多行
- 'categories'(id PK,+ 无关字段)
- 'users'(id PK,+无关字段)没问题。
---- 7 月 3 日更新 ----
表格结构:
CREATE TABLE `videos` ( -- Holding +100k records
`id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) unsigned NOT NULL default '0', -- irrelevant for this example
`status` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `status` (`status`)
-- ... -- Irrelevant Keys
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC AUTO_INCREMENT=113339 ;
CREATE TABLE `videos_categories` ( -- Holding +600k records (several categories per video)
`video_id` int(10) unsigned NOT NULL default '0',
`category_id` int(10) unsigned NOT NULL default '0',
KEY `video_id` (`video_id`),
KEY `category_id` (`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Categories 表有一个 PK id 和不相关的字段。它拥有80条记录。 用户表完全不相关,可以忽略。很抱歉在第一个实例中添加它。
---- 7 月 3 日更新结束----
这是查询的解释结果
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE V range status status 1 NULL 112895 Using where; Using temporary; Using filesort
1 SIMPLE VC ref video_id video_id 4 V.id 2
1 SIMPLE C eq_ref PRIMARY PRIMARY 4 VC.category_id 1 Using where
1 SIMPLE U eq_ref PRIMARY PRIMARY 4 V.user_id 1 Using where
我认为问题在于 SQL 引擎是“使用文件排序”,因为它使用的是“状态”索引,而不是 V.id。
此外,它是“使用临时”,因为引擎必须写入的记录数和内存表不够。
更新(7 月 3 日): 经过一些测试,我得出的结论是,这个特定查询的问题是使用 V.status 作为索引根本没有帮助(98%的视频状态=1)
- 问题 1:为什么优化器不使用 V.id 作为索引进行排序和过滤?为此,我使用了 ORDER BY 和 LIMIT。
重要提示:如果我从 WHERE 子句中删除 'V.status=1' 过滤器,查询需要 0.01 秒,它使用 V.id (PRIMARY) 作为索引,解决它全部。
- 问题2:有没有办法在mysql
---- 7 月 3 日更新说明结束----
总结一下
假设我已涵盖所有相关索引:如何优化查询,它需要 0.1 秒?
我很确定这对高级 SQL 管理员和程序员来说是一个很好的挑战。
【问题讨论】:
标签: mysql optimization