【学习计划】《MySQL实战45讲》16 | “order by”是怎么工作的?

本文详细讲解了在MySQL中全字段排序与rowid排序的区别,介绍了新参数max_length_for_sort_data的作用,并通过实例和新知识点讨论了内存排序与可能的外部排序。理解这些概念有助于提高查询性能。


一、说明示例

假设你要查询城市是“杭州”的所有人名字,并且按照姓名排序返回前1000个人的姓名、年龄。

假设这个表的部分定义是这样的:

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `city` varchar(16) NOT NULL,
  `name` varchar(16) NOT NULL,
  `age` int(11) NOT NULL,
  `addr` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `city` (`city`)
) ENGINE=InnoDB;

这时,你的SQL语句可以这么写:

select city,name,age from t where city='杭州' order by name limit 1000  ;

二、新名词

1. 全字段排序

select city,name,age from t where city='杭州' order by name limit 1000  ;

语句执行流程如下所示 :

  1. 初始化sort_buffer,确定放入name、city、age这三个字段;
  2. 从索引city找到第一个满足city='杭州’条件的主键id;
  3. 到主键id索引取出整行,取name、city、age三个字段的值,存入sort_buffer中;
  4. 从索引city取下一个记录的主键id;
  5. 重复步骤3、4直到city的值不满足查询条件为止,对应的主键id也就是图中的ID_Y;
  6. 对sort_buffer中的数据按照字段name做快速排序;
  7. 按照排序结果取前1000行返回给客户端。

全字段排序

2. rowid排序

  1. 初始化sort_buffer,确定放入两个字段,即name和id;
  2. 从索引city找到第一个满足city='杭州’条件的主键id;
  3. 到主键id索引取出整行,取name、id这两个字段,存入sort_buffer中;
  4. 从索引city取下一个记录的主键id;
  5. 重复步骤3、4直到不满足city='杭州’条件为止;
  6. 对sort_buffer中的数据按照字段name进行排序;
  7. 遍历排序结果,取前1000行,并按照id的值回到原表中取出city、name和age三个字段返回给客户端。

rowid排序

3. sort_buffer

MySQL会给每个线程分配一块内存用于排序。

3. sort_buffer_size

MySQL为排序开辟的内存(sort_buffer)的大小。


三、新参数

1. max_length_for_sort_data

SET max_length_for_sort_data = 16;

max_length_for_sort_data,是MySQL中专门控制用于排序的行数据的长度的一个参数。它的意思是,如果单行的长度超过这个值,MySQL就认为单行太大,要换一个算法。


四、新知识点

1. 排序在内存中完成,也可能需要使用外部排序?

按name排序”这个动作,可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数sort_buffer_size。

如果要排序的数据量小于sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。

2. 确定一个排序语句是否使用了临时文件的方法

/* 打开optimizer_trace,只对本线程有效 */
SET optimizer_trace='enabled=on'; 

/* @a保存Innodb_rows_read的初始值 */
select VARIABLE_VALUE into @a from  performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 执行语句 */
select city, name,age from t where city='杭州' order by name limit 1000; 

/* 查看 OPTIMIZER_TRACE 输出 */
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G

/* @b保存Innodb_rows_read的当前值 */
select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 计算Innodb_rows_read差值 */
select @b-@a;

number_of_tmp_files表示的是,排序过程中使用的临时文件数。

3. 全字段排序 VS rowid排序

如果MySQL实在是担心排序内存太小,会影响排序效率,才会采用rowid排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。

如果MySQL认为内存足够大,会优先选择全字段排序,把需要的字段都放到sort_buffer中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。

这也就体现了MySQL的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问。

对于InnoDB表来说,rowid排序会要求回表多造成磁盘读,因此不会被优先选择。

### 《MySQL实战45》相关选择题与练习题 以下是一些与《MySQL实战45》相关的选择题和练习题,涵盖基础架构、查询优化、事务管理等核心内容。这些问题旨在帮助用户巩固对 MySQL 的理解。 --- #### 选择题 1. **关于 MySQL 的基础架构,下列哪一项描述是正确的?** - A. 每次执行 `SELECT` 查询时,MySQL 都会优先检查查询缓存。 - B. 查询缓存始终存储在内存中,因此其性能非常高[^1]。 - C. 如果表结构发生变化,MySQL 会自动清空所有查询缓存。 - D. 查询缓存可以在 MySQL 8.0 中启用。 **答案:C**。解析:从 MySQL 8.0 开始,查询缓存功能已被移除[^1]。 2. **关于 MySQL 的视图(View),下列哪一项描述是错误的?** - A. 视图是一种虚拟表,基于 SQL 查询结果生成。 - B. 更新视图中的数据可以直接影响底层表的数据。 - C. 视图可以包含聚合函数,如 `SUM()` 或 `AVG()`。 - D. 视图支持索引以提高查询性能。 **答案:D**。解析:视图本身不支持索引,但可以通过优化底层表的索引来间接提升视图查询性能[^2]。 3. **关于事务隔离级别,下列哪一项描述是正确的?** - A. `READ UNCOMMITTED` 是默认的隔离级别。 - B. `REPEATABLE READ` 可以完全避免幻读问题。 - C. `SERIALIZABLE` 是最严格的隔离级别,但可能导致性能下降。 - D. `READ COMMITTED` 不允许脏读,但可能产生不可重复读。 **答案:C**。解析:`SERIALIZABLE` 是最严格的隔离级别,确保事务按顺序执行,但可能导致锁等待和性能瓶颈。 --- #### 练习题 1. **创建一个视图 `top_employees`,显示薪资排名前三的员工信息(包括员工编号、姓名和薪资)。** ```sql CREATE VIEW top_employees AS SELECT empno, ename, sal FROM ( SELECT *, RANK() OVER (ORDER BY sal DESC) AS rn FROM emp ) t WHERE rn <= 3; ``` 2. **编写一条 SQL 查询语句,统计每个部门的平均薪资,并按平均薪资降序排列。** ```sql SELECT deptno, AVG(sal) AS avg_salary FROM emp GROUP BY deptno ORDER BY avg_salary DESC; ``` 3. **假设有一张名为 `orders` 的表,包含字段 `order_id`, `customer_id`, `order_date`, 和 `total_amount`。请编写一条 SQL 查询语句,找出总金额最高的前五名客户及其订单数量。** ```sql SELECT customer_id, COUNT(order_id) AS order_count FROM ( SELECT customer_id, total_amount, RANK() OVER (ORDER BY total_amount DESC) AS rn FROM orders ) t WHERE rn <= 5 GROUP BY customer_id; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值