權限表使用聯合主鍵嗎_天天寫 order by,你知道Mysql底層執行流程嗎?

6ff714ca651f44780c4d968c6d7b349e.gif

前言

  • 在實際的開發中一定會碰到根據某個字段進行排序后來顯示結果的需求,但是你真的理解order by在 Mysql 底層是如何執行的嗎?
  • 假設你要查詢城市是蘇州的所有人名字,并且按照姓名進行排序返回前 1000 個人的姓名、年齡,這條 sql 語句應該如何寫?
  • 首先創建一張用戶表,sql 語句如下:
CREATE?TABLE?user?(
??id?int(11)?NOT?NULL,
??city?varchar(16)?NOT?NULL,
??name?varchar(16)?NOT?NULL,
??age?int(11)?NOT?NULL,
??PRIMARY?KEY?(id),
??KEY?city?(city)
)?ENGINE=InnoDB;
  • 則上述需求的 sql 查詢語句如下:
select?city,name,age?from?user?where?city='蘇州'?order?by?name?limit?1000;
  • 這條 sql 查詢語句相信大家都能寫出來,但是你了解它在 Mysql 底層的執行流程嗎?今天陳某來大家聊一聊這條 sql 語句是如何執行的以及有什么參數會影響執行的流程。
  • 本篇文章分為如下幾個部分進行詳細的闡述:
  1. 全字段排序
  2. rowid 排序
  3. 全字段排序 VS rowid 排序
  4. 如何避免排序

全字段排序

  • 前面聊過索引能夠避免全表掃描,因此我們給city這個字段上添加了索引,當然城市的字段很小,不用考慮字符串的索引問題,之前有寫過一篇關于如何給字符串的加索引的文章,有不了解朋友看一下這篇文章:Mysql 性能優化:如何給字符串加索引?
  • 此時用Explain來分析一下的這條查詢語句的執行情況,結果如下圖:d3e47078ff03c6097b35909dceb9b55f.png
  • Extra這個字段中的Using filesort表示的就是需要排序,MySQL 會給每個線程分配一塊內存用于排序,稱為sort_buffer
  • 既然使用了索引進行查詢,我們來簡單的畫一下city這棵索引樹的結構,如下圖:cf5f31ee2a7cfce8779fca9288fc5124.png
  • 從上圖可以看出,滿足city='蘇州'是從ID3IDX這些記錄。
  • 通常情況下,此條 sql 語句執行流程如下:
  1. 初始化 sort_buffer,確定放入 name、city、age 這三個字段。
  2. 從索引 city 找到第一個滿足city='蘇州'條件的主鍵id,也就是圖中的ID3
  3. 主鍵id索引取出整行,取namecityage三個字段的值,存入sort_buffer中。
  4. 從索引city取下一個記錄的主鍵 id。
  5. 重復步驟 3、4 直到 city 的值不滿足查詢條件為止,對應的主鍵 id 也就是圖中的IDX
  6. sort_buffer中的數據按照字段name做快速排序。
  7. 按照排序結果取前 1000 行返回給客戶端。
我們稱這個排序過程為全字段排序,執行的流程圖如下:1238934cc9c325a8bcd35db4612d21a5.png圖中按name排序這個動作,可能在內存中完成,也可能需要使用外部排序,這取決于排序所需的內存和參數sort_buffer_sizesort_buffer_size:就是 MySQL 為排序開辟的內存(sort_buffer)的大小。如果要排序的數據量小于 sort_buffer_size,排序就在內存中完成。但如果排序數據量太大,內存放不下,則不得不利用磁盤臨時文件輔助排序。

rowid 排序

  • 在上面這個算法過程里面,只對原表的數據讀了一遍,剩下的操作都是在sort_buffer臨時文件中執行的。但這個算法有一個問題,就是如果查詢要返回的字段很多的話,那么sort_buffer里面要放的字段數太多,這樣內存里能夠同時放下的行數很少,要分成很多個臨時文件,排序的性能會很差
  • 所以如果單行很大,這個方法效率不夠好。
  • 我們可以修改一個max_length_for_sort_data這個參數使其使用另外一種算法。max_length_for_sort_data,是 MySQL 中專門控制用于排序的行數據的長度的一個參數。它的意思是,如果單行的長度超過這個值,MySQL 就認為單行太大,要換一個算法。
  • citynameage 這三個字段的定義總長度是36,我把max_length_for_sort_data設置為 16,我們再來看看計算過程有什么改變。設置的 sql 語句如下:
SET?max_length_for_sort_data?=?16;
  • 新的算法放入 sort_buffer 的字段,只有要排序的列(即 name 字段)和主鍵 id。

  • 但這時,排序的結果就因為少了 city 和 age 字段的值,不能直接返回了,整個執行流程就變成如下所示的樣子:

  1. 初始化sort_buffer,確定放入兩個字段,即nameid
  2. 從索引 city 找到第一個滿足city='蘇州'條件的主鍵id,也就是圖中的ID3
  3. 主鍵id索引取出整行,取 name、id 這兩個字段,存入 sort_buffer 中。
  4. 從索引city取下一個記錄的主鍵 id。
  5. 重復步驟 3、4 直到 city 的值不滿足查詢條件為止,對應的主鍵 id 也就是圖中的IDX
  6. sort_buffer中的數據按照字段name做快速排序。
  7. 遍歷排序結果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三個字段返回給客戶端。

這個執行流程的示意圖如下,我把它稱為rowid排序af6686f05f054dd08de044163fd0a01e.png

對比全字段排序rowid排序多了一次回表查詢,即是多了第7步的查詢主鍵索引樹。

全字段排序 VS rowid 排序

  • 如果 MySQL 實在是擔心排序內存太小,會影響排序效率,才會采用 rowid 排序算法,這樣排序過程中一次可以排序更多行,但是需要再回到原表去取數據。
  • 如果 MySQL 認為內存足夠大,會優先選擇全字段排序,把需要的字段都放到 sort_buffer 中,這樣排序后就會直接從內存里面返回查詢結果了,不用再回到原表去取數據。
  • 這也就體現了 MySQL 的一個設計思想:如果內存夠,就要多利用內存,盡量減少磁盤訪問
  • 對于 InnoDB 表來說,rowid 排序會要求回表多造成磁盤讀,因此不會被優先選擇。

如何避免排序

  • 其實,并不是所有的order by語句,都需要排序操作的。從上面分析的執行過程,我們可以看到,MySQL 之所以需要生成臨時表,并且在臨時表上做排序操作,其原因是原來的數據都是無序的
  • 如果能夠保證從city這個索引上取出來的行,天然就是按照 name 遞增排序的話,是不是就可以不用再排序了呢?
  • 因此想到了聯合索引,創建(city,name)聯合索引,sql 語句如下:
alter?table?user?add?index?city_user(city,?name);
  • 此時的索引樹如下:6baba8b98e910721f184d2b165d77805.png
  • 在這個索引里面,我們依然可以用樹搜索的方式定位到第一個滿足city='蘇州'的記錄,并且額外確保了,接下來按順序取“下一條記錄”的遍歷過程中,只要 city 的值是蘇州,name 的值就一定是有序的。
  • 按照上圖,整個查詢的流程如下:
  1. 從索引(city,name)找到第一個滿足 city='蘇州'條件的主鍵 id。
  2. 到主鍵 id 索引取出整行,取 name、city、age 三個字段的值,作為結果集的一部分直接返回。
  3. 從索引(city,name)取下一個記錄主鍵 id。
  4. 重復步驟 2、3,直到查到第 1000 條記錄,或者是不滿足 city='蘇州'條件時循環結束。
對應的流程圖如下:2bc84c5bcc423727bd164a6f5dca004b.png可以看到,這個查詢過程不需要臨時表,也不需要排序。接下來,我們用 explain 的結果來印證一下。eaedb4beeaa2fc7e4b5e55d2c6a559d2.png從圖中可以看到,Extra字段中沒有Using filesort了,也就是不需要排序了。而且由于(city,name)這個聯合索引本身有序,所以這個查詢也不用把 4000 行全都讀一遍,只要找到滿足條件的前 1000 條記錄就可以退出了。也就是說,在我們這個例子里,只需要掃描 1000 次。難道僅僅這樣就能滿足了?此條查詢語句是否能再優化呢?de3cf1dd5e9db1becbe6dc9b3f6bad90.png朋友們還記得覆蓋索引嗎?覆蓋索引的好處就是能夠避免再次回表查詢,不了解的朋友們可以看一下陳某之前寫的文章:Mysql 性能優化:如何使用覆蓋索引?。我們創建(city,name,age)聯合索引,這樣在執行上面的查詢語句就能使用覆蓋索引了,避免了回表查詢了,sql 語句如下:
alter?table?user?add?index?city_user_age(city,?name,?age);
  • 此時執行流程圖如下:f98367cde4c078b674bb0c83ea094dc2.png
  • 當然,覆蓋索引能夠提升效率,但是維護索引也是需要代價的,因此還需要權衡使用。

總結

  • 今天這篇文章,我和你介紹了 MySQL 里面order by語句的幾種算法流程。
  • 在開發系統的時候,你總是不可避免地會使用到 order by 語句。心里要清楚每個語句的排序邏輯是怎么實現的,還要能夠分析出在最壞情況下,每個語句的執行對系統資源的消耗,這樣才能做到下筆如有神,不犯低級錯誤。

7ca92a4383241f6eaf2e32ae047a8c4e.png

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

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

相關文章

nodejs簡介

nodejs是啥? Node.js是運行在服務端的JavaScript。 Node.js是一個基于Chrome JavaScript運行時建立的一個平臺。 Node.js是一個事件驅動I/O服務端JavaScript環境,基于Google的V8引擎,V8引擎執行Javascript的速度非常快,性能非常…

jumpserver v0.4.0 基于 CenOS7 的安裝詳解

標簽(linux): jumpserver 筆者Q:972581034 交流群:605799367。有任何疑問可與筆者或加群交流 首首先使用Jumpserver前要理解清楚這三個用戶關系: 1.用戶: 是指你在web上創建的用戶,會在跳板機上創建這個用戶,作用就是用…

Node.js中事件的循環

Node.js 事件循環 Node.js 是單進程單線程應用程序,但是通過事件和回調支持并發,所以性能非常高。 Node.js 的每一個 API 都是異步的,并作為一個獨立線程運行,使用異步函數調用,并處理并發。 Node.js 基本上所有的事…

python爬boss網站_python之requests爬蟲Boss數據

python之requests爬蟲Boss數據需要用到的庫:reqeusts、lxml沒有的可以用直接下載pip install requestspip install lxm這里以python崗位,地點北京為例爬取的數據就是崗位名稱、薪資、地點 首先導入需要用到的模塊import requestsfrom lxml import etree崗…

live555源代碼簡介

文章出自:http://blog.csdn.net/imliujie/archive/2008/01/30/2072657.aspx live555源代碼簡介liveMedia項目的源代碼包括四個基本的庫,各種測試代碼以及IVE555 Media Server。四個基本的庫分別是UsageEnvironment&TaskScheduler,groups…

并發無鎖隊列學習(單生產者單消費者模型)

1、引言 本文介紹單生產者單消費者模型的隊列。依據寫入隊列的內容是定長還是變長,分為單生產者單消費者定長隊列和單生產者單消費者變長隊列兩種。單生產者單消費者模型的隊列操作過程是不須要進行加鎖的。生產者通過寫索引控制入隊操作,消費者通過讀索…

ecshop 收貨人信息電話必填改為手機必填

首先通過在flow.dwt中,查找flow.php?stepconsignee中的關鍵字 consignee(結算中心)查找所在模板/Library/consignee.lbi 大概57行 把必填去掉,其次 在js/shopping_flow.js里邊注釋掉 if (Utils.isEmpty(frm.elements[‘tel’].v…

流媒體傳輸協議

1.流媒體( Streaming Media) 1.1流媒體概念 流媒體技術是網絡技術和多媒體技術發展到一定階段的產物。術語流媒體既可以指在網上傳輸連續時基媒體的流式技術,也可以指使用流式技術的連續時基媒體本身。在網上傳輸音頻、視頻等多媒體信息目前主要有兩種方式:下載和流…

關閉瀏覽器網頁觸發事件_淺析瀏覽器渲染和 script 加載

前言前端代碼離不開瀏覽器環境,理解 js、css 代碼如何在瀏覽器中工作是非常重要的。如何優化渲染過程中的回流,重繪?script 腳本在頁面中是怎么個加載順序?了解這些對前端性能優化起著非常大的作用。借著這篇文章,讓自…

Open vSwitch實驗常用命令

1. 基本架構 ovs-vsctl: 管理ovsdb-server的配置,提供OVSDB的配置方法,包括創建和刪除網橋、端口等; ovs-ofctl: 提供ovs-vswitchd的流表配置方法; ovs-dpctl: 配置OVS內核模塊,提供緩存流表的操作方法&#xff1b…

記IOS8中碰到的一個JS bug

IOS8的JS版本過低導致 var id "123"; var temp1 {id, "left": "200"}; // error in IOS8 var temp2 {"id":id, "left": "200"};平時還是多寫ES5的代碼,es6的語法總能碰到兼容的坑。 改了好幾天。…

Emmet的html語法

Emmet的html語法 所有操作按下“tab”鍵即可瞬間完成 元素 1.在編輯器中輸入元素名稱,即可自動補全生成 HTML 標簽,即使不是標準的 HTML 標簽。 2.輸入:! 或者 html:5 或者 html:4s 或者 html:4t 將自動補全html基本結構 嵌套操作 1.使用…

RTP Payload Format for H.264 Video

H.264 RTP協議的封裝格式rfc3984 英文原版:http://tools.ietf.org/html/rfc3984 部分中文翻譯: H.264 視頻 RTP 負載格式 1. 網絡抽象層單元類型 (NALU) NAL單元1字節包頭負載 NALU 頭由一個字節組成, 它的語法如下: —————|0|1|2|3|4|5|6|7|------…

js字符串、數組和數字常用方法總結

https://github.com/AnHyun/blog/issues/3 一、string 常用方法: 1.substring(start開始位置的索引,end結束位置索引) 截取的位置不包含結束位置的字符,只寫一個參數表示從開始位置截取到最后,輸入負值時將負值變為0,哪個較小作為開始位置 va…

Oracle 存儲過程錯誤之PLS-00201: 必須聲明標識符

轉自:http://blog.csdn.net/u010678947/article/details/20702149 錯誤: ORA-06550: 第 1 行, 第 7 列: PLS-00201: 必須聲明標識符ZUO.PROCE_TESTORA-06550: 第 1 行, 第 7 列: PL/SQL: Statement ignored 解決方法: (1&#x…

mysql中如何把兩個查詢結果列數不同并成一張表_MySQL

引言本文整理了MySQL相關的知識,方便以后查閱。 基礎架構下圖是 MySQL 的一個簡要架構圖,從下圖你可以很清晰的看到用戶的 SQL 語句在 MySQL 內部是如何執行的。 先簡單介紹一下下圖涉及的一些組件的基本作用幫助大家理解這幅圖。 - 連接器: …

JavaWeb筆記01-XML

今日內容 XML 概念語法解析 XML: 概念: Extensible Markup Language 可擴展標記語言 可擴展:標簽都是自定義的.<user><student> 功能 存儲數據 配置文件在網絡中傳輸 一個故事 由于瀏覽器之間的競爭,導致HTML發展的十分不順利 用戶:唉,這怎么報錯了呢?…

centos下如何使用sendmail發送郵件

最近在實施服務端日志監控腳本&#xff0c;需要對異常情況發送郵件通知相關責任人&#xff0c;記錄下centos通過sendmail發送郵件的配置過程。一. 安裝sendmail和mailx1、安裝sendmail&#xff1a;1): centos下可以安裝命令:yum install -y sendmail service sendmail start yu…

H.263 H.263+ Payload Type

h263 rtp協議封裝協議英文版&#xff1a;rfc4629:http://tools.ietf.org/html/rfc4629 以下文章是部分參考翻譯&#xff1a; 文章出處&#xff1a; http://blog.csdn.net/zblue78/archive/2009/04/09/4059414.aspxGeneral H.263 Payload Header The H.263 payload header is s…

OC 中 load 方法和 initialize 方法的異同

(void)load; 當類對象被引入項目時, runtime 會向每一個類對象發送 load 消息load 方法會在每一個類甚至分類被引入時僅調用一次,調用的順序:父類優先于子類, 子類優先于分類load 方法不會被類自動繼承 (void)initialize; 也是在第一次使用這個類的時候會調用這個方法 轉載于:h…