sql 加入顺序显着改变性能
sql join order dramatically changes performance
所以我在 mysql 中有两个表:articles 和 articles_rubrics,都有 ~20.000 行
文章有多个列,但其 article_id 已编入索引。
articles_rubrics 只有两个列:article_id 和 rubrics_id 并且两者都是单独索引的,最重要的是这两个列的联合索引。
我的问题是,当我 select 来自这些表的数据与连接时,顺序非常重要,这对我来说是个问题,我不明白其中的原因:
SELECT article_id,rubric_id FROM articles
LEFT JOIN articles_rubrics USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
并解释说(articles_rubrics)这个:
time: 0.312 s
key_len: 1
ref: const
rows: 7352
extra: Using where; Using temporary; Using filesort
但是当我调换它的顺序时:
SELECT article_id,rubric_id FROM articles_rubrics
LEFT JOIN articles USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
并解释说(articles_rubrics)这个:
time: 0.001 s
key_len:9
ref: NULL
rows: 28
extra: Using where; Using index
所以我有两个表,这使得他们的查询进行了约 300 次 slower/faster。这怎么可能?
PS:对于这个例子,我已经大大简化了我的现实世界问题,但我偶然发现了这个,因为我的
SELECT * FROM articles [LEFT JOIN for 5 other tables]
需要 1.5 秒,当我实际添加其他连接时,执行时间变为 0.006 秒。
显示索引:
show index from articles;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
articles 0 PRIMARY 1 article_id A 20043 NULL NULL BTREE
articles 1 article_url_title 1 article_url_title A 10021 NULL NULL BTREE
articles 1 FULLTEXT 1 article_title NULL 1 NULL NULL FULLTEXT
articles 1 FULLTEXT 2 article_content NULL 1 NULL NULL FULLTEXT
show index from articles_rubrics;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type
articles_rubrics 0 PRIMARY 1 article_id A NULL NULL NULL BTREE
articles_rubrics 0 PRIMARY 2 rubric_id A 20814 NULL NULL BTREE
articles_rubrics 1 rubric_id 1 rubric_id A 17 NULL NULL BTREE
articles_rubrics 1 article_id 1 article_id A 20814 NULL NULL BTREE
SELECT article_id,rubric_id
FROM articles
LEFT JOIN articles_rubrics USING(article_id)
WHERE rubric_id=1 <<<<<<<<<<<<<<<<<<<<<<<<<<< problem here
ORDER BY article_id DESC
LIMIT 10;
通过坚持从此查询返回的每一行都具有 rubric_id=1,您已经消除了 2 table 之间不匹配的任何行,因此没有必要使用左连接
SELECT a.article_id, ar.rubric_id
FROM articles AS a
INNER JOIN articles_rubrics AS ar ON a.article_id = ar.article_id
WHERE ar.rubric_id = 1
ORDER BY a.article_id DESC
LIMIT 10;
您需要在每个引用中使用 table 或 table 别名。
数据库的连接操作是昂贵的过程。最好使用简单的 SELECT 嵌套。制作一个列表来存储数据,然后使用列表中的项目进行下一次查询。
这两个查询运行相同,唯一不同的是在两者中使用articles_rubrics中的article_id。
-- SELECT article_id,rubric_id FROM articles -- would be slow here
SELECT ar.article_id,ar.rubric_id FROM articles
JOIN articles_rubrics ar USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
SELECT ar.article_id,ar.rubric_id FROM articles_rubrics ar
JOIN articles USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
如果我强制 sql 服务器在结果中使用 articles_rubrics table,他会正确地决定实际上不需要这些文章。但是,服务器不会自动执行此操作,即使 article_id 用作密钥也是如此。
我仍然不完全理解为什么会这样(或者优化算法实际上是如何工作的),因为在这两种情况下,where rubric_id=1
进入 articles_rubrics
table 并且在在这两种情况下,所选列已经存在(在这两种情况下,join articles
的存在又是 运行)。
但是,由于某些原因,在第一个示例中,服务器决定先加载所有文章,然后才检查每个文章的 rubric_id
。
所以我在 mysql 中有两个表:articles 和 articles_rubrics,都有 ~20.000 行
文章有多个列,但其 article_id 已编入索引。
articles_rubrics 只有两个列:article_id 和 rubrics_id 并且两者都是单独索引的,最重要的是这两个列的联合索引。
我的问题是,当我 select 来自这些表的数据与连接时,顺序非常重要,这对我来说是个问题,我不明白其中的原因:
SELECT article_id,rubric_id FROM articles
LEFT JOIN articles_rubrics USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
并解释说(articles_rubrics)这个:
time: 0.312 s
key_len: 1
ref: const
rows: 7352
extra: Using where; Using temporary; Using filesort
但是当我调换它的顺序时:
SELECT article_id,rubric_id FROM articles_rubrics
LEFT JOIN articles USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
并解释说(articles_rubrics)这个:
time: 0.001 s
key_len:9
ref: NULL
rows: 28
extra: Using where; Using index
所以我有两个表,这使得他们的查询进行了约 300 次 slower/faster。这怎么可能?
PS:对于这个例子,我已经大大简化了我的现实世界问题,但我偶然发现了这个,因为我的
SELECT * FROM articles [LEFT JOIN for 5 other tables]
需要 1.5 秒,当我实际添加其他连接时,执行时间变为 0.006 秒。
显示索引:
show index from articles;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
articles 0 PRIMARY 1 article_id A 20043 NULL NULL BTREE
articles 1 article_url_title 1 article_url_title A 10021 NULL NULL BTREE
articles 1 FULLTEXT 1 article_title NULL 1 NULL NULL FULLTEXT
articles 1 FULLTEXT 2 article_content NULL 1 NULL NULL FULLTEXT
show index from articles_rubrics;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type
articles_rubrics 0 PRIMARY 1 article_id A NULL NULL NULL BTREE
articles_rubrics 0 PRIMARY 2 rubric_id A 20814 NULL NULL BTREE
articles_rubrics 1 rubric_id 1 rubric_id A 17 NULL NULL BTREE
articles_rubrics 1 article_id 1 article_id A 20814 NULL NULL BTREE
SELECT article_id,rubric_id
FROM articles
LEFT JOIN articles_rubrics USING(article_id)
WHERE rubric_id=1 <<<<<<<<<<<<<<<<<<<<<<<<<<< problem here
ORDER BY article_id DESC
LIMIT 10;
通过坚持从此查询返回的每一行都具有 rubric_id=1,您已经消除了 2 table 之间不匹配的任何行,因此没有必要使用左连接
SELECT a.article_id, ar.rubric_id
FROM articles AS a
INNER JOIN articles_rubrics AS ar ON a.article_id = ar.article_id
WHERE ar.rubric_id = 1
ORDER BY a.article_id DESC
LIMIT 10;
您需要在每个引用中使用 table 或 table 别名。
数据库的连接操作是昂贵的过程。最好使用简单的 SELECT 嵌套。制作一个列表来存储数据,然后使用列表中的项目进行下一次查询。
这两个查询运行相同,唯一不同的是在两者中使用articles_rubrics中的article_id。
-- SELECT article_id,rubric_id FROM articles -- would be slow here
SELECT ar.article_id,ar.rubric_id FROM articles
JOIN articles_rubrics ar USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
SELECT ar.article_id,ar.rubric_id FROM articles_rubrics ar
JOIN articles USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;
如果我强制 sql 服务器在结果中使用 articles_rubrics table,他会正确地决定实际上不需要这些文章。但是,服务器不会自动执行此操作,即使 article_id 用作密钥也是如此。
我仍然不完全理解为什么会这样(或者优化算法实际上是如何工作的),因为在这两种情况下,where rubric_id=1
进入 articles_rubrics
table 并且在在这两种情况下,所选列已经存在(在这两种情况下,join articles
的存在又是 运行)。
但是,由于某些原因,在第一个示例中,服务器决定先加载所有文章,然后才检查每个文章的 rubric_id
。