mysql order by 排序原理

sql语句按照指定的字段进行排序是查询数据时是一个很常见的操作。当涉及到大量数据时,对于 ORDER BY 操作,可以考虑为相应的列添加索引,如果不使用索引,mysql会使用filesort来进行排序。

filesort

filesort虽然有file,但是不一定是文件排序,要分情况。下面来看下排序的具体逻辑。

filesort会将查询行数据放入sort_buffer中,然后按排序字段进行排序。sort_buffer的大小有变量sort_buffer_size来控制,默认大小256kb。

mysql> SELECT @@sort_buffer_size;
+--------------------+
| @@sort_buffer_size |
+--------------------+
|             262144 |

如果要排序的数据内容小于sort_buffer_size,排序在内存中即可完成;否则filesort会使用临时文件进行排序。数据越多生成的临时文件越多,每份文件单独排序后再归并合并成一个有序的结果。

临时文件存放再 tmpdir 变量指定的目录下,排序完成后会自动删除。

mysql> select @@tmpdir;
+----------+
| @@tmpdir |
+----------+
| /tmp     |

如果使用了filesort,在explain的Extra会显示:Using filesort。是否使用了临时文件还需要根据具体的执行过程来判断。下面通过information_schema.OPTIMIZER_TRACE表来查看是否使用临时文件。

1、准备一个t_user表,首先开启optimizer_trace

mysql> SET optimizer_trace='enabled=on';

2、然后执行查看explain这里再Extra会有Using filesort。

mysql> explain select * from t_user order by username;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+
|  1 | SIMPLE      | t_user | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 2700 |   100.00 | Using filesort |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.00 sec)

3、执行查询

select * from t_user order by username;

注意这里是执行查询,不是explain,否则下面一步看不到信息

4、查看OPTIMIZER_TRACE

SELECT * FROM information_schema.OPTIMIZER_TRACE;

OPTIMIZER_TRACE一共有4列:

QUERY:表示当前查询语句

TRACE: 包含查询优化器的trace信息,json格式

MISSING_BYTES_BEYOND_MAXMEM:丢失字节数。

在trace内容中有“filesort_summary”部分是关于filesort处理信息,如上面查询对应的filesort_summary

mysql8:

"filesort_summary": {
  "memory_available": 262144,
  "key_size": 481,
  "row_size": 6591,
  "max_rows_per_buffer": 39,
  "num_rows_estimate": 2700,
  "num_rows_found": 2800,
  "num_initial_chunks_spilled_to_disk": 8,
  "peak_memory_used": 264192,
  "sort_algorithm": "std::sort",
  "sort_mode": "<fixed_sort_key, packed_additional_fields>"
}

mysql5.7:

"filesort_summary": {
  "rows": 2800,
  "examined_rows": 2800,
  "number_of_tmp_files": 6,
  "sort_buffer_size": 261784,
  "sort_mode": "<sort_key, rowid>"
}

这里看到5.7和8差别还是有点大,但是几个主要的字段还是差不多的。这里以5.7的trace信息来看。

sort_buffer_size是sort_buffer的大小。

rows:数据行数

number_of_tmp_files:临时文件数,如果该值为0,则表示未使用临时文件,sort_buffer够用。

sort_mode:排序方式。这个指定了参与排序的数据内容不同。

sort_key, rowid:sort_buffer中加载的数据只有排序字段(sort_key)和rowid,rowid用来排序后再回表查询获取行数据。

sort_key, additional_fields:sort_buffer中加载数据包含所有的要查询的字段。

sort_key, packed_additional_fields:和上面的additional_fields差不多,只是有些可变长度会进行压缩。

那么sort_mode这两种类型一种有所有查询字段,一种只有排序字段,查询优化器是根据什么选择的呢。这里有一个参数max_length_for_sort_data,如果查询的数据行记录超过该值,则会采用sort_key, rowid模式,否则会采用sort_key, additional_fields模式。不过这个值在MySQL 8.0.20被标为过时了,建议通过调整sort_buffer_size大小来控制join_buffer大小,尽量避免使用磁盘临时文件。这两种排序模式比较:additional_fields模式不需要回表,如果查询的列比较多,可能会导致sort_buffer所能容纳的行数据变少;rowid模式每行数据很小,sort_buffer可以加载更多的行,但是最后返回数据要回表。不同数据量场景,要合理设置sort_buffer_size和max_length_for_sort_data搭配。

这里看到排序的过程,一行行拿出来进行比较,数据量大还会使用到临时文件,还是比较耗时的。那么有没有更快的方法呢?那就是使用索引。

使用索引

为什么使用索引会快?因为构建后的索引就是天然有序的,不需要再经过一行行逐一对sort_key进行比较。跳过额外的filesort。

排序使用索引的情况一般在Extra中只有Using index。使用索引一般场景:

1、查询条件中有排序索引

2、索引覆盖,查询的列都在对应的索引中

3、多列索引排序,满足最左匹配

4、排序方向一致

这只是一般的规则,下面结合几个具体的例子来看看。

t_user表上有复合索引 (username,gender,department) 还有索引(phone)

例1:

SELECT uid,phone FROM t_user ORDER BY phone;

查询列都在索引上(uid是主键),Using index

例2:

SELECT * FROM t_user ORDER BY phone;

索引不包含查询访问的所有列,则仅当索引访问比其他访问方法更高效时才使用该索引.这里查询优化器选择了Using filesort。 不太理解?

例3:

SELECT uid,phone FROM t_user WHERE uid<20 ORDER BY phone;

虽然查询内容是索引覆盖,但是where条件不在索引上(不是同一个索引),Using filesort。

这个应该可以理解一个索引筛选出来的数据内容对另一个索引是无序的。

例4:

SELECT username,gender FROM t_user  ORDER BY username,gender;
SELECT username,gender FROM t_user WHERE username='a' ORDER BY gender;

查询内容索引覆盖,where满足索引最左匹配部分,Using index。

例5:

SELECT username,gender FROM t_user WHERE username LIKE 'a%'  ORDER BY username desc,gender ;

多列排序,第一列排序username满足使用索引条件,gender和username排序方向相反,无法使用索引,会进行依次filesort。

例6:

SELECT username,gender FROM t_user WHERE username LIKE 'a%'  ORDER BY username,department ;

username排序使用filesort,department排序不满足最左匹配,中间复合索引断开(缺少gender)使用filesort。

这里只看了几个常见例子,可能有些场景比这复杂的多。想一想能使用到索引排序一个最基本的条件:当前要查询的数据范围(where筛选后)在排序列(对应的索引)是有序的。然后才考虑覆盖索引等其它需要满足的条件。

  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQLorder by原理是通过利用索引顺序或者使用rowid排序来实现数据的排序。当查询语句的order by条件和查询的执行计划中所利用的索引的索引键完全一致,并且索引访问方式为rang、ref或者index时,MySQL可以直接取得已经排好序的数据,而无需进行实际的排序操作。对于单行数据过长的情况,MySQL将使用rowid排序,即根据行数据的长度进行排序。此外,MySQL排序开辟了一个内存区域称为sort_buffer,如果要排序的数据量小于这个内存区域的大小,则会在内存中进行排序。总的来说,MySQLorder by原理是根据索引顺序或者行数据的长度来排序数据,并且使用sort_buffer来存储排序的数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Mysqlorder by 的原理](https://blog.csdn.net/ADi_1998/article/details/106590037)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [MySQLorder by的工作原理](https://blog.csdn.net/Longstar_L/article/details/107372892)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值