mysql DISTINCT 的实现与优化

DISTINCT实际上和GROUP
BY的操作非常相似,只不过是在GROUP BY之后的每组中只取出一条记录而已。所以,DISTINCT的实现和GROUP
BY的实现也基本差不多,没有太大的区别。同样可以通过松散索引扫描或者是紧凑索引扫描来实现,当然,在无法仅仅使用索引即能完成DISTINCT的时候,MySQL只能通过临时表来完成。但是,和GROUP
BY有一点差别的是,DISTINCT并不需要进行排序。也就是说,在仅仅只是DISTINCT操作的Query如果无法仅仅利用索引完成操作的时候,MySQL会利用临时表来做一次数据的“缓存”,但是不会对临时表中的数据进行filesort操作。当然,如果我们在进行DISTINCT的时候还使用了GROUP
BY并进行了分组,并使用了类似于MAX之类的聚合函数操作,就无法避免filesort了。

下面我们就通过几个简单的Query示例来展示一下DISTINCT的实现。

 

1.首先看看通过松散索引扫描完成DISTINCT的操作:

sky@localhost: 
example
11:03:41>EXPLAIN
SELECT  DISTINCT group_id

-> 
FROM group_message\G

***************************1. 
row  ***************************

id: 
1SELECT_type:SIMPLE

table:group_message

type:range

possible_keys:NULL

key: 
idx_gid_uid_gc

key_len:4ref: 
NULL

rows:10Extra:
Using index
for  group-by


row 
in  set  (0.00sec)

 

我们可以很清晰的看到,执行计划中的Extra信息为“Usingindex
for group-by”,这代表什么意思?为什么我没有进行GROUP
BY操作的时候,执行计划中会告诉我这里通过索引进行了GROUP BY呢?其实这就是于DISTINCT的实现原理相关的,在实现DISTINCT的过程中,同样也是需要分组的,然后再从每组数据中取出一条返回给客户端。而这里的Extra信息就告诉我们,MySQL利用松散索引扫描就完成了整个操作。当然,如果MySQLQuery
Optimizer要是能够做的再人性化一点将这里的信息换成“Using
index for distinct”那就更好更容易让人理解了,呵呵。

 

2. 
我们再来看看通过紧凑索引扫描的示例:

sky@localhost: example
11:03:53>
EXPLAIN SELECT DISTINCT user_id

->FROM group_message

->WHERE group_id =
2\G

***************************1.
row ***************************

id:1SELECT_type:
SIMPLE

table:group_message

type:ref

possible_keys:idx_gid_uid_gc

key:idx_gid_uid_gc

key_len:4ref:
const

rows:4Extra:
Using WHERE; Using index

1row
in set (0.00
sec)

这里的显示和通过紧凑索引扫描实现GROUP BY也完全一样。实际上,这个Query的实现过程中,MySQL会让存储引擎扫描group_id=2的所有索引键,得出所有的user_id,然后利用索引的已排序特性,每更换一个user_id的索引键值的时候保留一条信息,即可在扫描完所有gruop_id=2的索引键的时候完成整个DISTINCT操作。

3.下面我们在看看无法单独使用索引即可完成DISTINCT的时候会是怎样:

sky@localhost: example
11:04:40>
EXPLAIN SELECT DISTINCT user_id

->FROM group_message

->WHERE group_id >
1 AND group_id <
10\G

***************************1.
row ***************************

id:1SELECT_type:
SIMPLE

table:group_message

type:range

possible_keys:idx_gid_uid_gc

key:idx_gid_uid_gc

key_len:4ref:
NULL

rows:32Extra:
Using WHERE; Using index; Using temporary

1row
in set (0.00
sec)

当MySQL无法仅仅依赖索引即可完成DISTINCT操作的时候,就不得不使用临时表来进行相应的操作了。但是我们可以看到,在MySQL利用临时表来完成DISTINCT的时候,和处理GROUP
BY有一点区别,就是少了filesort。实际上,在MySQL的分组算法中,并不一定非要排序才能完成分组操作的,这一点在上面的GROUP
BY优化小技巧中我已经提到过了。实际上这里MySQL正是在没有排序的情况下实现分组最后完成DISTINCT操作的,所以少了filesort这个排序操作。

4.最后再和GROUP
BY结合试试看:

sky@localhost: example
11:05:06>
EXPLAIN SELECT DISTINCT max(user_id)

->FROM group_message

->WHERE group_id >
1 AND group_id <
10

->GROUP BY group_id\G

***************************1.
row ***************************

id:1SELECT_type:
SIMPLE

table:group_message

type:range

possible_keys:idx_gid_uid_gc

key:idx_gid_uid_gc

key_len:4ref:
NULL

rows:32Extra:
Using WHERE; Using index; Using temporary; Usingfilesort

1row
in set (0.00
sec)

最后我们再看一下这个和GROUP BY一起使用带有聚合函数的示例,和上面第三个示例相比,可以看到已经多了filesort排序操作了,因为我们使用了MAX函数的缘故。

对于DISTINCT的优化,和GROUP
BY基本上一致的思路,关键在于利用好索引,在无法利用索引的时候,确保尽量不要在大结果集上面进行DISTINCT操作,磁盘上面的IO操作和内存中的IO操作性能完全不是一个数量级的差距。

时间: 2024-07-30 01:49:03

mysql DISTINCT 的实现与优化的相关文章

用MySQL内建复制功能来优化可用性

在Soundbreak我们每天24小时不间断地播放实况音频和视频,所以对于MySQL的新增的复制特性,我们不能做出很令人信服的测试.通过测试我们发现,可以使用这个特性来与备份数据库服务器保持数据同步,这样当主服务器因为某种原因处理失效时,能够使用备份机处理所有的查询.对于这样的要求,配置两台服务器并不困难.我将详细讨论整个处理过程,同时讨论一下当主服务器失效时,如何使用PHP来重定向查询. MySQL内部复制功能是建立在两个或两个以上服务器之间,通过设定它们之间的主-从关系来实现的.其中一个作为

《深入浅出MySQL:数据库开发、优化与管理维护(第2版)》一一1.1 MySQL的下载

1.1 MySQL的下载 深入浅出MySQL:数据库开发.优化与管理维护(第2版) 用户通常可以到官方网站www.mysql.com下载最新版本的MySQL数据库.按照用户群分类,MySQL数据库目前分为社区版(Community Server)和企业版(Enterprise),它们最重要的区别在于:社区版是自由下载而且完全免费的,但是官方不提供任何技术支持,适用于大多数普通用户:企业版是收费的,不能在线下载,相应地,它提供了更多的功能和更完备的技术支持,更适合于对数据库的功能和可靠性要求较高的

MySQL my.cnf参数配置优化详解

MySQL my.cnf参数配置优化详解 本配置文件针对Dell R710,双至强E5620.16G内存的硬件配置.CentOS 5.6 64位系统,MySQL 5.5.x 稳定版.适用于日IP 50-100w,PV 100-300w的站点,主要使用InnoDB存储引擎.其他应用环境请根据实际情况来设置优化. 注:你的MySQL 版本可能和这里用的不同,所以有些参数会废弃,有些被替代,当发现启动异常或者使用异常时,请取消某些配置. # 客户端 # 以下选项会被MySQL客户端应用读取.注意只有M

关于mysql的dbcp的配置优化

问题描述 关于mysql的dbcp的配置优化 一直以来对dbcp对mysql的配置比较疑惑,始终没有找到如何配置的优化方案,这比较让人困惑,关键是没有找到任何有帮助的文档和资料,网上搜到的一些资料也都千篇一律,没有太多的借鉴意义,希望有朋友可以给一些有帮助的建议 <!-- 默认的事务隔离级别 --> <property name="defaultTransactionIsolation" value="REPEATABLE_READ"><

mysql SELECT查询的速度优化详解

总的来说,要想使一个较慢速select ... where更快,应首先检查是否能增加一个索引.不同表之间的引用通常通过索引来完成.你可以使用explain语句来确定select语句使用哪些索引.参见7.4.5节,"mysql教程如何使用索引"和7.2.1节,"explain语法(获取关于select的信息)". 下面是一些加速对myisam表的查询的一般建议: ·         为了帮助mysql更好地优化查询,在一个装载数据后的表上运行analyze table

MySQL数据库21条最佳性能优化经验_Mysql

今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情. 当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能.这里,我们不会讲过多的SQL语句的优化,而只是针对MySQL这一Web应用最多的数据库.希望下面的这些优化技巧对你有用. 1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被M

高性能的MySQL(6)如何优化特定类型的查询

只要说明几种日常用到的查询可以优化的地方,只是特别的情况下可以使用,不要期望任何情况下都成立,优化是一个实践的漫长过程! 一.最大值和最小值优化 对于min()和max()查询,MySQL的优化并不是很好,比方说: select min(actor_id) from actor where first_name = 'Jane'; 因为first_name上没有索引,所以这个会扫描全表.如果MySQL能按主键扫描,那么第一个满足条件的记录就是要找的最小指了,因为主键是严格按照大小顺序排列的,一个

Mysql group by语句的优化分析

默认情况下,MySQL排序所有GROUP BY col1, col2, ....,查询的方法如同在查询中指定ORDER BY  col1, col2, ....如果显式包括一个包含相同的列的ORDER BY子句,MySQL可以毫不减速地对它进行优化,尽管仍然进行排序. 如果查询包括GROUP BY但你想要避免排序结果的消耗,你可以指定ORDER BY NULL禁止排序.例如:  代码如下 复制代码 INSERT INTO foo SELECT a, COUNT(*) FROM bar GROUP

Linux VPS下MySQL的安装配置和优化

MySQL是Linux的重要组件,所有的Linux几乎都自带MySQL.RasHost VPS为了给所有客户提供一个干净的VPS环境,没有预装MySQL,毕竟不是所有的客户都用MySQL. 本文给出在Linux VPS下怎样手工安装配置和优化mysql 安装 在Debian/Ubuntu下: apt-get install mysql-server-5.0 在CentOS下: yum install -y mysql-server 配置 主要是中文环境配置,配置MySQL全面缺省支持UTF-8.