Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL8-SOP

  • MySQL实战45讲学习笔记

    • MySQL45讲学习笔记
    • MySQL基础架构
    • MySQL日志系统 (Redo Log 与 Binlog)
    • MySQL字符串字段索引优化
    • MySQL索引原理与优化
    • MySQL锁机制详解
    • MySQL事务与MVCC机制
    • MySQL普通索引与唯一索引的选择
    • MySQL优化器如何选择索引
    • MySQL抖动刷脏页
    • 表空间管理与回收
    • count函数详解
    • 日志索引
    • orderby工作原理
      • 1. OrderBy基础概念
        • 1.1 OrderBy基本语法
        • 1.2 OrderBy的执行时机
      • 2. OrderBy内部执行机制
        • 2.1 排序算法选择
        • 2.2 内存排序过程
        • 2.3 外部排序过程
      • 3. OrderBy性能分析
        • 3.1 排序性能影响因素
        • 3.2 EXPLAIN分析排序
        • 3.3 排序相关状态监控
      • 4. OrderBy优化策略
        • 4.1 索引优化
        • 4.2 复合索引优化
        • 4.3 排序字段优化
      • 5. 特殊排序场景优化
        • 5.1 分页排序优化
        • 5.2 多字段排序优化
        • 5.3 字符串排序优化
      • 6. OrderBy与索引的深度关系
        • 6.1 索引覆盖与排序
        • 6.2 索引顺序的重要性
        • 6.3 复合索引的最左前缀原则
      • 7. 大数据量排序优化
        • 7.1 排序缓冲区优化
        • 7.2 外部排序优化
        • 7.3 内存与磁盘平衡
      • 8. 实际应用案例
        • 8.1 电商商品排序
        • 8.2 用户列表排序
        • 8.3 日志排序
      • 9. 性能监控与调优
        • 9.1 排序性能监控
        • 9.2 慢查询分析
        • 9.3 排序优化验证
      • 10. 最佳实践建议
        • 10.1 设计阶段优化
        • 10.2 查询优化建议
        • 10.3 监控与维护
      • 11. 总结
    • 随机排序实现
    • SQL性能差异函数与转换
    • 慢查询分析锁与版本
    • 幻读与间隙锁
    • 加锁规则分析
    • 应急性能优化方法
    • 数据持久化保证
    • 主备一致性原理
    • 高可用架构与切换
    • 备库延迟分析与优化
    • 主备切换GTID
    • 读写分离实践与问题
    • 数据库健康检查
    • 锁与死锁
    • 数据误删恢复
    • Kill命令详解
    • 查询与内存使用分析
    • Join原理与选择
    • 临时表原理与应用
    • 内部临时表详解
    • Memory引擎详解
    • 自增ID详解
    • Insert加锁分析
    • 表复制方法比较
    • Grant与权限管理
    • 分区表详解
    • SQL语句中的Join问题
    • 自增ID用尽问题
  • 专题系列
  • MySQL实战45讲学习笔记
Carry の Blog
2024-07-27
目录

orderby工作原理

# OrderBy工作原理

在MySQL数据库查询中,ORDER BY子句用于对结果集进行排序,是日常开发中最常用的功能之一。然而,排序操作往往成为查询性能的瓶颈。本文将深入剖析MySQL中ORDER BY的内部工作原理,并提供优化策略。

# 1. OrderBy基础概念

# 1.1 OrderBy基本语法

-- OrderBy基本语法
SELECT column1, column2, ...
FROM table_name
ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...;

-- 示例
SELECT name, age, salary 
FROM employees 
ORDER BY salary DESC, age ASC;

-- 单列排序
SELECT name, age FROM employees ORDER BY age;

-- 多列排序
SELECT name, department, salary 
FROM employees 
ORDER BY department ASC, salary DESC;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 1.2 OrderBy的执行时机

-- OrderBy执行时机:
-- 1. 在WHERE条件过滤后执行
-- 2. 在SELECT字段选择后执行
-- 3. 在GROUP BY和HAVING之后执行

-- 执行顺序示例
SELECT name, salary 
FROM employees 
WHERE age > 25 
GROUP BY department 
HAVING AVG(salary) > 5000 
ORDER BY salary DESC;
1
2
3
4
5
6
7
8
9
10
11
12

# 2. OrderBy内部执行机制

# 2.1 排序算法选择

-- MySQL根据数据量选择不同的排序算法:
-- 1. 内存排序:数据量较小时使用
-- 2. 外部排序:数据量较大时使用

-- 查看排序相关参数
SHOW VARIABLES LIKE 'sort_buffer_size';
SHOW VARIABLES LIKE 'read_buffer_size';
SHOW VARIABLES LIKE 'tmp_table_size';
1
2
3
4
5
6
7
8

# 2.2 内存排序过程

-- 内存排序的工作过程:
-- 1. 为排序分配内存缓冲区(sort_buffer)
-- 2. 从表中读取数据到内存
-- 3. 在内存中进行排序
-- 4. 将排序结果返回给客户端

-- 查看排序状态
SHOW STATUS LIKE 'Sort%';

-- 关键状态变量:
-- Sort_scan: 使用排序扫描的行数
-- Sort_range: 使用排序范围的行数
-- Sort_rows: 排序的行数
-- Sort_merge_passes: 排序合并的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.3 外部排序过程

-- 外部排序适用场景:
-- 1. 排序数据量超过sort_buffer_size
-- 2. 内存不足以容纳所有数据

-- 外部排序工作过程:
-- 1. 将数据分块读入内存
-- 2. 每块数据在内存中排序
-- 3. 将排序后的数据块写入临时文件
-- 4. 多路归并排序合并临时文件
-- 5. 将最终结果返回给客户端

-- 查看临时文件使用情况
SHOW STATUS LIKE 'Created_tmp%';
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3. OrderBy性能分析

# 3.1 排序性能影响因素

-- 影响排序性能的因素:
-- 1. 数据量大小
-- 2. 排序字段的索引情况
-- 3. 内存配置参数
-- 4. 排序字段的数据类型
-- 5. 排序方向(ASC/DESC)

-- 性能测试示例
-- 测试不同数据量的排序性能
SET @start_time = NOW();
SELECT * FROM large_table ORDER BY sort_column;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as execution_time;

-- 比较不同排序方向
SET @start_time = NOW();
SELECT * FROM large_table ORDER BY sort_column ASC;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as asc_time;

SET @start_time = NOW();
SELECT * FROM large_table ORDER BY sort_column DESC;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as desc_time;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 3.2 EXPLAIN分析排序

-- 使用EXPLAIN分析排序
EXPLAIN SELECT name, salary FROM employees ORDER BY salary DESC;

-- 查看Extra列中的信息
-- "Using filesort": 需要进行文件排序
-- "Using index": 使用了索引,避免了文件排序
-- "Using temporary": 创建了临时表

-- 更详细的分析
EXPLAIN FORMAT=JSON 
SELECT name, salary FROM employees ORDER BY salary DESC;
1
2
3
4
5
6
7
8
9
10
11

# 3.3 排序相关状态监控

-- 监控排序性能
SELECT 
    VARIABLE_NAME,
    VARIABLE_VALUE
FROM information_schema.GLOBAL_STATUS 
WHERE VARIABLE_NAME LIKE '%Sort%';

-- 关键监控指标:
-- Sort_scan: 扫描行数
-- Sort_range: 范围排序行数
-- Sort_rows: 排序行数
-- Sort_merge_passes: 合并次数
1
2
3
4
5
6
7
8
9
10
11
12

# 4. OrderBy优化策略

# 4.1 索引优化

-- 为排序字段创建索引
CREATE INDEX idx_employee_salary ON employees(salary);
CREATE INDEX idx_employee_dept_salary ON employees(department, salary);

-- 使用索引避免文件排序
-- 优化前:需要文件排序
SELECT name, salary FROM employees ORDER BY salary DESC;

-- 优化后:使用索引
SELECT name, salary FROM employees ORDER BY salary DESC;

-- 检查是否使用了索引
EXPLAIN SELECT name, salary FROM employees ORDER BY salary DESC;
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.2 复合索引优化

-- 复合索引优化排序
-- 创建复合索引
CREATE INDEX idx_composite_sort ON employees(department, salary, age);

-- 使用复合索引进行排序
SELECT name, salary, age 
FROM employees 
WHERE department = 'IT' 
ORDER BY salary DESC, age ASC;

-- 复合索引的使用原则:
-- 1. 索引字段顺序与查询条件匹配
-- 2. 优化器会使用最左边的索引前缀
-- 3. 保持索引顺序与排序顺序一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.3 排序字段优化

-- 优化排序字段类型
-- 1. 使用合适的字段类型
-- 2. 避免不必要的字符集转换
-- 3. 减少字符串长度

-- 示例:优化前后的对比
-- 优化前:大字符串排序
SELECT name, email FROM users ORDER BY email;

-- 优化后:使用索引优化
CREATE INDEX idx_users_email ON users(email);
SELECT name, email FROM users ORDER BY email;

-- 避免函数操作
-- 优化前:函数导致索引失效
SELECT name, salary FROM employees ORDER BY UPPER(name);

-- 优化后:直接使用字段
SELECT name, salary FROM employees ORDER BY name;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 5. 特殊排序场景优化

# 5.1 分页排序优化

-- 分页排序的性能问题
-- 问题示例:大表分页排序
SELECT name, salary FROM employees ORDER BY salary DESC LIMIT 10000, 10;

-- 优化方案1:使用索引
CREATE INDEX idx_employee_salary ON employees(salary);
SELECT name, salary FROM employees ORDER BY salary DESC LIMIT 10000, 10;

-- 优化方案2:延迟关联
SELECT e.name, e.salary 
FROM (
    SELECT id FROM employees ORDER BY salary DESC LIMIT 10000, 10
) t 
JOIN employees e ON t.id = e.id;

-- 优化方案3:使用游标分页
SELECT name, salary FROM employees 
WHERE salary < (SELECT salary FROM employees ORDER BY salary DESC LIMIT 10000, 1) 
ORDER BY salary DESC 
LIMIT 10;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 5.2 多字段排序优化

-- 多字段排序优化
-- 优化前:没有合适的索引
SELECT name, department, salary 
FROM employees 
ORDER BY department, salary DESC;

-- 优化后:创建复合索引
CREATE INDEX idx_dept_salary ON employees(department, salary DESC);

-- 查询优化
SELECT name, department, salary 
FROM employees 
ORDER BY department, salary DESC;
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5.3 字符串排序优化

-- 字符串排序优化
-- 问题:字符集转换和排序
SELECT name FROM users ORDER BY name COLLATE utf8_general_ci;

-- 优化方案:使用合适字符集
-- 在创建表时指定字符集
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
);

-- 查询时避免不必要的转换
SELECT name FROM users ORDER BY name;
1
2
3
4
5
6
7
8
9
10
11
12
13

# 6. OrderBy与索引的深度关系

# 6.1 索引覆盖与排序

-- 索引覆盖排序
-- 创建覆盖索引
CREATE INDEX idx_cover_sort ON employees(salary, name, department);

-- 使用覆盖索引
SELECT salary, name FROM employees ORDER BY salary DESC;
-- 不需要回表,直接从索引获取数据

-- 查看执行计划
EXPLAIN SELECT salary, name FROM employees ORDER BY salary DESC;
1
2
3
4
5
6
7
8
9
10

# 6.2 索引顺序的重要性

-- 索引顺序对排序的影响
-- 创建不同顺序的索引
CREATE INDEX idx_salary_name ON employees(salary, name);
CREATE INDEX idx_name_salary ON employees(name, salary);

-- 测试不同索引的性能
-- 使用salary排序
SELECT name, salary FROM employees ORDER BY salary DESC;

-- 使用name排序
SELECT name, salary FROM employees ORDER BY name;

-- 索引顺序应该与查询需求匹配
1
2
3
4
5
6
7
8
9
10
11
12
13

# 6.3 复合索引的最左前缀原则

-- 最左前缀原则示例
CREATE INDEX idx_composite ON employees(department, salary, age);

-- 可以使用的查询:
SELECT * FROM employees ORDER BY department;
SELECT * FROM employees ORDER BY department, salary;
-- 不能使用:ORDER BY salary, age(缺少department)

-- 最左前缀原则:
-- 1. 从最左边的字段开始
-- 2. 可以跳过中间字段,但不能跳过
-- 3. 不能从中间字段开始
1
2
3
4
5
6
7
8
9
10
11
12

# 7. 大数据量排序优化

# 7.1 排序缓冲区优化

-- 调整排序缓冲区大小
SET SESSION sort_buffer_size = 256*1024*1024;  -- 256MB
SET SESSION read_buffer_size = 64*1024*1024;   -- 64MB

-- 查看当前设置
SHOW VARIABLES LIKE 'sort_buffer_size';
SHOW VARIABLES LIKE 'read_buffer_size';

-- 监控排序性能
SHOW STATUS LIKE 'Sort%';
1
2
3
4
5
6
7
8
9
10

# 7.2 外部排序优化

-- 外部排序的优化策略
-- 1. 增加tmp_table_size
SET SESSION tmp_table_size = 512*1024*1024;  -- 512MB

-- 2. 优化临时文件存储
SHOW VARIABLES LIKE 'tmpdir';

-- 3. 分批处理大数据
-- 使用LIMIT分批处理
SELECT * FROM large_table ORDER BY id LIMIT 1000;
SELECT * FROM large_table ORDER BY id LIMIT 1000 OFFSET 1000;
1
2
3
4
5
6
7
8
9
10
11

# 7.3 内存与磁盘平衡

-- 内存与磁盘排序的平衡
-- 查看临时文件使用情况
SHOW STATUS LIKE 'Created_tmp%';

-- 如果Created_tmp_disk_tables较高,说明经常使用外部排序
-- 需要优化:
-- 1. 增加sort_buffer_size
-- 2. 优化索引
-- 3. 减少排序数据量
1
2
3
4
5
6
7
8
9

# 8. 实际应用案例

# 8.1 电商商品排序

-- 电商商品排序场景
-- 商品列表排序
SELECT product_id, product_name, price, rating 
FROM products 
WHERE category_id = 1 
ORDER BY price DESC, rating DESC 
LIMIT 20;

-- 优化方案:
-- 1. 创建复合索引
CREATE INDEX idx_product_category_price_rating ON products(category_id, price DESC, rating DESC);

-- 2. 使用覆盖索引
CREATE INDEX idx_product_cover ON products(category_id, price DESC, rating DESC, product_id, product_name);

-- 3. 优化查询
SELECT product_id, product_name, price, rating 
FROM products 
WHERE category_id = 1 
ORDER BY price DESC, rating DESC 
LIMIT 20;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 8.2 用户列表排序

-- 用户列表排序
SELECT user_id, name, created_date, last_login 
FROM users 
WHERE status = 'active' 
ORDER BY last_login DESC, created_date DESC 
LIMIT 50;

-- 优化方案:
-- 1. 创建复合索引
CREATE INDEX idx_user_status_lastlogin_created ON users(status, last_login DESC, created_date DESC);

-- 2. 使用索引覆盖
CREATE INDEX idx_user_cover ON users(status, last_login DESC, created_date DESC, user_id, name);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.3 日志排序

-- 日志排序场景
SELECT log_id, timestamp, level, message 
FROM logs 
WHERE log_date = '2024-01-01' 
ORDER BY timestamp DESC 
LIMIT 1000;

-- 优化方案:
-- 1. 创建索引
CREATE INDEX idx_logs_date_timestamp ON logs(log_date, timestamp DESC);

-- 2. 使用分区表
CREATE TABLE logs_partitioned (
    log_id INT AUTO_INCREMENT,
    timestamp DATETIME,
    level VARCHAR(20),
    message TEXT,
    log_date DATE,
    PRIMARY KEY (log_id, log_date)
) PARTITION BY RANGE (YEAR(log_date)) (
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 9. 性能监控与调优

# 9.1 排序性能监控

-- 创建排序性能监控脚本
DELIMITER //
CREATE PROCEDURE monitor_sort_performance()
BEGIN
    DECLARE sort_scan BIGINT;
    DECLARE sort_rows BIGINT;
    DECLARE sort_merge_passes BIGINT;
    
    SELECT VARIABLE_VALUE INTO sort_scan
    FROM information_schema.GLOBAL_STATUS 
    WHERE VARIABLE_NAME = 'Sort_scan';
    
    SELECT VARIABLE_VALUE INTO sort_rows
    FROM information_schema.GLOBAL_STATUS 
    WHERE VARIABLE_NAME = 'Sort_rows';
    
    SELECT VARIABLE_VALUE INTO sort_merge_passes
    FROM information_schema.GLOBAL_STATUS 
    WHERE VARIABLE_NAME = 'Sort_merge_passes';
    
    -- 记录监控数据
    INSERT INTO sort_performance_log (timestamp, sort_scan, sort_rows, sort_merge_passes) 
    VALUES (NOW(), sort_scan, sort_rows, sort_merge_passes);
END //
DELIMITER ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 9.2 慢查询分析

-- 启用慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

-- 分析慢查询中的排序
-- 查看慢查询日志
SHOW VARIABLES LIKE 'slow_query_log_file';

-- 重点关注包含"Using filesort"的查询
1
2
3
4
5
6
7
8
9

# 9.3 排序优化验证

-- 排序优化前后对比测试
-- 优化前测试
SET @start_time = NOW();
SELECT name, salary FROM employees ORDER BY salary DESC;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as before_optimization_time;

-- 创建索引后测试
CREATE INDEX idx_employee_salary ON employees(salary);

SET @start_time = NOW();
SELECT name, salary FROM employees ORDER BY salary DESC;
SET @end_time = NOW();
SELECT TIMEDIFF(@end_time, @start_time) as after_optimization_time;

-- 查看执行计划
EXPLAIN SELECT name, salary FROM employees ORDER BY salary DESC;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 10. 最佳实践建议

# 10.1 设计阶段优化

-- 1. 预估查询需求
-- 2. 为常用排序字段创建索引
-- 3. 考虑复合索引的使用
-- 4. 选择合适的字段类型

-- 示例:合理设计索引
CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT,
    order_date DATETIME,
    amount DECIMAL(10,2),
    status VARCHAR(20),
    INDEX idx_customer_date (customer_id, order_date),
    INDEX idx_date_amount (order_date, amount),
    INDEX idx_status_date (status, order_date)
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 10.2 查询优化建议

-- 1. 避免不必要的排序
-- 2. 使用索引优化排序
-- 3. 合理使用LIMIT
-- 4. 考虑分页策略

-- 优化示例
-- 原始查询
SELECT name, salary FROM employees ORDER BY salary DESC;

-- 优化后
-- 1. 创建索引
CREATE INDEX idx_employee_salary ON employees(salary);

-- 2. 使用索引查询
SELECT name, salary FROM employees ORDER BY salary DESC;

-- 3. 添加LIMIT限制结果集
SELECT name, salary FROM employees ORDER BY salary DESC LIMIT 100;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 10.3 监控与维护

-- 建立排序性能监控体系
CREATE TABLE sort_monitor (
    id INT AUTO_INCREMENT PRIMARY KEY,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    query_text TEXT,
    execution_time DECIMAL(10,4),
    sort_rows INT,
    sort_merge_passes INT,
    created_tmp_disk_tables INT
);

-- 定期检查排序性能
SELECT 
    DATE(timestamp) as date,
    AVG(execution_time) as avg_time,
    AVG(sort_rows) as avg_sort_rows
FROM sort_monitor 
GROUP BY DATE(timestamp)
ORDER BY date DESC
LIMIT 7;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 11. 总结

OrderBy操作是数据库查询中的重要组成部分,但也是性能优化的关键点。通过深入理解其内部机制和掌握优化策略,可以显著提升查询性能。

关键优化要点包括:

  1. 索引优化:为排序字段创建合适的索引,避免文件排序
  2. 复合索引:合理设计复合索引,匹配查询需求
  3. 内存配置:调整排序缓冲区大小,平衡内存与磁盘使用
  4. 分页优化:使用延迟关联或游标分页优化大表分页
  5. 监控分析:持续监控排序性能,及时发现和解决性能瓶颈

通过系统性的优化和监控,可以有效解决OrderBy相关的性能问题,提升整个数据库系统的响应能力和用户体验。

#MySQL#OrderBy#查询优化#排序算法#学习笔记
上次更新: 3/4/2026

← 日志索引 随机排序实现→

最近更新
01
表空间管理与回收
03-04
02
MySQL抖动刷脏页
03-04
03
count函数详解
03-04
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式