0

0

mysqlmysql如何优化distinct多列查询

P粉602998670

P粉602998670

发布时间:2025-09-19 09:52:01

|

690人浏览过

|

来源于php中文网

原创

答案:优化MySQL多列DISTINCT查询需创建覆盖复合索引避免临时表和文件排序,通过EXPLAIN分析执行计划,优先使用DISTINCT去重,必要时调整内存参数或改用GROUP BY以提升性能。

mysqlmysql如何优化distinct多列查询

优化MySQL中多列

DISTINCT
查询,核心在于减少MySQL处理数据所需的开销,这通常涉及到如何高效地利用索引来避免全表扫描、临时表创建以及磁盘排序。很多时候,这不仅仅是查询语句本身的问题,更深层次地,它触及到了数据存储结构和索引策略的设计。

解决方案

要有效优化

DISTINCT
多列查询,关键在于构建能够覆盖所有
DISTINCT
列的复合索引,并理解MySQL在处理这类查询时可能创建临时表和进行文件排序的机制。通过
EXPLAIN
分析查询计划,识别性能瓶颈,然后针对性地调整索引或重写查询,例如将
DISTINCT
转换为
GROUP BY
,往往能带来显著的性能提升。

为什么
DISTINCT
多列查询会慢?

当我第一次遇到一个

SELECT DISTINCT col1, col2, col3 FROM large_table;
查询慢得像蜗牛爬的时候,我开始思考,为什么只是“去重”这么个简单的操作,就能把数据库搞得如此狼狈。后来才明白,这背后隐藏着MySQL的一些默认行为和潜在的性能陷阱。

最常见的原因是,MySQL在没有合适索引的情况下,为了找出所有唯一的

col1, col2, col3
组合,往往需要做两件事:

  1. 创建临时表(Using temporary):它会把所有符合条件的行(或者至少是
    DISTINCT
    涉及的列)先放到一个内部的内存临时表里。如果数据量大到内存装不下,这个临时表就会被写到磁盘上,这可是性能杀手。
  2. 文件排序(Using filesort):为了确保去重是准确的,MySQL通常需要对这些数据进行排序。如果数据量大,且没有合适的索引可以利用,这个排序操作也会在磁盘上进行,也就是我们常说的“文件排序”。磁盘I/O的速度远低于内存,所以一旦发生文件排序,查询速度就会急剧下降。

想象一下,数据库就像一个图书馆管理员。如果你让他找出所有不同作者和出版社组合的书籍,但他手头没有一个按“作者+出版社”排序的索引卡片系统,他只能把所有书搬出来,一本本翻看,记录下所有组合,然后再把重复的剔除掉。这个过程,就是临时表和文件排序的写照。

索引优化:如何让
DISTINCT
飞起来?

说到底,优化

DISTINCT
多列查询,很大程度上就是“喂饱”MySQL的索引。一个设计得当的复合索引,能让
DISTINCT
查询的效率天壤之别。

我的经验是,为

DISTINCT
操作涉及的所有列创建一个复合索引,而且这个索引最好是覆盖索引(Covering Index)。这意味着,查询所需的所有列(包括
WHERE
子句中的列和
DISTINCT
的列)都能直接从索引中获取,而无需回表到主数据行去查找。

例如,如果你的查询是

SELECT DISTINCT col1, col2 FROM my_table WHERE col3 = 'some_value';
,那么一个理想的索引应该是
(col3, col1, col2)
。这样,MySQL可以先用
col3
快速定位到行,然后直接在索引内部对
col1
col2
进行去重,避免了回表操作。

-- 原始查询:可能很慢
SELECT DISTINCT col1, col2 FROM my_table WHERE col3 = 'some_value';

-- 创建一个覆盖索引
CREATE INDEX idx_col3_col1_col2 ON my_table (col3, col1, col2);

通过

EXPLAIN
分析,你会发现原本的
Using temporary; Using filesort
很可能就消失了,取而代之的是
Using index
,这表示MySQL直接在索引上完成了所有操作。

有一点需要注意,复合索引的列顺序很重要。如果你经常在

WHERE
子句中使用
col3
,那么
col3
应该放在索引的最前面。如果
DISTINCT
的列没有被
WHERE
子句过滤,那么
DISTINCT
的列可以作为索引的前缀。总之,目标是让索引尽可能地帮助MySQL过滤和排序。

GROUP BY
DISTINCT
:这对“双胞胎”有何不同?

在MySQL中,

SELECT DISTINCT col1, col2 FROM table;
SELECT col1, col2 FROM table GROUP BY col1, col2;
这两种写法,在很多情况下,它们的执行计划是非常相似的,甚至可能完全一样。MySQL的查询优化器足够聪明,它会识别出这两种语句在逻辑上的等价性,并尝试用相同的方式去优化。

神采PromeAI
神采PromeAI

将涂鸦和照片转化为插画,将线稿转化为完整的上色稿。

下载

我个人在工作中,如果仅仅是为了去重,通常会优先使用

DISTINCT
,因为它语义更明确,代码也更简洁。但如果查询中还需要进行聚合操作,比如
SELECT col1, COUNT(col2) FROM table GROUP BY col1;
,那么
GROUP BY
就是唯一的选择。

然而,在某些边缘情况下,或者当MySQL版本不同时,

GROUP BY
的执行计划可能会略有不同,甚至可能在某些特定的复合索引和数据分布下表现出微小的性能差异。例如,
GROUP BY
在某些优化路径下,可能更能利用到松散索引扫描(Loose Index Scan),这是一种非常高效的
GROUP BY
优化方式。

所以,我的建议是:

  1. 优先使用
    DISTINCT
    ,如果仅仅是去重。
  2. 使用
    GROUP BY
    ,如果需要聚合函数
  3. 始终用
    EXPLAIN
    来验证
    :无论你用
    DISTINCT
    还是
    GROUP BY
    ,都应该运行
    EXPLAIN
    来检查其执行计划。如果发现性能瓶颈,再考虑是否可以通过切换两者来尝试不同的优化路径。
-- 使用DISTINCT
SELECT DISTINCT product_id, category_id FROM orders;

-- 使用GROUP BY,通常执行计划相似
SELECT product_id, category_id FROM orders GROUP BY product_id, category_id;

这两种写法,对于优化器来说,往往殊途同归。关键还是在于底层的索引是否给力。

内存与磁盘:临时表背后的性能瓶颈

深入一点看,

DISTINCT
GROUP BY
查询如果需要创建临时表,那么这个临时表是放在内存里还是磁盘上,对性能的影响巨大。这主要受几个MySQL系统变量控制:

  • tmp_table_size
    :这是内存中
    HEAP
    临时表的最大大小。
  • max_heap_table_size
    :这个变量也限制了用户创建的
    MEMORY
    表的以及内部临时表的大小。

当一个查询需要创建内部临时表,并且这个临时表的大小超过了

tmp_table_size
max_heap_table_size
(取两者中的最小值),MySQL就会自动将这个内存中的临时表转换成磁盘上的
MyISAM
InnoDB
临时表。一旦数据写入磁盘,性能就会急剧下降,因为磁盘I/O比内存I/O慢得多。

你可以在会话级别调整这些参数,但要小心,全局调整可能会消耗大量内存。

-- 查看当前会话的临时表大小限制
SHOW VARIABLES LIKE 'tmp_table_size';
SHOW VARIABLES LIKE 'max_heap_table_size';

-- 尝试在会话级别提高限制(仅供测试或特定场景,生产环境需谨慎)
SET SESSION tmp_table_size = 128 * 1024 * 1024; -- 128MB
SET SESSION max_heap_table_size = 128 * 1024 * 1024; -- 128MB

除了临时表大小,

sort_buffer_size
也会影响文件排序的效率。如果
filesort
发生,MySQL会尝试在
sort_buffer_size
指定的内存区域内进行排序。如果数据量超出这个限制,同样会溢出到磁盘。

优化这些参数并非万能药,它只是在索引优化失效或不适用时的辅助手段。最根本的还是让查询尽可能地利用索引,避免临时表和文件排序的发生。毕竟,避免问题发生比解决问题要高效得多。

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

651

2023.06.20

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

244

2023.06.21

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

277

2023.07.18

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

513

2023.07.19

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

249

2023.07.25

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

384

2023.08.08

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

521

2023.08.11

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

592

2023.08.14

苹果官网入口直接访问
苹果官网入口直接访问

苹果官网直接访问入口是https://www.apple.com/cn/,该页面具备0.8秒首屏渲染、HTTP/3与Brotli加速、WebP+AVIF双格式图片、免登录浏览全参数等特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

10

2025.12.24

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 1.4万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 769人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号