MySQL DISTINCT 的基本實(shí)現(xiàn)原理詳解
前言
DISTINCT 實(shí)際上和 GROUP BY 操作的實(shí)現(xiàn)非常相似,只不過(guò)是在 GROUP BY 之后的每組中只取出一條記錄而已。所以,DISTINCT 的實(shí)現(xiàn)和 GROUP BY 的實(shí)現(xiàn)也基本差不多,沒(méi)有太大的區(qū)別。同樣可以通過(guò)松散索引掃描或者是緊湊索引掃描來(lái)實(shí)現(xiàn),當(dāng)然,在無(wú)法僅僅使用索引即能完成 DISTINCT 的時(shí)候,MySQL 只能通過(guò)臨時(shí)表來(lái)完成。
但是,和 GROUP BY 有一點(diǎn)差別的是,DISTINCT 并不需要進(jìn)行排序。也就是說(shuō),在僅僅只是 DISTINCT 操作的 Query 如果無(wú)法僅僅利用索引完成操作的時(shí)候,MySQL 會(huì)利用臨時(shí)表來(lái)做一次數(shù)據(jù)的“緩存”,但是不會(huì)對(duì)臨時(shí)表中的數(shù)據(jù)進(jìn)行 filesort 操作。
當(dāng)然,如果我們?cè)谶M(jìn)行 DISTINCT 的時(shí)候還使用了 GROUP BY 并進(jìn)行了分組,并使用了類(lèi)似于 MAX 之類(lèi)的聚合函數(shù)操作,就無(wú)法避免 filesort 了。
下面我們就通過(guò)幾個(gè)簡(jiǎn)單的 Query 示例來(lái)展示一下 DISTINCT 的實(shí)現(xiàn)。
1.首先看看通過(guò)松散索引掃描完成 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í)行計(jì)劃中的 Extra 信息為“Using index for group-by”,這代表什么意思?為什么我沒(méi)有進(jìn)行 GROUP BY 操作的時(shí)候,執(zhí)行計(jì)劃中會(huì)告訴我這里通過(guò)索引進(jìn)行了 GROUP BY 呢?
其實(shí)這就是于 DISTINCT 的實(shí)現(xiàn)原理相關(guān)的,在實(shí)現(xiàn) DISTINCT的過(guò)程中,同樣也是需要分組的,然后再?gòu)拿拷M數(shù)據(jù)中取出一條返回給客戶端。而這里的 Extra 信息就告訴我們,MySQL 利用松散索引掃描就完成了整個(gè)操作。
當(dāng)然,如果 MySQL Query Optimizer 要是能夠做的再人性化一點(diǎn)將這里的信息換成“Using index for distinct”那就更好更容易讓人理解了,呵呵。
2.我們?cè)賮?lái)看看通過(guò)緊湊索引掃描的示例:
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)
這里的顯示和通過(guò)緊湊索引掃描實(shí)現(xiàn) GROUP BY 也完全一樣。實(shí)際上,這個(gè) Query 的實(shí)現(xiàn)過(guò)程中,MySQL 會(huì)讓存儲(chǔ)引擎掃描 group_id = 2 的所有索引鍵,得出所有的 user_id,然后利用索引的已排序特性,每更換一個(gè) user_id 的索引鍵值的時(shí)候保留一條信息,即可在掃描完所有 gruop_id = 2 的索引鍵的時(shí)候完成整個(gè) DISTINCT 操作。
3.下面我們?cè)诳纯礋o(wú)法單獨(dú)使用索引即可完成 DISTINCT 的時(shí)候會(huì)是怎樣:
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 無(wú)法僅僅依賴索引即可完成 DISTINCT 操作的時(shí)候,就不得不使用臨時(shí)表來(lái)進(jìn)行相應(yīng)的操作了。但是我們可以看到,在 MySQL 利用臨時(shí)表來(lái)完成 DISTINCT 的時(shí)候,和處理 GROUP BY 有一點(diǎn)區(qū)別,就是少了 filesort。
實(shí)際上,在 MySQL 的分組算法中,并不一定非要排序才能完成分組操作的,這一點(diǎn)在上面的 GROUP BY 優(yōu)化小技巧中我已經(jīng)提到過(guò)了。實(shí)際上這里 MySQL 正是在沒(méi)有排序的情況下實(shí)現(xiàn)分組最后完成 DISTINCT 操作的,所以少了 filesort 這個(gè)排序操作。
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)
最后我們?cè)倏匆幌逻@個(gè)和 GROUP BY 一起使用帶有聚合函數(shù)的示例,和上面第三個(gè)示例相比,可以看到已經(jīng)多了 filesort 排序操作了,正是因?yàn)槲覀兪褂昧?MAX 函數(shù)的緣故。要取得分組后的 MAX 值,又無(wú)法使用索引完成操作,只能通過(guò)排序才行了。
由于 DISTINCT的實(shí)現(xiàn)基本上和 GROUP BY 的實(shí)現(xiàn)差不多,所以這篇文章就不再畫(huà)圖展示實(shí)現(xiàn)過(guò)程了
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Mysql關(guān)于數(shù)據(jù)庫(kù)是否應(yīng)該使用外鍵約束詳解說(shuō)明
MySQL 外鍵約束(FOREIGN KEY)是表的一個(gè)特殊字段,經(jīng)常與主鍵約束一起使用。對(duì)于兩個(gè)具有關(guān)聯(lián)關(guān)系的表而言,相關(guān)聯(lián)字段中主鍵所在的表就是主表,外鍵所在的表就是從表。外鍵用來(lái)建立主表與從表的關(guān)聯(lián)關(guān)系,為兩個(gè)表的數(shù)據(jù)建立連接,約束兩個(gè)表中數(shù)據(jù)的一致性和完整性2021-10-10
mysql 的root 用戶無(wú)法授權(quán)及解決navicat 遠(yuǎn)程授權(quán)提示1044問(wèn)題
這篇文章主要介紹了mysql 的root 用戶無(wú)法授權(quán),解決navicat 遠(yuǎn)程授權(quán)提示1044問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
SQL如何獲取目標(biāo)時(shí)間點(diǎn)或日期的方法實(shí)例
日期獲取在我們?nèi)粘i_(kāi)發(fā)中經(jīng)常會(huì)遇到,這篇文章主要給大家介紹了關(guān)于SQL如何獲取目標(biāo)時(shí)間點(diǎn)或日期的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10
mySQL服務(wù)器連接,斷開(kāi)及cmd使用操作
這篇文章主要介紹了mySQL服務(wù)器連接,斷開(kāi)及cmd使用操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
MySQL數(shù)據(jù)庫(kù)高級(jí)數(shù)據(jù)操作之新增數(shù)據(jù)
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)高級(jí)數(shù)據(jù)操作之新增數(shù)據(jù),文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-06-06
MySQL中的物理存儲(chǔ)結(jié)構(gòu)詳解
這篇文章主要介紹了MySQL中的物理存儲(chǔ)結(jié)構(gòu)用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
深入了解MySQL中分區(qū)表的原理與企業(yè)級(jí)實(shí)戰(zhàn)
本文詳細(xì)講解什么是分區(qū)表,分區(qū)表增刪改查的工作原理以及分區(qū)表的實(shí)戰(zhàn),分區(qū)表的場(chǎng)景有哪些,哪些場(chǎng)景不建議用分區(qū)表,并列舉出六點(diǎn)使用分區(qū)表的誤區(qū),需要的可以參考一下2022-11-11
SQL實(shí)現(xiàn)LeetCode(180.連續(xù)的數(shù)字)
這篇文章主要介紹了SQL實(shí)現(xiàn)LeetCode(180.連續(xù)的數(shù)字),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08

