8.2.1.15 ORDER BY Optimization  ORDER BY 优化


在一些情况下, MySQL 可以使用一个索引来满足一个ORDER BY 子句不需要做额外的排序


index 可以用于即使ORDER BY 不精确的匹配index,

只要所有未使用的索引的部分和所有额外的ORDER BY 列是WHERE 子句中的常量。

下面的查询使用index来解决ORDER BY 部分:

SELECT * FROM t1
  ORDER BY key_part1,key_part2,... ;

SELECT * FROM t1
  WHERE key_part1 = constant
  ORDER BY key_part2;

SELECT * FROM t1
  ORDER BY key_part1 DESC, key_part2 DESC;

SELECT * FROM t1
  WHERE key_part1 = 1
  ORDER BY key_part1 DESC, key_part2 DESC;

SELECT * FROM t1
  WHERE key_part1 > constant
  ORDER BY key_part1 ASC;

SELECT * FROM t1
  WHERE key_part1 < constant
  ORDER BY key_part1 DESC;

SELECT * FROM t1
  WHERE key_part1 = constant1 AND key_part2 > constant2
  ORDER BY key_part2;


在一些情况下, MySQL 不能使用indexes来解决ORDER BY,尽管它仍旧使用索引来找到匹配的记录。

例如如下:

1. 查看使用ORDER BY 在不同的索引上:

SELECT * FROM t1 ORDER BY key1, key2;

2.查询使用ORDER BY 在一个索引非连续的部分


SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2;

3.查询混合ASC和DESC

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;

4.index 用于获取记录不同于一个用于ORDER BY 

SELECT * FROM t1 WHERE key2=constant ORDER BY key1;


5.查询使用ORDER BY 使用一个表达式 包含条件除index列以外

SELECT * FROM t1 ORDER BY ABS(key);
SELECT * FROM t1 ORDER BY -key;



6.查询关联很多表, 列在ORDER BY 不是所有从第一个非常量表来检索记录


7.查询有不同的ORDER BY 和GROUP BY 表达式

8. 有一个索引只有前缀列命名在ORDER BY 子句,在这种情况下,

index 不能被完全使用来解决排序顺序。

如果 如果 只有一个CHAR(20) 的前10个字节被索引,


一个索引的可利用性对于排序可能是有影响的通过使用列的别名,假设 列t1.a是索引的。

在这种情况下, 列的名字在选择的list 是a.it 它指向t1.a  索引可以被使用


SELECT a FROM t1 ORDER BY a;


在这个语句, 列的名字在select list 是a, 但是它是alias的名字。它指向ABS(a),

因此指向a 在ORDER BY,索引不能被使用

SELECT ABS(a) AS a FROM t1 ORDER BY a;


在下面的语句,ORDER BY 指向一个名字 不是列的名字在SELECT 列表。

但是有一个列在t1叫做a,因此 ORDER BY 使用 索引可以被使用(排序顺序可能是不同的)

SELECT ABS(a) AS b FROM t1 ORDER BY a;



默认, MYSQL 排序所有的GROUP BY col1,col2 查询如果你指定ORDER BY col1,col2,

如果你包含一个显示的ORDER BY 子句 包含相同的列集合


注意:

如果一个查询包含 GROUP BY 但是你需要避免 排序结果的开销,你可以指定排序通过ORDER BY NULL

INSERT INTO foo
SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;


使用EXPLAIN SELECT ... ORDER BY,你可以检查是否 MySQL 使用索引来查询。

它不能?你使用filesort 在额外的列

mysql> explain select * from Client order by sn
    -> ;
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+
|  1 | SIMPLE      | Client | index | NULL          | PRIMARY | 4       | NULL | 5884 | NULL  |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------+
1 row in set (0.03 sec)

mysql> explain select * from Client order by status;
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | Client | ALL  | NULL          | NULL | NULL    | NULL | 5884 | Using filesort |
+----+-------------+--------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)


MySQL 有2种 filesort 算法用于排序和检索记录。

原始方法只使用ORDER BY 列,

修改方法不只是使用ORDER BY 列, 而是查询涉及的所有列



优化器选择文件排序算法使用,它通常使用修改算法除了当BLOB或者TEXT 列被设计,


在这种情况下它会使用原始的算法。对于两种算法, sort buffer size 是sort_buffer_size 系统变量控制的

[root@zjzc01 binlog]# cat /etc/my.cnf | grep sort
sort_buffer_size=1M


原始的filesort 算法如下:

1. 读取所有的记录根据索引或者通过表扫描,跳过不匹配的记录

2. 对于每条记录, 存储在sort buffer 一个元组有一对值组成(sort key 值和row ID)

3.如果所有的pairs 到放到sort buffer, 不需要创建临时文件。否则,

当sort buffer 变满时, 运行一个qsort(quicksort) 在内存里 写到一个临时文件,保存一个指向排序后的块

4.重复前面的步骤, 知道所有行被读取

5.做一个 合并到  MERGEBUFF (7) 区域到一个block在另外的temporary 文件。


重复知道所有的blocks 从第一个文件是在第2个文件

6.重复下面知道有较少相比 MERGEBUFF2 (15)块留下

7. 在最后的合并,只有row ID (值对的最后部分)是写入到结果文件

8. 读按顺序存储的记录使用row IDs 在结果文件。

优化这个, 读取row IDs的大的块,排序它们,使用它们读取排序的记录到row buffer.

row buffer size 是 read_rnd_buffer_size 系统变量设置


一个问题是它会读取量词, 一次在where 条件评估时,

另外一次在排序值对后。

修改的filesort 算法采用一个优化的来避免读2次, 它记录仪sort key 值,代替row ID

它记录查询涉及的列, 修改后的文件排序算法工作如下:

1.读取匹配WHERE 子句的记录

2.对于每个行, 存储在sort buffer 一个元组 有sort key值和查询涉及的列组成

3.当排序区变满后, sort 元组通过sort key 值在内存里写到一个临时文件

mysql> show variables like '%sort%';
+--------------------------------+---------------------+
| Variable_name                  | Value               |
+--------------------------------+---------------------+
| innodb_disable_sort_file_cache | OFF                 |
| innodb_ft_sort_pll_degree      | 2                   |
| innodb_sort_buffer_size        | 1048576             |
| max_length_for_sort_data       | 1024                |
| max_sort_length                | 1024                |
| myisam_max_sort_file_size      | 9223372036853727232 |
| myisam_sort_buffer_size        | 8388608             |
| sort_buffer_size               | 1048576             |
+--------------------------------+---------------------+
8 rows in set (0.00 sec)