mysql DISTINCT 的實現與優化

2021-06-21 19:03:17 字數 4569 閱讀 2866

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

:  1

select_type:******

table:group_message

type:range

possible_keys:null

key: 

idx_gid_uid_gc

key_len:

4ref: 

null

rows:

10extra: using 

index for

group-by 1

row  in

set  (0.00

sec)

我們可以很清晰的看到,執行計畫中的

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: ******

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 1

row

inset (

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 >

1and group_id <

10\g

***************************

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

:1select_type: ******

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 1

row

inset (

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 >

1and group_id < 10

->group by group_id\g

***************************

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

:1select_type: ******

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 1

row

inset (

0.00

sec)

最後我們再看一下這個和

group by

一起使用帶有聚合函式的示例,和上面第三個示例相比,可以看到已經多了

filesort

排序操作了,因為我們使用了

max函式的緣故。 對於

distinct

的優化,和

group by

基本上一致的思路,關鍵在於利用好索引,在無法利用索引的時候,確保盡量不要在大結果集上面進行

distinct

操作,磁碟上面的

io操作和記憶體中的

io操作效能完全不是乙個數量級的差距。

MySQL distinct 返回其他字段

前倆天接到乙個 面試,被面試官的乙個複雜sql語句問題給問懵逼了,今日回想,還是準備的不夠充分啊!這次就查漏補缺吧!言歸正傳,在使用mysql時,有時需要查詢出某個欄位不重複的記錄,雖然mysql提供有distinct這個關鍵字來過濾掉多餘的重覆記錄只保留一條,但往往只用它來返回不重覆記錄的條數,而...

MySQL Distinct 去掉查詢結果重覆記錄

出處 使用 distinct 關鍵字可以去掉查詢中某個欄位的重覆記錄。語法 select distinct column from tb name例子 假定 user 表有如下記錄 uidusername1小李 2小張3小李 4小王5小李 6小張sql 語句 select distinct user...

MySQL DISTINCT 去重(過濾重複資料)

在使用 mysql select 語句查詢資料的時候返回的是所有匹配的行。例如,查詢 tb students info 表中所有 age 的執行結果如下所示。mysql select age from tb students info age 25 23 23 22 24 21 22 23 22 2...