优化MySQL按限制安全选择不同顺序

优化MySQL按限制安全选择不同顺序,mysql,database-performance,sqlperformance,Mysql,Database Performance,Sqlperformance,我有一个有问题的查询,我知道如何写得更快,但从技术上讲,SQL是无效的,它不能保证在将来正常工作 原始的慢速查询如下所示: SELECT sql_no_cache DISTINCT r.field_1 value FROM table_middle m JOIN table_right r on r.id = m.id WHERE ((r.field_1) IS NOT NULL) AND (m.kind IN ('partial')) ORDER BY r.field_1 LIMIT 2

我有一个有问题的查询,我知道如何写得更快,但从技术上讲,SQL是无效的,它不能保证在将来正常工作

原始的慢速查询如下所示:

SELECT sql_no_cache DISTINCT r.field_1 value
FROM table_middle m
JOIN table_right r on r.id = m.id
WHERE ((r.field_1) IS NOT NULL) 
AND (m.kind IN ('partial')) 
ORDER BY r.field_1 
LIMIT 26
这大约需要37秒。解释输出:

+----+-------------+-------+--------+-----------------------+---------------+---------+---------+-----------------------------------------------------------+
| id | select_type | table | type   | possible_keys         | key           | key_len | rows    | Extra                                                     |
+----+-------------+-------+--------+-----------------------+---------------+---------+---------+-----------------------------------------------------------+
|  1 | SIMPLE      | r     | range  | PRIMARY,index_field_1 | index_field_1 | 9       | 1544595 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | m     | eq_ref | PRIMARY,index_kind    | PRIMARY       | 4       |       1 | Using where; Distinct                                     |
+----+-------------+-------+--------+-----------------------+---------------+---------+---------+-----------------------------------------------------------+
+----+-------------+------------+--------+-----------------------+------------+---------+---------+-----------------------------------------------------------+
| id | select_type | table      | type   | possible_keys         | key        | key_len | rows    | Extra                                                     |
+----+-------------+------------+--------+-----------------------+------------+---------+---------+-----------------------------------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                  | NULL       | NULL    | 1346348 | Using temporary                                           |
|  2 | DERIVED     | m          | ref    | PRIMARY,index_kind    | index_kind | 99      | 1539558 | Using where; Using index; Using temporary; Using filesort |
|  2 | DERIVED     | r          | eq_ref | PRIMARY,index_field_1 | PRIMARY    | 4       |       1 | Using where                                               |
+----+-------------+------------+--------+-----------------------+------------+---------+---------+-----------------------------------------------------------+
更快的版本看起来像这样;
order by
子句被下推到一个子查询中,该子查询在上被连接,并依次受到distinct的限制:

SELECT sql_no_cache DISTINCT value 
FROM (
  SELECT r.field_1 value
  FROM table_middle m
  JOIN table_right r ON r.id = m.id
  WHERE ((r.field_1) IS NOT NULL) 
  AND (m.kind IN ('partial')) 
  ORDER BY r.field_1 
) t
LIMIT 26
这大约需要2.7秒。解释输出:

+----+-------------+-------+--------+-----------------------+---------------+---------+---------+-----------------------------------------------------------+
| id | select_type | table | type   | possible_keys         | key           | key_len | rows    | Extra                                                     |
+----+-------------+-------+--------+-----------------------+---------------+---------+---------+-----------------------------------------------------------+
|  1 | SIMPLE      | r     | range  | PRIMARY,index_field_1 | index_field_1 | 9       | 1544595 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | m     | eq_ref | PRIMARY,index_kind    | PRIMARY       | 4       |       1 | Using where; Distinct                                     |
+----+-------------+-------+--------+-----------------------+---------------+---------+---------+-----------------------------------------------------------+
+----+-------------+------------+--------+-----------------------+------------+---------+---------+-----------------------------------------------------------+
| id | select_type | table      | type   | possible_keys         | key        | key_len | rows    | Extra                                                     |
+----+-------------+------------+--------+-----------------------+------------+---------+---------+-----------------------------------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                  | NULL       | NULL    | 1346348 | Using temporary                                           |
|  2 | DERIVED     | m          | ref    | PRIMARY,index_kind    | index_kind | 99      | 1539558 | Using where; Using index; Using temporary; Using filesort |
|  2 | DERIVED     | r          | eq_ref | PRIMARY,index_field_1 | PRIMARY    | 4       |       1 | Using where                                               |
+----+-------------+------------+--------+-----------------------+------------+---------+---------+-----------------------------------------------------------+
索引对于再现效果并不重要。上面的脚本未经测试;这是一个近似于我手动重现问题时使用的pry会话


m.kind in('partial')
替换为
m.field_1>0
或类似的非常正确的内容。观察两种不同技术在性能上的巨大差异,以及如何保留排序语义(使用MySQL 5.5进行测试)。当然,语义的不可靠性正是我提出这个问题的原因。

请提供
showcreatetable
。如果没有这一点,我想这些都是缺失的,可能是有用的:

m:  (kind, id)
r:  (field_1, id)

您可以通过

关闭MariaDB忽略子查询的
顺序,但是。。。第二个查询是否返回正确的结果?我无法想象它会这样。在MariaDB或MySQL中,除非提供了限制,否则将忽略ORDER BY。您的第二个查询只会运行得更快,因为数据库不必对数百万条记录进行排序。哦,但确实如此,@jnevil-您应该试试(MySQL 5.5)!对300万条记录进行排序不需要37秒,这就是奇怪的极端低效。与运行第一个查询相比,创建临时表、插入临时表以及从临时表中进行选择的速度更快。但我并不急于将这种工作流引入这些动态筛选查询。@jnevil我添加了一个脚本,以便您可以自己查看,如果您愿意的话。
SELECT FROM_middle m JOIN table_right r ON r.id=m.id,其中((r.field_1)不为NULL)和(m.kind IN('partial')的性能如何ORDER BY r.field_1
?@JNevill我已经不在我的工作机器上了,所以我不能直接告诉你可比的数字,但我在我的家庭机器上的一个虚拟机上有一个类似的例子(我使用上面的脚本设置的那个)。选择整个数百万表只需要I/O一段时间;如果我在末尾粘贴一个
limit 10 offset 2990000
,那么我们就在第二个查询区域(即快速、比较)。在一个查询中,distinct+order by+limit组合的速度很慢。FWIW甚至GNU sort可以在一个VM中在不到4秒钟的时间内完成300万行。请参阅我添加的脚本-这些create table语句足以显示问题。我使用的是MySQL 5.5,不是MariaDB。索引不能更改;任何字段都可以按排序,字段的任何组合都可以按筛选。这些表不依赖索引。如果他们能帮上忙,那就好了,但他们不希望这样。应用程序中有1000个这样的表,每个表最多有100万或两条记录,最多有100列(不是全部索引)。我专门致力于优化最坏的情况;不带索引的排序和过滤器是可以接受的,只要它们只需要一两秒钟;将innodb_buffer_pool_大小设置为可用ram的70%。缩小数据类型以最小化磁盘占用空间。有很多快速内核。删除索引,因为您正在针对无法使用它们的情况进行优化。