MySQL 17 如何正確地顯示隨機消息?

假設有一個場景,一個英語學習APP首頁有一個隨機顯示單詞的功能,用戶每次訪問首頁的時候,都會隨機滾動顯示三個單詞。

已知表里有10000條記錄,來看看隨機選擇3個單詞有什么方法,又存在什么問題。

建表語句:

mysql> CREATE TABLE `words` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

?

粉絲福利!

需要全套2025最新Java面試筆記【點擊此處即可】即可免費獲取!

內存臨時表

首先,可以使用order by rand()來實現:

 
select word from words order by rand() limit 3;

該語句的執行情況:

Extra字段顯示Using temporary,表示需要使用臨時表;Using filesort,表示需要執行排序操作。

為了更好地分析,這里引用上一篇文章中全字段排序和rowid排序的流程圖:

那么對于臨時內存表的排序來說,它會選擇哪一種算法呢?

對于內存表,回表過程只是簡單根據數據行的位置,直接訪問內存得到數據,不會導致多訪問磁盤。這種情況下,優化器會考慮用于排序的行的大小,所以MySQL會選擇rowid排序方法。

因此該語句的執行流程為:

  • 創建一個臨時表,臨時表用的是MEMORY引擎,表里有兩個字段,第一個是double類型,記為字段R,第二個是varchar(64)類型,記為字段W,臨時表沒有建索引

  • 從words表按主鍵順序取出所有的word值,對于每一個word,調用rand()生成一個大于0小于1的隨機數,并把這個隨機數和word分別存入臨時表的R和W字段中,該步驟掃描行數10000行;

  • 初始化sort_buffer,里面有兩個字段,一個是double類型,另一個是整型;

  • 從內存臨時表中逐行取出R值和“位置信息”(后面解釋),分別存入sort_buffer中的兩個字段里,這個過程要對內存臨時表做全表掃描,該步驟掃描行數10000行;

  • 在sort_buffer中根據R值進行排序;

  • 排序完成后,取出前三個結果的位置信息,依次到內存臨時表取出word值,返回給客戶端。該步驟訪問三行,因此總掃描行數變為20003。

完整的排序執行流程圖:

位置信息本質是數據庫引擎用來快速定位“一行數據”的唯一標識,一般稱為rowid,在不同引擎中其具體形式不同:

  • 對于有主鍵的InnoDB表,rowid就是主鍵ID;

  • 對于沒有主鍵的InnoDB表,rowid是由系統生成的6字節的主鍵;

  • 對于MEMORY引擎,由于其不是索引組織表,可以認為是一個數組,因此rowid其實是數組的下標。

因此,可以總結:order by rand()使用了內存臨時表,內存臨時表排序時候使用了rowid排序方法。

磁盤臨時表

并不是所有的臨時表都是內存表,參數tmp_table_size配置限制了內存臨時表的大小,默認是16M,如果臨時表大小超過了配置值,內存臨時表會轉成磁盤臨時表。

磁盤臨時表使用的引擎默認是InnoDB,是由參數internal_tmp_disk_storage_engine控制。

當使用磁盤臨時表,對應是一個沒有顯式索引的InnoDB表的排序過程。這里把tmp_table_size設為1024,sort_buffer_size設為32768,max_length_for_sort_data設為16,查看OPTIMIZER_TRACE,得到部分結果如下:

對于結果:

  • sort_mode里是rowid,這個符合預期,因為max_length_for_sort_data設為16,小于word字段的長度定義,因此使用rowid算法,參與排序是隨機值R和6字節的主鍵;

  • number_of_tmp_files=0,沒有用到臨時文件是因為這個語句的排序用的是MySQL 5.6版本引入的優先隊列排序算法。

對于取R值最小的3個rowid的目標,如果使用歸并排序,在算法結束后已經將10000行數據都排好序了,其實浪費了比較多的計算量,而使用優先隊列算法就可以精確只得到三個最小值,其執行流程為:

  • 對于10000個準備排序的(R,rowid),取前三行構造大根堆

  • 取下一行(R',rowid'),與堆頂R比較,如果R'小于R,將堆頂彈出,放入(R',rowid');

  • 重復上一步,直到第10000個數據完成比較;

  • 取出堆中的rowid,去臨時表里拿到word字段。

在OPTIMIZER_TRACE結果中,filesort_priority_queue_optimization里的chosen=true,就表示使用了優先隊列排序算法。

那為什么上篇文章的查詢語句(見下)沒有使用優先隊列呢?

 
select city,name,age from t where city='杭州' order by name limit 1000 ;

是因為如果使用優先隊列,需要維護的堆的大小是1000行的(name,rowid),超過了設置的sort_buffer_size大小,所以只能使用歸并排序算法。

不過,不論是使用內存臨時表還是磁盤臨時表,order by rand()這種方法都會讓計算過程非常復雜,需要大量的掃描行數。

隨機排序方法

把問題簡化一下,如果只隨機選擇一個word值,那么思路為:

  • 取表的主鍵id的最大值M和最小值N;

  • 用隨機函數生成一個最大值與最小值之間的數;

  • 取不小于X的第一個ID的行。

我們把這個算法稱為算法1,其執行語句為:

 
mysql> select max(id),min(id) into @M,@N from t ;
set @X= floor((@M-@N+1)*rand() + @N);
select * from t where id >= @X limit 1;

算法1的效率很高,因為取最值都不需要掃描索引,而第三步的查詢也能利用索引快速定位,可以認為總共就掃描了3行。但這個算法并不嚴格滿足要求,因為ID中間可能有空洞,那么選擇不同行的概率不一樣,并不是真正的隨機。

比如4個id,分別是1、2、4、5,如果按照上面的方法,那么取到id=4的概率是取得其他行概率的兩倍。

為了做到嚴格隨機,可以用下面流程:

  • 取得整個表的行數C;

  • 取得;

  • limit Y,1取一行。

我們把這個算法稱為算法2,其執行語句為:

 
mysql> select count(*) into @C from t;
set @Y = floor(@C * rand());
set @sql = concat("select * from t limit ", @Y, ",1");
prepare stmt from @sql;
execute stmt;
DEALLOCATE prepare stmt;

MySQL處理limit Y,1時會按順序一個一個讀出來,丟掉前Y個后將下一個記錄返回,因此需要掃描Y+1行,算上計算行數掃描的C行,總共掃描C+Y+1行,執行代價高于算法1,但小于order by rand()

那么按照算法2的思路,隨機取3個word值的做法為:

  • 取得整個表的行數C;

  • 根據相同的隨機方法取得Y1,Y2,Y3;

  • 執行三個limit Y,1取得三行數據。

其執行語句為:

 
mysql> select count(*) into @C from t;
set @Y1 = floor(@C * rand());
set @Y2 = floor(@C * rand());
set @Y3 = floor(@C * rand());
select * from t limit @Y1,1; //在應用代碼里面取Y1、Y2、Y3值,拼出SQL后執行
select * from t limit @Y2,1;
select * from t limit @Y3,1;

最后總結:對于隨機排序,盡量避免使用order by rand()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/89876.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/89876.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/89876.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

7-Zip 曝出兩個可導致拒絕服務的中危漏洞

研究人員在全球使用最廣泛的開源文件壓縮軟件7-Zip中新發現兩個漏洞(CVE-2025-53816和CVE-2025-53817)。這兩個漏洞影響7-Zip 25.0.0之前的所有版本,雖然不能實現遠程代碼執行,但可能引發內存損壞和拒絕服務(Denial of…

史上最簡單Conda+Ollama+Open-Webui安裝方法!

史上最簡單CondaOllamaOpen-Webui安裝方法 一、安裝Anaconda 1、到Anaconda官網下載conda_24.10.1 鏈接:https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Windows-x86_64.exe 2.雙擊安裝包,開始安裝 選擇All Users 切記安裝路徑不要選C盤&am…

Python-數據庫概念-pymysql-元編程-SQLAlchemy-學習筆記

序 欠4前年的一份筆記 ,獻給今后的自己。 數據庫 概念 數據庫:按照數據結構來組織、存儲、管理數據的倉庫。 誕生 計算機的發明是為了做科學計算的,而科學計算需要大量的數據輸入和輸出。 早期,可以使用打孔卡片的孔、燈泡的亮滅來…

Linux入門篇學習——借助 U 盤或 TF 卡拷貝程序到開發板上

借助 U 盤或 TF 卡拷貝程序到開發板上我們已經學習了怎么在 ubuntu 和 windows 上互傳文件,那么怎么把 ubuntu 或 win 上的程序拷貝到開發板呢,這里給大家介紹第一種方法,使用 U 盤或者 TF 卡來完成,如果大家使用的是 U 盤&#x…

【親測有效】防檢測插件playwright_stealth 2.X版本快速使用

這里寫自定義目錄標題核心方法apply_stealth_syncuse_sync和use_async一. playwright_stealth 2.0以上版本1.同步方法2.異步方法3.實例二.playwright_stealth 2.0以下版本playwright-stealth 是一個用于 Playwright 的庫,旨在幫助自動化腳本避開一些檢測機制&#x…

docker安裝與簡單項目上手

1.docker安裝 系統版本為almalinux9.6 首先添加一下docker的軟件安裝源(源選擇的阿里云,只要是rhel的系統都適用,無論是rockylinux還是almalinux還是紅帽企業版) dnf config-manager --add-repo https://mirrors.aliyun.com/doc…

計算機網絡基礎:從協議到通信全解析(大致框架)

本節重點:1.了解網絡發展背景,對局域網/廣域網的概念有基本認識2.了解網絡協議的意義,重點理解TCP/IP五層結構模型3.學習網絡傳輸的基本流程,理解封裝和解包分用一、計算機網絡發展背景:人與人之間是需要協同工作的&am…

PDF 編輯器:多文件合并 拆分 旋轉 順序隨便調 加水印 密碼鎖 頁碼背景

各位打工人、學生黨們,你們是不是也遇到過這種情況,領導甩來一個PDF讓你改,結果你搗鼓半天,發現這玩意兒根本動不了,簡直想原地爆炸!別急別急,今天就給你們安利一個辦公軟件——PDF編輯器&#…

【軟件基礎學習配置那些事 4-3】3ds Max2026 菜單欄常用命令-----文件、視圖、編輯、工具、組

3ds Max學習的筆記小知識!!!!!!!!后續都會補充添加!!!!(個人的一些學習筆記,如有不對,歡迎訂正&am…

網絡爬蟲的介紹

網絡爬蟲庫網絡爬蟲通俗來講就是使用代碼將HTML網頁的內容下載到本地的過程。爬取網頁主要是為了獲取網中的關鍵信息,例如網頁中的數據、圖片、視頻等。Python語言中提供了多個具有爬蟲功能的庫,下面將具的介紹。urlib庫:是Python自帶的標準庫&#xff0…

C# 編程實戰進階:字符串與字符串數組 (3)

目錄 1、給定一個整數數組 nums,將數組中的元素向右輪轉 k 個位置,其中 k 是非負數。 2、無重復字符的最長字符串 ,給定一個字符串 s 請你找出其中不含有重復字符的最長字符串的長度。 3、給定兩個字符串 s 和 t ,它們只包含小…

Python趣味算法:百錢百雞問題——雙重循環優化與算法效率分析

如何用Python解決中國古代數學難題?本文從暴力枚舉到高效優化,帶你領略算法之美,效率提升100倍! 看在每天堅持分享有趣知識的份上,點個關注吧(づ ̄ 3 ̄)づ 關注是我更新的動力 ̄︶ ̄? ̄︶ ̄?) 作者會分享更多涉及到各種編程語言的有趣知識!(^?^●)?? 目錄 …

JAVA_TWO-初識Java2

1.IDEA管理Java程序的結構2.idea編譯后的class文件在哪在工程out文件夾下。3.idea一些快捷鍵4.導入模塊File→New→Module from Existing Sources → 添加后綴.iml文件5.注釋單行注釋 //多行注釋 /* 注釋內容1注釋內容2 */文檔注釋 /** 注釋內容 */ (文檔注釋內容可…

二、Dify 版本升級教程(LInux-openeuler)

首先,你需要先按照好dify,然后才能升級,本文教程是基與Docker Compose 如果你還沒有安裝,可以看看這個教程。 一、Dify 私有部署、本地安裝教程(LInux-openeuler)_dify1.5版本部署-CSDN博客 安裝完成后&a…

Java 大視界 -- Java 大數據在智能安防門禁系統中的多生物特征融合識別與權限管理(280)

??親愛的朋友們,熱烈歡迎來到 青云交的博客!能與諸位在此相逢,我倍感榮幸。在這飛速更迭的時代,我們都渴望一方心靈凈土,而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識,也期待你毫無保留地分享獨特見解,愿我們于此攜手成長,共赴新程!?? 本博…

【Tools】Ubuntu24.04安裝詳細教程

00. 目錄 文章目錄00. 目錄01. Ubuntu 24.04簡介02. Ubuntu 24.04下載03. Ubuntu 24.04虛擬機創建04. Ubuntu 24.04安裝步驟05. Ubuntu 24.04常用軟件06. 附錄01. Ubuntu 24.04簡介 Ubuntu 24.04 LTS(代號“Noble Numbat”)是Canonical于2024年4月25日發…

linux基礎入門Ubuntu 22.04 系統中添加、刪除和授予用戶 sudo權限

在 Ubuntu 中,sudo 允許授權用戶以 root 級別權限執行任務,即使他們不知道 root 用戶密碼。這對于執行管理任務非常重要,因為它可以避免直接使用 root 用戶,從而減少系統被誤操作的風險,同時在企業生產中由于ubuntu系統…

npm : 無法加載文件 C:\Program Files\nodejs\npm.ps1

問題描述使用git bash, cmd運行npm都可以,但是用Power Shell運行npm,卻報錯:npm : 無法加載文件 C:\Program Files\nodejs\npm.ps1,因為在此系統上禁止運行腳本。有關詳細信息,請參閱 https:/go.microsoft.com/fwlink/…

【面經】實習經歷

文章目錄一、求職準備篇1.1提升技術水平1.1.1學什么?1.1.2怎么學?1.2做項目1.3做簡歷1.4找實習二、求職難度篇找實習難不難?筆試面試三、實習內容篇新人入職 -- 學會看代碼參與小需求實習日常實習到底難不難?四、總結 一、求職準備…

The Missing Semester of Your CS Education 學習筆記以及一些拓展知識(二)

文章目錄The Missing Semester of Your CS Education 學習筆記以及一些拓展知識Bash腳本筆記部分一些在Bash腳本中的常用命令補充常用標準輸入輸出命令常用環境變量(普通變量)控制命令常用系統時間信息獲取命令常用函數執行狀態控制命令常用腳本執行控制命令Bash腳本的創建和運…