MySQL中的隨機(jī)抽取的實(shí)現(xiàn)
1. 引言
現(xiàn)在有一個(gè)需求是從一個(gè)單詞表中每次隨機(jī)選取三個(gè)單詞。
這個(gè)表的建表語句和如下所示:
mysql> Create table 'words'(
'id' int(11) not null auto_increment;
'word' varchar(64) default null;
primary key ('id')
) ENGINE=InnoDB;
然后我們向其中插入10000行數(shù)據(jù)。接下來我們看看如何從中隨機(jī)選擇3個(gè)單詞。
2. 內(nèi)存臨時(shí)表
首先,我們通常會想到用order by rand()來實(shí)現(xiàn)這個(gè)邏輯:
mysql> select word from words order by rand() limit 3;
雖然這句話很簡單,但是執(zhí)行流程則比較復(fù)雜。我們使用explain來看看語句的執(zhí)行情況:

Extra字段中Using temporary表示需要使用臨時(shí)表,Using filesort表示需要進(jìn)行排序。也就是需要進(jìn)行排序操作。
對于InnoDB表來說,執(zhí)行全字段排序能夠減少對于磁盤的訪問,所以會被優(yōu)先選擇。

而對于內(nèi)存表來說,回表過程只是簡單地根據(jù)數(shù)據(jù)行的位置,直接訪問內(nèi)存得到數(shù)據(jù),根本不會導(dǎo)致多訪問磁盤。所以這時(shí)MySQL會優(yōu)選選擇rowid排序。

我們接下來再來梳理下這條語句的執(zhí)行流程:
- 創(chuàng)建一個(gè)臨時(shí)表,這個(gè)表使用memory引擎,表里有兩個(gè)字段,第一個(gè)字段是double類型,記為R,第二個(gè)字段是varchar(64)類型,記為W。并且這個(gè)表沒有索引。
- 從words表中,按主鍵順序取出所有的word。對于每個(gè)word,調(diào)用rand()函數(shù)隨機(jī)生成一個(gè)大于0小于1的隨機(jī)小數(shù),并把這個(gè)隨機(jī)小數(shù)和word分別存入臨時(shí)表的R和W字段中。
- 接下來就是按照字段R進(jìn)行排序
- 初始化sort_buffer。sort_buffer有兩個(gè)字段,一個(gè)是double類型,另一個(gè)是整型。
- 從內(nèi)存臨時(shí)表中一行行取出R值和位置信息,分別存入sort_buffer的兩個(gè)字段里。
- sort_buffer按照R值進(jìn)行排序
- 排序完成后,取出前三個(gè)結(jié)果的位置信息,到內(nèi)存臨時(shí)表中取出相應(yīng)的word,返回給客戶端。
流程示意圖如下所示:

上面講的位置信息,其實(shí)就是行所在的位置,也就是我們之前說的rowid。
對于InnoDB引擎來說,對于有沒有主鍵表來說有兩種處理方式:
- 對于有主鍵的InnoDB表來說,這個(gè)rowid就是主鍵id
- 對于沒有主鍵的InnoDB表來說,這個(gè)rowid是由系統(tǒng)生成的,用來標(biāo)識不同行。
因此,order by randn()使用了內(nèi)存臨時(shí)表,內(nèi)存臨時(shí)表的排序方法用的是rowid排序方法。
3. 磁盤臨時(shí)表
不是所有的臨時(shí)表都是內(nèi)存臨時(shí)表。tmp_table_size這個(gè)配置限制了內(nèi)存臨時(shí)表的大小,如果超過了這個(gè)大小,就會使用磁盤臨時(shí)表。InnoDB引擎就是默認(rèn)使用磁盤臨時(shí)表。
4. 優(yōu)先隊(duì)列排序算法
在MySQL5.6之后,引入了優(yōu)先隊(duì)列排序算法,這種算法是不需要使用臨時(shí)文件的。而原本的歸并排序算法則是需要使用臨時(shí)文件。
因?yàn)楫?dāng)你使用歸并算法的時(shí)候,其實(shí)你只需要得到前3,但是你是用完歸并排序,那已經(jīng)整體有序了,造成了資源的浪費(fèi)。
而優(yōu)先隊(duì)列排序算法則可以只取到前三,執(zhí)行流程如下:
- 對于這10000個(gè)準(zhǔn)備排序的(R,rowid),先取前三行,構(gòu)造成一個(gè)堆,并且將最大的值放在堆頂;
- 取下一行(R’,rowid’),跟當(dāng)前堆里面最大的R比較,如果R’小于R,則把(R,rowid)從堆中去掉,換成(R’,rowid’)。
- 不斷重復(fù)上面的過程。
流程如下圖所示:

但是當(dāng)limit的數(shù)比較大時(shí),維護(hù)堆比較困難,所以又會使用歸并排序算法。
來源:自己整理的MySQL實(shí)戰(zhàn)45講筆記
到此這篇關(guān)于MySQL中的隨機(jī)抽取的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)MySQL 隨機(jī)抽取內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL數(shù)據(jù)庫存儲引擎的應(yīng)用
存儲引擎是MySQL將數(shù)據(jù)存儲在文件系統(tǒng)中的存儲方式,本文主要介紹了MySQL數(shù)據(jù)庫的存儲引擎的應(yīng)用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
Ubuntu 設(shè)置開放 MySQL 服務(wù)遠(yuǎn)程訪問教程
這篇文章主要介紹了Ubuntu 設(shè)置開放 MySQL 服務(wù)遠(yuǎn)程訪問教程,需要的朋友可以參考下2014-10-10
解決MySQL:Invalid GIS data provided to&nbs
這篇文章主要介紹了解決MySQL:Invalid GIS data provided to function st_geometryfromtext問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
MySQL?原理與優(yōu)化之Limit?查詢優(yōu)化
這篇文章主要介紹了MySQL?原理與優(yōu)化之Limit?查詢優(yōu)化,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
mysql把一段數(shù)據(jù)變成一個(gè)臨時(shí)表
這篇文章主要介紹了mysql把一段數(shù)據(jù)變成一個(gè)臨時(shí)表,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02
MySQL授權(quán)用戶訪問數(shù)據(jù)操作方式
用戶授權(quán)操作可以控制數(shù)據(jù)庫用戶對數(shù)據(jù)庫對象的訪問權(quán)限,本文就來介紹MySQL授權(quán)用戶訪問數(shù)據(jù)操作方式,感興趣的可以了解一下2023-10-10

