前言
DISTINCT 實(shí)際上和 GROUP BY 操作的實(shí)現(xiàn)非常相似,只不過是在 GROUP BY 之后的每組中只取出一條記錄而已。所以,DISTINCT 的實(shí)現(xiàn)和 GROUP BY 的實(shí)現(xiàn)也基本差不多,沒有太大的區(qū)別。同樣可以通過松散索引掃描或者是緊湊索引掃描來實(shí)現(xiàn),當(dāng)然,在無法僅僅使用索引即能完成 DISTINCT 的時候,MySQL 只能通過臨時表來完成。
但是,和 GROUP BY 有一點(diǎn)差別的是,DISTINCT 并不需要進(jìn)行排序。也就是說,在僅僅只是 DISTINCT 操作的 Query 如果無法僅僅利用索引完成操作的時候,MySQL 會利用臨時表來做一次數(shù)據(jù)的“緩存”,但是不會對臨時表中的數(shù)據(jù)進(jìn)行 filesort 操作。
當(dāng)然,如果我們在進(jìn)行 DISTINCT 的時候還使用了 GROUP BY 并進(jìn)行了分組,并使用了類似于 MAX 之類的聚合函數(shù)操作,就無法避免 filesort 了。
下面我們就通過幾個簡單的 Query 示例來展示一下 DISTINCT 的實(shí)現(xiàn)。
1.首先看看通過松散索引掃描完成 DISTINCT 的操作:
sky@localhost : example 11:03:41> EXPLAIN SELECT DISTINCT group_id -> FROM group_messageG *************************** 1. row *************************** id: 1 SELECT_type: SIMPLE table: group_message type: range possible_keys: NULL key: idx_gid_uid_gc key_len: 4 ref: NULL rows: 10 Extra: Using index for group-by 1 row in set (0.00 sec)
我們可以很清晰的看到,執(zhí)行計劃中的 Extra 信息為“Using index for group-by”,這代表什么意思?為什么我沒有進(jìn)行 GROUP BY 操作的時候,執(zhí)行計劃中會告訴我這里通過索引進(jìn)行了 GROUP BY 呢?
其實(shí)這就是于 DISTINCT 的實(shí)現(xiàn)原理相關(guān)的,在實(shí)現(xiàn) DISTINCT的過程中,同樣也是需要分組的,然后再從每組數(shù)據(jù)中取出一條返回給客戶端。而這里的 Extra 信息就告訴我們,MySQL 利用松散索引掃描就完成了整個操作。
當(dāng)然,如果 MySQL Query Optimizer 要是能夠做的再人性化一點(diǎn)將這里的信息換成“Using index for distinct”那就更好更容易讓人理解了,呵呵。
2.我們再來看看通過緊湊索引掃描的示例:
sky@localhost : example 11:03:53> EXPLAIN SELECT DISTINCT user_id -> FROM group_message -> WHERE group_id = 2G *************************** 1. row *************************** id: 1 SELECT_type: SIMPLE table: group_message type: ref possible_keys: idx_gid_uid_gc key: idx_gid_uid_gc key_len: 4 ref: const rows: 4 Extra: Using WHERE; Using index 1 row in set (0.00 sec)
這里的顯示和通過緊湊索引掃描實(shí)現(xiàn) GROUP BY 也完全一樣。實(shí)際上,這個 Query 的實(shí)現(xiàn)過程中,MySQL 會讓存儲引擎掃描 group_id = 2 的所有索引鍵,得出所有的 user_id,然后利用索引的已排序特性,每更換一個 user_id 的索引鍵值的時候保留一條信息,即可在掃描完所有 gruop_id = 2 的索引鍵的時候完成整個 DISTINCT 操作。
3.下面我們在看看無法單獨(dú)使用索引即可完成 DISTINCT 的時候會是怎樣:
sky@localhost : example 11:04:40> EXPLAIN SELECT DISTINCT user_id -> FROM group_message -> WHERE group_id > 1 AND group_id 10G *************************** 1. row *************************** id: 1 SELECT_type: SIMPLE table: group_message type: range possible_keys: idx_gid_uid_gc key: idx_gid_uid_gc key_len: 4 ref: NULL rows: 32 Extra: Using WHERE; Using index; Using temporary 1 row in set (0.00 sec)
當(dāng) MySQL 無法僅僅依賴索引即可完成 DISTINCT 操作的時候,就不得不使用臨時表來進(jìn)行相應(yīng)的操作了。但是我們可以看到,在 MySQL 利用臨時表來完成 DISTINCT 的時候,和處理 GROUP BY 有一點(diǎn)區(qū)別,就是少了 filesort。
實(shí)際上,在 MySQL 的分組算法中,并不一定非要排序才能完成分組操作的,這一點(diǎn)在上面的 GROUP BY 優(yōu)化小技巧中我已經(jīng)提到過了。實(shí)際上這里 MySQL 正是在沒有排序的情況下實(shí)現(xiàn)分組最后完成 DISTINCT 操作的,所以少了 filesort 這個排序操作。
4.最后再和 GROUP BY 結(jié)合試試看:
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_idG *************************** 1. row *************************** id: 1 SELECT_type: SIMPLE table: group_message type: range possible_keys: idx_gid_uid_gc key: idx_gid_uid_gc key_len: 4 ref: NULL rows: 32 Extra: Using WHERE; Using index; Using temporary; Using filesort 1 row in set (0.00 sec)
最后我們再看一下這個和 GROUP BY 一起使用帶有聚合函數(shù)的示例,和上面第三個示例相比,可以看到已經(jīng)多了 filesort 排序操作了,正是因?yàn)槲覀兪褂昧?MAX 函數(shù)的緣故。要取得分組后的 MAX 值,又無法使用索引完成操作,只能通過排序才行了。
由于 DISTINCT的實(shí)現(xiàn)基本上和 GROUP BY 的實(shí)現(xiàn)差不多,所以這篇文章就不再畫圖展示實(shí)現(xiàn)過程了
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
標(biāo)簽:駐馬店 陜西 北京 鄂爾多斯 梅河口 昌都 荊門 黔西
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL DISTINCT 的基本實(shí)現(xiàn)原理詳解》,本文關(guān)鍵詞 MySQL,DISTINCT,的,基本,實(shí)現(xiàn),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。