背景:有個單詞表,隨機顯示3個單詞
建表語句與初始化語句
mysql> create tablewords
(
id
int(11) not null auto_increment,
word
varchar(64) default null,
primary key (id
)
) engine=innodb;
delimiter ;;
create procedure idata()
begin
declare i int;
set i=0;
while i<10000 do
insert into words(word) values(concat(char(97+(i div 1000)), char(97+(i % 1000 div 100)), char(97+(i % 100 div 10)), char(97+(i % 10))));
set i=i+1;
end while;
end;;
delimiter ;
call idata();
隨機顯示3個單詞用什麼sql
一般可能會用到的 order by rand()
select word from words order by rand() limit 3;
我們用explain查詢語句執行。
關注extra欄位using temporary表示使用臨時表,using filesort表示需要執行排序。
對於記憶體臨時表的排序來說,(ps:記憶體臨時表的預設引擎是memory)
innodb為了減少磁碟的訪問,優先選擇全欄位排序
記憶體表memory回表無須訪問磁碟,優先選擇rowid排序。
語句執行流程:
建立臨時表,引擎為memory,有r,w兩個字段,沒有建索引
2.從words表中,俺主鍵順序取出所有的word值,對於每乙個word值,呼叫rand()函式生成乙個大於0小於1的隨機小數。把隨機小數與word存入臨時表的rw欄位
3.臨時表安裝r欄位排序。
4.初始化sort_buffer,(由於用rowid排序)只有這個r欄位與預設rowid欄位。
5.從臨時表中取出r值與rowid,存入sort_buffer。(這個過程設計記憶體表的全表掃瞄會更加掃瞄行數。)
6.sort_buffer根據r值排序。
7.排序後取前三個結果rowid,到記憶體表取w欄位返回給客戶端。
小結:order by rand()使用了記憶體臨時表,記憶體臨時表排序的時候使用了rowid排序。
磁碟臨時表
tmp_table_size這個引數限制了記憶體臨時表的大小。預設值是16m,如果臨時表的大小超過了tmp_table_size這個值。則記憶體臨時表會轉為磁碟臨時表
磁碟臨時表預設引擎使用innodb。由internal_tmp_disk_storage_engine控制的。
為了復現這個過程,把tmp_table_size=1024 sort_buffer_size=32768
max_length_for_sort_data=16
set tmp_table_size=1024;
set sort_buffer_size=32768;
set max_length_for_sort_data=16;
/* 開啟 optimizer_trace,只對本執行緒有效 */
set optimizer_trace=『enabled=on』;
/* 執行語句 */
select word from words order by rand() limit 3;
/* 檢視 optimizer_trace 輸出 */
select * frominformation_schema
.optimizer_trace
\g
檢視optimizer_trace的結果。
number_of_tmp_files為0,不需要臨時檔案。
mysql5.6後引入了優先佇列排序演算法(大小根堆演算法,不需要將所有的資料進行排序。)
select city,name,age from t where city=『杭州』 order by name limit 1000 ;
之前我們只有三條記錄,需要維護堆的大小只有3行。
但是現在我們將記錄行擴充套件到了1000行,超過了設定的sort_buffer_size,只能選用歸併排序。
小結:order by rand()不管用什麼臨時表,計算過程都比較複雜。需要掃瞄大量的行,且排序過程消耗大量的資源。
隨機排序方法
隨機演算法1:
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.獲取這個表主鍵id的最大值m與最小值n
2.用隨機函式生成乙個m~n之間的數x
3.取不小於x的第乙個id的行。
缺點id如果不連續,則m~n之前的空洞會影響其他行的概率。
隨機演算法2
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;
1.取整個表的行數c。
2.用隨機函式取得1~c之前的隨機值
3.再用limit y,1取得一行
mysql處理limit y,1的做法是按順序讀入,丟棄前y個,然後把下個作為結果返回。掃瞄行數會加上y。 掃瞄行數增加了,但是解決了概率平均的問題。
如何正確地寫出單例模式
發表於 2014 08 28 分類於 程式設計 閱讀次數 50513 單例模式算是設計模式中最容易理解,也是最容易手寫 的模式了吧。但是其中的坑卻不少,所以也常作為面試題來考。本文主要對幾種單例寫法的整理,並分析其優缺點。很多都是一些老生常談的問題,但如果你不知道如何建立乙個執行緒安全的單例,不知道...
如何正確地部署防火牆?
防火牆在實際的部署應用過程當中,經常部署在閘道器的位置,也就是經常部署在網內和網外的乙個 中間分隔點 上,而就是在這樣乙個部署的環境中,也還存在著多種方式,且存在著許多 陷阱 本文將對幾種方式進行分析。請閱讀全文 防火牆在實際的部署應用過程當中,經常部署在閘道器的位置,也就是經常部署在網內和網外的乙...
如何正確地在MDK中使用關鍵
筆者在做移植時,將embest ide環境下的例程移到realview mdk的過程中,曾經遇到這樣乙個問題 在生成工程時,編譯全部通過,但在鏈結時提示許多符號未定義!如果讀者也遇到過這個問題,請繼續看下去,如果鏈結時提示未定義的變數是一些內聯函式 即使用了關鍵字 inline 那麼就是筆者遇到的問...