【MySQL】再说order by 优化

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 一 前言     为什么是再说呢?因为前面已经写过一篇blog,介绍order by 的基本原理以及优化。如果觉得对order by原理了解不透彻可以参考其他同行的文章《MySQL排序内部原理探秘》.本文是基于官网文档的二刷(基本翻译+测试验证),看完本文可以了解到什么样的select + order by 语句可以使用索引,什么样的不能利用到索引排序。
一 前言 
   为什么是再说呢?因为前面已经写过 一篇blog,介绍order by 的基本原理以及优化。如果觉得对order by原理了解不透彻可以参考其他同行的文章《 MySQL排序内部原理探秘》.本文是基于官网文档的二刷(基本翻译+测试验证),看完本文可以了解到什么样的select + order by 语句可以使用索引,什么样的不能利用到索引排序。
二 分析  
2.1 官方标准介绍
对于select  order by语句如何能够利用到索引,官方表述如下:
  1. "The index can also be used even if the ORDER BY does not match the index exactly, as long as all of the unused portions of the index and all the extra ORDER BY columns are constants in the WHERE clause."
翻译一下就是
即使ORDER BY语句不能精确匹配(组合)索引列也能使用索引,只要WHERE条件中的所有未使用的索引部分和所有额外的ORDER BY列为常数就行。
如何理解这句话呢?我们通过具体用例来解释。
2.2 准备工作
  1. CREATE TABLE `tx` (
  2.   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  3.   `shid` int(11) NOT NULL COMMENT '商店ID',
  4.   `gid` int(11) NOT NULL COMMENT '物品ID',
  5.   `type` tinyint(1) NOT NULL COMMENT '支付方式',
  6.   `price` int(10) NOT NULL COMMENT '物品价格',
  7.   `comment` varchar(200) NOT NULL COMMENT '备注',
  8.   PRIMARY KEY (`id`),
  9.   UNIQUE KEY `uniq_shid_gid` (`shid`,`gid`),
  10.   KEY `idx_price` (`price`),
  11.   KEY `idx_type` (`type`)
  12. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ;
  13. INSERT INTO `tx` (`shid`, `gid`, `type`, `price`, `comment`) VALUES (6, 2, 0, '399', '2'),(6, 5, 0, '288', '2'),(6, 11, 0, '10', '2');
  14. (1, 1, 0, '10', 'sd'),
  15. (2, 55, 0, '210', 'sa'),
  16. (2, 33, 1, '999', 'a'),
  17. (3, 17, 0, '198', 'b'),
  18. (3, 22, 1, '800', 'e'),
  19. (4, 12, 0, '120', 'f'),
  20. (4, 73, 0, '250', 'd'),
  21. (5, 61, 0, '10', 'c'),
  22. (6, 1, 0, '210', '2'),
  23. (7, 9, 1, '999', '44'),
  24. (7, 2, 0, '198', '45'),
  25. (8, 3, 1, '800', 'rt'),
  26. (9, 4, 0, '120', 'pr'),
  27. (9, 6, 0, '250', 'x'),
  28. (10, 8, 0, '10', 'w'),
  29. (12, 9, 0, '210', 'w'),
  30. (12, 10, 1, '999', 'q'),
  31. (13, 11, 0, '198', ''),
  32. (13, 12, 1, '800', ''),
  33. (14, 13, 0, '120', ''),
  34. (14, 19, 0, '250', '');
  35. CREATE TABLE `goods_type` (
  36.   `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  37.   `type` int NOT NULL COMMENT '类型',
  38.   `name` varchar(20) NOT NULL COMMENT '名称',
  39.   PRIMARY KEY (`id`)
  40. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  41. INSERT INTO `goods_type` (`id`, `type`, `name`) VALUES
  42. (1, 1, 'hw手机'),
  43. (2, 0, 'xiaomi'),
  44. (3, 1, 'apple')

2.3 能够利用索引的例子分析
官方的文档 中介绍有7个例子可以使用索引进行排序。如果使用explain/desc工具查看执行计划中的extra中出现了 Using filesort则说明sql没有用到排序优化。
案例一
文档: SELECT * FROM t1 ORDER BY key_part1,key_part2,...; 
  1. test [RW] 06:03:52 >desc select * from tx order by shid,gid;
  2. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
  4. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  5. | 1  | SIMPLE      | tx    | ALL  | NULL          | NULL | NULL    | NULL | 24   | Using filesort |
  6. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  7. 1 row in set (0.00 sec)
分析:
  显然上述sql没有利用到索引排序. type=ALL Extra=Using filesort,因为where字句没有条件,优化器选择全表扫描和内存排序。
  1. test [RW] 06:04:39 >desc select gid from tx order by shid,gid;
  2. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  3. | id | select_type | table | type  | possible_keys | key         | key_len   | ref  | rows | Extra       |
  4. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  5. | 1  | SIMPLE      | tx    | index | NULL          | uniq_shid_gid | 8       | NULL | 24   | Using index |
  6. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  7. 1 row in set (0.00 sec)
  8. test [RW] 06:04:47 >desc select shid,gid from tx order by shid,gid;
  9. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  10. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra |
  11. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  12. | 1  | SIMPLE      | tx    | index | NULL          | uniq_shid_gid | 8       | NULL | 24   | Using index |
  13. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  14. 1 row in set (0.00 sec)
  15. test [RW] 06:04:54 >desc select id,shid,gid from tx order by shid,gid;
  16. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  17. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra |
  18. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  19. | 1  | SIMPLE      | tx    | index | NULL          | uniq_shid_gid | 8       | NULL | 24   | Using index |
  20. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  21. 1 row in set (0.00 sec)
分析
    从type=index,extra=Using index 可以看出当select 的字段包含在索引中时,能利用到索引排序功能,进行覆盖索引扫描。
    使用select * 则不能利用覆盖索引扫描且由于where语句没有具体条件MySQL选择了全表扫描且进行了排序操作。
案例二
  SELECT * FROM t1 WHERE key_part1 = constant ORDER BY key_part2;
使用组合索引中的一部分做等值查询 ,另一部分作为排序字段。更严谨的说法是where条件使用组合索引的左前缀等值查询,使用剩余字段进行order by排序。
  1. test [RW] 06:05:41 >desc select * from tx where shid= 2 order by gid;
  2. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref     | rows  | Extra|
  4. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  5. | 1  | SIMPLE      | tx    | ref  | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | Using where |
  6. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  7. 1 row in set (0.00 sec)
  8. test [RW] 11:30:13 >desc select * from tx where shid= 2 order by gid desc;
  9. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  10. | id | select_type | table | type | possible_keys | key | key_len | ref     | rows  | Extra|
  11. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  12. | 1  | SIMPLE      | tx    | ref  | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | Using where |
  13. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  14. 1 row in set (0.00 sec)
分析:
  where 条件字句可以基于 shid 进行索引查找 并且利用(shid,gid)中gid的有序性避免额外的排序工作. 我们基于本例解释"即使ORDER BY语句不能精确匹配(组合)索引列也能使用索引,只要WHERE条件中的所有未使用的索引部分和所有额外的ORDER BY列为常数就行。"
该语句的order by  gid 并未精确匹配到组合索引(shid,gid),where条件 shid利用了组合索引的最左前缀且为等值常量查询,对order by 而言shid就是额外的字段,没有出现在order by子句中却是组合索引的一部分。这样的条件既可以使用索引来排序。

案例三
SELECT * FROM t1  ORDER BY key_part1 DESC, key_part2 DESC;
其实和案例一 类似,只是选择了倒序。该sql不能利用索引的有序性,需要server层进行排序。
  1. test [RW] 06:06:30 >desc select * from tx order by shid desc,gid desc;
  2. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
  4. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  5. | 1  | SIMPLE      | tx    | ALL  | NULL          | NULL | NULL    | NULL | 24   | Using filesort |
  6. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  7. 1 row in set (0.00 sec)
  8. 如果select 中选择索引字段,可以利用覆盖索引扫描则可以利用索引进行排序。
  9. test [RW] 06:06:31 >desc select shid,gid from tx order by shid desc,gid desc;
  10. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  11. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra       |
  12. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  13. | 1  | SIMPLE      | tx    | index | NULL          | uniq_shid_gid | 8       | NULL | 24   | Using index |
  14. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+
  15. 1 row in set (0.00 sec)
案例四
SELECT * FROM t1 WHERE key_part1 = 1  ORDER BY key_part1 DESC, key_part2 DESC;
本例和案例二类似,只是order by 字句中包含所有的组合索引列。
  1. test [RW] 06:06:55 >desc select * from tx where shid=4 order by shid desc ,gid desc;
  2. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra       |
  4. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  5. | 1  | SIMPLE      | tx    | ref  | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | Using where |
  6. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
  7. 1 row in set (0.00 sec)
分析:
   where shid=4 可以利用shid的索引定位数据记录,select *  有不在索引里面的字段,所以回表访问组合索引列之外的数据,利用了gid索引的有序性避免了排序工作。
案例五
SELECT * FROM t1 WHERE key_part1 > constant ORDER BY key_part1 ASC;
SELECT * FROM t1 WHERE key_part1 < constant ORDER BY key_part1 DESC;
  1. test [RW] 11:40:48 >desc select * from tx where shid>5 order by shid desc ;
  2. +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
  4. +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
  5. | 1  | SIMPLE      | tx    | ALL  | uniq_shid_gid | NULL | NULL    | NULL | 24   | Using where; Using filesort |
  6. +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+
  7. 1 row in set (0.00 sec)

  8. test [RW] 11:47:25 >desc select * from tx where shid>13 order by shid desc ;
  9. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-----------------------+
  10. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                 |
  11. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-----------------------+
  12. | 1  | SIMPLE      | tx    | range | uniq_shid_gid | uniq_shid_gid | 4       | NULL | 2    | Using index condition |
  13. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-----------------------+
  14. 1 row in set (0.00 sec)
分析
  表总共24行,其中大于5的有16行,大于13的2行,导致MySQL优化器选择了不同的执行计划。这个测试说明和shid的区分度有关。
案例六
SELECT * FROM t1 WHERE key_part1 = constant1 AND key_part2 > constant2 ORDER BY key_part2;
利用组合索引前缀索引进行ref等值查询,其他字段进行范围查询,order by 非等值的字段
  1. test [RW] 06:10:41 >desc select * from tx where shid=6 and gid>1 order by gid;
  2. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-----------------------+
  3. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                 |
  4. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-----------------------+
  5. | 1  | SIMPLE      | tx    | range | uniq_shid_gid | uniq_shid_gid | 8       | NULL | 3    | Using index condition |
  6. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-----------------------+
  7. 1 row in set (0.02 sec)
分析:
    利用shid=6的进行索引查询记录到了MySQL的ICP特性,无排序操作。为啥使用ICP 这个待确认。

2.4 不能利用索引排序的分析
案例一
order by语句使用了多个不同的索引
SELECT * FROM t1 ORDER BY key1, key2;
  1. test [RW] 09:44:03 >desc select * from tx order by price, type;
  2. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  3. | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
  4. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  5. | 1  | SIMPLE      | tx    | ALL  | NULL          | NULL | NULL    | NULL | 24   | Using filesort |
  6. +----+-------------+-------+------+---------------+------+---------+------+------+----------------+
  7. 1 row in set (0.00 sec)
因为sql使用了不同的索引列,在存储上顺序存在不一致的可能性,MySQL会选择排序操作。
特例 因为所有的辅助索引里面都包含主键id,当where 字段加上order by字段沟通完整的索引时 ,可以避免filesort的
  1. test [RW] 11:20:10 >desc select * from tx where type=1 order by id;
  2. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra       |
  4. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
  5. | 1  | SIMPLE      | tx    | ref  | idx_type      | idx_type | 1       | const | 6    | Using where |
  6. +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+
  7. 1 row in set (0.00 sec)
案例二
当查询条件使用了与order by不同的其他的索引,且值为常量,但排序字段是另一个联合索引的非连续部分时
SELECT * FROM t1 WHERE key2=constant ORDER BY key_part1, key_part3;

  1. test [RW] 11:19:17 >desc select * from tx where type=1 order by gid;
  2. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  3. | id | select_type | table | type | possible_keys | key      | key_len | ref | rows   | Extra                       |
  4. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  5. | 1  | SIMPLE      | tx    | ref  | idx_type      | idx_type | 1       | const | 6    | Using where; Using filesort |
  6. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  7. 1 row in set (0.00 sec)
  8. test [RW] 11:21:08 >desc select * from tx where type=1 order by shid;
  9. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  10. | id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra                       |
  11. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  12. | 1  | SIMPLE      | tx    | ref  | idx_type      | idx_type | 1       | const | 6    | Using where; Using filesort |
  13. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  14. 1 row in set (0.00 sec)
分析 
  与案例一一致,key2 的顺序语句key1(key_part1)存储排序不一样的情况下,MySQL 都会选择filesort 。
案例三
order by 语句使用了和组合索引默认不同的排序规则
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
官方文档中提示使用混合索引排序规则会导致额外排序,其实我们创建索引的时候可以做 (key_part1 DESC, key_part2 ASC)
案例四
当where 条件中利用的索引与order by 索引不同时,与案例二有相似性。
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
  1. test [RW] 11:19:44 >desc select * from tx where type=1 order by shid;
  2. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  3. | id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra                       |
  4. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  5. | 1  | SIMPLE      | tx    | ref  | idx_type      | idx_type | 1       | const | 6    | Using where; Using filesort |
  6. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  7. 1 row in set (0.00 sec)
  8. test [RW] 11:20:07 >desc select * from tx where type=1 order by shid,gid;
  9. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  10. | id | select_type | table | type | possible_keys | key      | key_len | ref   | rows | Extra                       |
  11. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  12. | 1  | SIMPLE      | tx    | ref  | idx_type      | idx_type | 1       | const | 6    | Using where; Using filesort |
  13. +----+-------------+-------+------+---------------+----------+---------+-------+------+-----------------------------+
  14. 1 row in set (0.00 sec)
案例五
order by 字段使用了表达式
SELECT * FROM t1 ORDER BY ABS(key);
SELECT * FROM t1 ORDER BY -key;

  1. test [RW] 11:53:39 >desc select * from tx where shid=3 order by -shid;
  2. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-----------------------------+
  3. | id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra                       |
  4. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-----------------------------+
  5. | 1  | SIMPLE      | tx    | ref  | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | Using where; Using filesort |
  6. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-----------------------------+
  7. 1 row in set (0.00 sec)
  1. test [RW] 11:56:26 >desc select * from tx where shid=3 order by shid;
  2. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+
  3. | id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra |
  4. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+
  5. | 1  | SIMPLE      | tx    | ref  | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | NULL |
  6. +----+-------------+-------+------+---------------+---------------+---------+-------+------+-------+
分析
    order by 的字段使用函数,和在where条件中使用函数索引一样 ,MySQL都无法利用到索引。
案例六
The query joins many tables, and the columns in the ORDER BY are not all from the first nonconstant table that is used to retrieve rows. (This is the first table in the EXPLAIN output that does not have a const join type.)
当查询语句是多表连接,并且ORDER BY中的列并不是全部来自第1个用于搜索行的非常量表.(这是EXPLAIN输出中的没有使用const联接类型的第1个表)

  1. test [RW] 12:32:43 >explain select shid,gid from tx a left join goods_type b on a.shid=b.id where a.shid=2 order by a.gid,b.id;
  2. +----+-------------+-------+-------+---------------+---------------+---------+-------+------+-------------------------------+
  3. | id | select_type | table | type  | possible_keys | key           | key_len | ref   | rows | Extra                         |
  4. +----+-------------+-------+-------+---------------+---------------+---------+-------+------+-------------------------------+
  5. | 1  | SIMPLE      | a     | ref   | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | Using index; Using temporary;                                                                                                Using filesort                |
  6. | 1  | SIMPLE      | b     | const | PRIMARY       | PRIMARY       | 4       | const | 1    | Using index                   |
  7. +----+-------------+-------+-------+---------------+---------------+---------+-------+------+----------------------------------------------+
  8. 2 rows in set (0.00 sec)
  9. test [RW] 12:32:44 >explain select shid,gid from tx a left join goods_type b on a.shid=b.id where a.shid=2 order by a.gid;
  10. +----+-------------+-------+-------+---------------+---------------+---------+-------+------+--------------------------+
  11. | id | select_type | table | type  | possible_keys | key           | key_len | ref   | rows | Extra |
  12. +----+-------------+-------+-------+---------------+---------------+---------+-------+------+--------------------------+
  13. | 1  | SIMPLE      | a     | ref   | uniq_shid_gid | uniq_shid_gid | 4       | const | 2    | Using where; Using index |
  14. | 1  | SIMPLE      | b     | const | PRIMARY       | PRIMARY       | 4       | const | 1    | Using index |
  15. +----+-------------+-------+-------+---------------+---------------+---------+-------+------+--------------------------+
  16. 2 rows in set (0.00 sec)
分析
  出现join的情况下不能利用索引其实有很多种,只要对a的访问不满足上面说的可以利用索引排序的情况都会导致额外的排序动作。但是当where + order 复合要求,order by 有包含了其他表的列就会导致额外的排序动作。
案例七
sql中包含的order by 列与group by 列不一致 
  1. test [RW] 11:26:54 >desc select * from tx group by shid order by gid;
  2. +----+-------------+-------+-------+---------------+---------------+---------+------+------+---------------------------------+
  3. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                          |
  4. +----+-------------+-------+-------+---------------+---------------+---------+------+------+--------------------------------+
  5. | 1  | SIMPLE      | tx    | index | uniq_shid_gid | uniq_shid_gid | 8       | NULL | 24   | Using temporary; Using filesor |
  6. +----+-------------+-------+-------+---------------+---------------+---------+------+------+--------------------------------+
  7. 1 row in set (0.00 sec)
group by 本身会进行排序的操作,我们可以显示的注让group by不进行额外的排序动作。
  1. test [RW] 12:09:52 >desc select * from tx group by shid order by null;
  2. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------+
  3. | id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra |
  4. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------+
  5. | 1  | SIMPLE      | tx    | index | uniq_shid_gid | uniq_shid_gid | 8       | NULL | 24   | NULL |
  6. +----+-------------+-------+-------+---------------+---------------+---------+------+------+-------+
  7. 1 row in set (0.00 sec)
案例八
索引本身不支持排序存储 比如,hash索引。
  1. CREATE TABLE `hash_test` (
  2.   `id` int(10) unsigned NOT NULL AUTO_INCREMENT ,
  3.   `name` varchar(20) NOT NULL COMMENT '名称',
  4.   PRIMARY KEY (`id`),
  5.   KEY `name` (`name`)
  6. ) ENGINE=MEMORY ;
  7. INSERT INTO `hash_test` (`id`, `name`) VALUES
  8. (1, '张三'),
  9. (2, '李四');
  10. test [RW] 12:07:27 >explain select * from hash_test force index(name) order by name;
  11. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  12. | id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows | Extra          |
  13. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  14. | 1  | SIMPLE      | hash_test | ALL  | NULL          | NULL | NULL    | NULL | 2    | Using filesort |
  15. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  16. 1 row in set (0.00 sec)
  17. test [RW] 12:07:48 >explain select * from hash_test order by name;
  18. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  19. | id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows | Extra          |
  20. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  21. | 1  | SIMPLE      | hash_test | ALL  | NULL          | NULL | NULL    | NULL | 2    | Using filesort |
  22. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  23. 1 row in set (0.00 sec)
  24. test [RW] 12:07:53 >alter table hash_test ENGINE=innodb;
  25. Query OK, 2 rows affected (0.45 sec)
  26. Records: 2 Duplicates: 0 Warnings: 0
  27. test [RW] 12:08:33 >explain select * from hash_test order by name;
  28. +----+-------------+-----------+-------+---------------+------+---------+------+------+-------------+
  29. | id | select_type | table     | type  | possible_keys | key  | key_len | ref  | rows | Extra |
  30. +----+-------------+-----------+-------+---------------+------+---------+------+------+-------------+
  31. | 1  | SIMPLE      | hash_test | index | NULL          | name | 82      | NULL | 1    | Using index |
  32. +----+-------------+-----------+-------+---------------+------+---------+------+------+-------------+
  33. 1 row in set (0.00 sec)
分析 
   hash 索引本身不支持排序存储,故不能利用到排序特性,将表转化为innodb再次查询,避免了filesort
案例九
order by的索引使用部分字符串 比如 key idx_name(name(2))

  1. test [RW] 12:08:37 >alter table hash_test drop key name ,add key idx_name(name(2));
  2. Query OK, 0 rows affected (0.03 sec)
  3. Records: 0 Duplicates: 0 Warnings: 0
  4. test [RW] 12:09:50 >explain select * from hash_test order by name;
  5. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  6. | id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows | Extra          |
  7. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  8. | 1  | SIMPLE      | hash_test | ALL  | NULL          | NULL | NULL    | NULL | 1    | Using filesort |
  9. +----+-------------+-----------+------+---------------+------+---------+------+------+----------------+
  10. 1 row in set (0.00 sec)
三 老生常谈的优化策略
 为了提高order by 查询的速度,尽可能的利用索引的有序性进行排序,如果不能利用索引排序的功能,那么我们只能退而求其次优化order by相关的缓存参数
1 增加 sort_buffer_size 大小,建议sort_buffer_size要足够大能够避免磁盘排序和合并排序次数。
2 增加 read_rnd_buffer_size 大小。
3 使用合适的列大小存储具体的内容,比如对于city字段 varchar(20)比varchar(200)能获取更好的性能。
4 将tmpdir 目录指定到os上面有足够空间的具有比较高iops能力的存储上。

四 推荐文章
[1] MySQL order by 优化的那些事儿
[2] 官方文档 
[3] order by 结果不准确的问题及解决  
[4]  MySQL排序原理与案例分析 
[5]  order by 原理以及优化 
看完本文 如果您觉得有所收获 ,可以请 北在南方 一瓶饮料 ^_^




相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
9天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
72 9
|
1月前
|
SQL 关系型数据库 MySQL
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
MySQL慢查询优化、索引优化,是必知必备,大厂面试高频,本文深入详解,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验分享。
大厂面试官:聊下 MySQL 慢查询优化、索引优化?
|
13天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
52 18
|
12天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
18 7
|
11天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
41 5
|
1月前
|
SQL 关系型数据库 MySQL
MySQL慢查询优化、索引优化、以及表等优化详解
本文详细介绍了MySQL优化方案,包括索引优化、SQL慢查询优化和数据库表优化,帮助提升数据库性能。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
MySQL慢查询优化、索引优化、以及表等优化详解
|
1月前
|
关系型数据库 MySQL Java
MySQL索引优化与Java应用实践
【11月更文挑战第25天】在大数据量和高并发的业务场景下,MySQL数据库的索引优化是提升查询性能的关键。本文将深入探讨MySQL索引的多种类型、优化策略及其在Java应用中的实践,通过历史背景、业务场景、底层原理的介绍,并结合Java示例代码,帮助Java架构师更好地理解并应用这些技术。
32 2
|
3天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
13 3
|
3天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
18 3
|
3天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE &#39;log_%&#39;;`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
22 2