日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql 排序后 下一条记录_Mysql如何使用order by工作

發(fā)布時間:2024/9/19 数据库 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql 排序后 下一条记录_Mysql如何使用order by工作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
日常開發(fā)中,我們經常要進行字段的排序,但是我們大多不知道排序是如何執(zhí)行的,今天我們就說說order by?的執(zhí)行邏輯,CREATE TABLE `t` (`id` int(11) NOT NULL,`city` varchar(16) NOT NULL,`name` varchar(16) NOT NULL,`age` int(11) NOT NULL,`addr` varchar(128) DEFAULT NULL, PRIMARY KEY (`id`),KEY `city` (`city`)) ENGINE=InnoDB如果我們執(zhí)行下面語句是如何進行排序的呢select city,name,age from t where city='杭州' order by name limit 1000 ;

全字段排序

之前我們說過,為了避免全表掃描,我們在city字段上加索引,現在我使用explain命令查看這個語句的執(zhí)行情況

我們發(fā)現extra這個子彈中的Using?filesort?表是要進行排序,Mysql為每一個線程分配一塊內存用于排序,這個叫sort_buffer.

如圖所示,通常情況下,這個語句的流程如下
  • 初始化sort_buffer,確定放入name,city,age這三個字段

  • 從索引中找到第一個杭州的主鍵id

  • 然后到主鍵id取出整行(name,age,city),存入sort_buffer中,

  • 從索引字段中去下一個記錄的id

  • 重復3,4步驟,直到不滿足條件

  • 對sort_buffer中的name字段進行排序

  • 按照排序結果取前1000條返回給客戶端

  • 我們把上面的排序叫全字段排序,執(zhí)行流程如下

    圖中nama的排序有可能在內存中完成,也就可能使用外部排序,這個取決于所需的內存和參數sort_buffer_sizesort_buffer_size,就是Mysql為排序開辟的內存的大小,如果排序的數據量小于sort_buffer_size,排序就在內存中排序,如果大于內存大小,就會使用磁盤的臨時文件輔助排序,我們可以使用下面方法,來確定一個排序語句是否使用了臨時文件/* 打開optimizer_trace,只對本線程有效 */SET optimizer_trace='enabled=on';/* @a保存Innodb_rows_read的初始值 */select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Innodb_rows_read';/* 執(zhí)行語句 */select city, name,age from t where city='杭州' order by name limit 1000;/* 查看 OPTIMIZER_TRACE 輸出 */SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G/* @b保存Innodb_rows_read的當前值 */select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';/* 計算Innodb_rows_read差值 */select @b-@a;這個方法是通過查詢OPTIMIZER_TRACE結果來確認,你可以從number_of_tmp_files看到是否使用了臨時文件,

    number_of_tmp_files表示使用的臨時文件數,我們可以理解為mysql在排序的時候把數據分成了12份,每一份單獨排序后存在這些臨時文件中,然后把12有序文件再合并一個有序的大文件。如果number_of_tmp_files=0,說明使用的是內存進行排序,我們看到examined_rows=4000.說明參與排序的行數是4000.sort_mode里面packed_additional_fields的意思是說,排序過程對字段進行了緊湊處理,如果字段定義為varchar(16),實際上排序過程中也就是按照字段的實際長度進行排序。注意的是這里我們設置internal_tmp_disk_storage_engin設置成MyiSAM,否則select @a-@b的結果會顯示4001,這是因為如果是innodb,數據從臨時表取出也會進行加1操作。

    rowid排序

    我們可以看到如果查詢的字段很多的話,那么sort_buffer存放的字段數太多,就會使用臨時文件進行排序,因此造成了很大的浪費,此時mysql任務排序的單行長度會怎么做呢,

    首先我要知道如何判斷單行長度太大,如下參數

    SET max_length_for_sort_data = 16;我們看到city,name,age總長度為36,遠遠大于16,因此我們判定單行長度過大,Mysql就會使用另外一種算法進行排序.新的算法放入sort_buffer的字段,只需要排序的列和主鍵id,但是這個時候,并不能直接返回排序的結果,而是要回表查詢整行。
  • 初始化sort_buffer,確定放入兩個字段,即name和id

  • 從索引city中找到第一個滿足的條件主鍵id

  • 再到主鍵id索引中獲取整行,取出name,id兩個字段,存入sort_buffer

  • 在從索引city中到下一個記錄id

  • 重復3,4步驟,知道不滿足條件位置

  • 對sort_buffer進行name排序

  • 遍歷排序結果,取出前1000條記錄,?并按照id再到原表獲取city,name,age字段返回給客戶端

  • 上面的排序算法叫做rowid排序,對比之前的流程,我們發(fā)現不同于之前的是多了一次訪問表T的步驟,我們可根據上圖的步驟,想一下select @b-@a,結果是啥,

    首先,圖中examined_rows的值還是4000,表示用于排序的數據是4000行,但是select@b-@a這個語句的值變成5000.因為這個時候除了排序過程外,在排序完成后,還要根據id取原表取值,由于語句是limit 1000,因此會多讀1000行。我們也可以發(fā)現sort_mode=sort_key,rowid,表示參與排序只要name和id兩個字段.

    numner_of_tmp_files=10,那是因為參與排序的行數雖然仍然是4000行,但是每一個行都變小了,因此需要排序的總數量變小了,需要的臨時文件相應變少了。

    全字段排序和rowid排序

    如果msyql實在是擔心內存太小,會影響排序效率,才會采取rowid算法,這樣排序過程中一次可以排序更多行,但是需要回表取數據。

    如果任務內存足夠大,會優(yōu)先選擇全字段排序,把需要的字段放入到sort_buffer,這樣就會直接從內存里面返回查詢結果,不再回表查詢數據,

    對于innodb來說,rowid排序要求回表造成磁盤讀,因此不會優(yōu)先選擇,

    看到這里,是不是所有的order by都要進行排序操作,如果不排序就不能獲取正確的數據呢,其實,并不是多有的order by?語句,都需要排序,MySQL之所以要使用臨時文件排序,是因為原來的數據都是無序的,因此如果本身的從city索引獲取的數據就是按照name進行排序的,是不是就可以不用再進行排序呢.

    實時上,確實是這樣的,我們現在來建立一個(city,name)聯(lián)合索引,對應的sql語句如下alter?table?add?index city_user(city,name)

    從上面索引看出,我們依然可以用樹索引定位到第一個city='杭州'的數據,并且額外確保了,接下來按順序去下一條記錄,只要city=杭州,name就是有序的
  • 從索引(city,name)找到一個滿足city=杭州條件的主鍵id

  • 到主鍵id取到整行,取name,age?,city,作為結果的一部分直接返回

  • 從索引(city,name)取下一個主鍵id

  • 重復2,3步驟,直達查詢到1000記錄,或者不滿足條件循環(huán)結束

  • 可以看到這個查詢過程不需要臨時表,也不需要排序,接下來,我們用explain結果驗證一下

    發(fā)現extra的字段中沒有using filesort,也就是不用排序,而且由于(city,name)索引本身就是索引有序,所以這個查詢不需要查詢4000行數據,只要找到前1000條數據就可以了。

    到這里,我是不是還可以進行優(yōu)化呢,當然是可以的,我們可以使用覆蓋索引,覆蓋索引是指,索引上的信息足夠滿足查詢請求,不需要再回到主鍵索引上取數據,

    我們按照覆蓋索引的概念,建立(city,name.age)聯(lián)合索引,

    alter?table?add?index?(city,name,age)這里如果city相等,還是按照name字段進行遞增進行排序的,此時查詢語句也就不需要排序了,
  • 從索引(city,name,age)中找到滿足city=杭州的記錄,取出city,name,age這三個字段的值,作為結果集的一部分返回

  • 從索引(city,name,age)取下一個記錄,同樣取出三個字段的值,作為結果返回

  • 重復2步驟,直到查到1000記錄,或者不滿足city=杭州的條件結束循環(huán)

  • 然后我們再看explain可以看到Extra字段里面多了Using index ,表示使用了覆蓋索引,性能上會快很多.

    果對您有一絲絲幫助,麻煩點個關注,也歡迎轉發(fā),謝謝

    掃碼關注

    總結

    以上是生活随笔為你收集整理的mysql 排序后 下一条记录_Mysql如何使用order by工作的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。