理解SQL Server是如何執行查詢的 (2/3)

来源:http://www.cnblogs.com/Joe-T/archive/2016/08/11/5762047.html
-Advertisement-
Play Games

查詢執行的記憶體授予(Query Execution Memory Grant) 有些操作符需要較多的記憶體才能完成操作。例如,SORT、HASH、HAS聚合等。執行計劃通過操作符需要處理數據量的預估值(通過統計信息獲得的預估行數)、操作符類型和列大小來獲得所需要的記憶體總量。這個執行計劃的記憶體總量,也叫 ...


  • 查詢執行的記憶體授予(Query Execution Memory Grant)

    有些操作符需要較多的記憶體才能完成操作。例如,SORT、HASH、HAS聚合等。執行計劃通過操作符需要處理數據量的預估值(通過統計信息獲得的預估行數)、操作符類型和列大小來獲得所需要的記憶體總量。這個執行計劃的記憶體總量,也叫做記憶體授予(Memory Grant)。

    當多併發查詢的環境中,如果 查詢中有很多這種需要大量記憶體授予的操作符時,併發的執行計劃所需的記憶體授予總量可能會超過伺服器記憶體總量。SQL Server使用 資源信號量(Resource Semaphore) 來避免這種情況發生。當記憶體授予量超過可用記憶體量時,當前請求記憶體授予的查詢必須等待其它查詢完成並釋放出它占用的記憶體授予量。

    可通過 sys.dm_exec_query_memory_grants 查詢當前查詢記憶體授予的狀態。當查詢等待記憶體授予時會產生 Execution Warnings 事件。

    分配記憶體授予時可以會出現兩種情況:
    • 分配小於請求量的記憶體,查詢執行也能順利完成
    • 按請求量分配後,記憶體授予還是不夠用。這會導致操作符的數據被 Spill Out到磁碟(tempdb)。當這種情況發生事,會產生一些警告事件:
      • Exchange Spill 事件
      • Sort Warnings 事件
      • Hash Warning 事件

    對於查詢的記憶體授予記憶體是預先保留出來,而不是按需分配。所以在查詢執執行中可能出現實現使用的記憶體量比授予理要少的情況,這部分多來的記憶體會用做數據緩存。因為有資源信號量的限制,所以過大記憶體授予量會讓其它的查詢缺少授予記憶體而等待。

    SQL Server 中有一個相似的概念叫 查詢編譯資源信號量(query compile resource semaphore) 。它的作用機制跟資源信號量一樣,但是它只針對查詢的編譯行為生效。通常查詢編譯不會成為系統的瓶頸,如果發生很可能是查詢計劃重用出問題了。

    需要註意一點,並不是所有查詢都需要記憶體授予才能執行。包含排序、大型掃描、HASH連接和聚合等操作的複雜查詢才需要記憶體授予。在需要快速響應的系統中(如OLTP),如果發生記憶體授予問題,應當考慮從數據模型設計上做出調整。OLAP系統中的記憶體授予導致的延遲,通賞是正常的。

    關於記憶體的相關資源:理解SQL Server的查詢記憶體授予

  • 數據組織方式(Data Organization)

    SQL Server的數據組織方式有三種:
    1. 堆表:堆表中的數據是無序的。

    2. 聚集索引:聚集索引表中的數據是有序的,順序跟聚集索引鍵相關。聚集索引是B-Tree結構。

    3. 非聚集索引:它的數據是表中數據子集,並且數據是有序的,順序跟索引鍵相關

    SQL Server 2012之後,還有列存儲索引的數據組織方式。

  • 數據訪問(Data Access)

    前面說到,在執行樹葉級的操作符會訪問實際的數據行。它們訪問數據的操作符通常有三種:

  1. 掃描操作符(Scan Operator)
    顧名思義,它會訪問目標對象的所有數據行。像 Clustered Index Scan, Nonclustered Index Scan, Table Scan, Remote Index Scan and Remote Scan等操作符,它們都掃描方式,只對目標對象不一樣。通常掃描成本是很高的,應當將之做為最後的數據訪問。

  2. 查找操作符(Seek Operator)
    查找是通過一個(或多個)鍵列定位到一行(或多行)數據。查找可以實現範圍查找。查找只會出現在B-Tree上,即只有訪問聚集或者非聚集索才會有查找操作。堆表的數據是無序的,所以辦法通過某個鍵列定位到一行。查找是非常高效的,理想情況下應當做為數據訪問的首選方式。

  3. 書簽查找操作符(Bookmark Lookup Operator)
    通過一種特殊的值(即書簽)定位到數據行的操作符。書簽由資料庫引擎產生,不能人為指定,通常來自前一個操作符的輸出數據。書簽查找可以在任何數據組織方式的對象上發生。書簽查找操作符包括:Bookmark Lookup, Row ID Lookup (堆表上的查找)或者 Key Value Lookup (B-Tree上的查找)。

    嚴格來說,數據訪問操作符還有 Inserted Scan 和 Deleted Scan ,它們訪問是inserted和deleted 偽表。還有 Log Row Scan ,它從事務日誌讀取數據,而不是表。

    還有一個叫範圍掃描的概念。它是Seek操作符根據提供的兩個鍵值,掃描鍵值區間內的數據時的操作。

    現在回頭再去看查詢執行過程,我們就會明白數據訪問操作符是如何驅動整個執行計划進行迭代的了:執行樹根節點的操作符調用next()並沒著子節點逐級調用next(),直到到達到數據訪問操作符所在葉節點。而這些葉節點操作符通過讀取實際的數據和返回相關數據來實現next()介面。

  • 數據讀取 (Data Reading)

    數據訪問操作符會只從緩存池(Buffer Pool)讀取數據。如果緩存池中沒有所需要的數據,則需要將數據從存儲子系統讀取到緩存池。緩存池的數據被所有查詢共用。SQL Server會盡將儘可能多的數據緩存到緩存池,以備使用,直到用盡所有分配給SQL Server的記憶體量。可以通過 max server memory 選項控制緩存池的記憶體使用上限。無論是緩存池,還是磁碟IO,它們數據請求的都以8KB頁為單位。

    讓我們來看看掃描(Scan)數據訪問操作符是如何從堆上讀取數據的:

    1. 當在掃描操作符上每一次調用next()時,它去找到第一條數據並返回。SQL Server 內部的元數據信息表存儲著哪些頁屬於哪一個表的信息。數據訪問操作符會請求一個指針,此指針指向此數據頁在緩存池中副本的地址。如果此頁在豐緩存池中,則請求會被阻塞,直到頁被載入到緩存池。頁包含一個由單個數據記錄(Data Record)組成的數組。一個數據記錄不一定是一行,有些變長和大型數據列,會跨多個頁存儲。數據訪問操作符會定位頁上的第一行數據,然後讀取相關的列值並返回。操作符內部保存著當前的狀態信息,此狀態信息讓它能夠高效地返回到當前行的位置。
    2. 父級操作符取走返回的第一行數據。
    3. 當再次next(),數據訪問操作符會利用之前保存的狀態信息,快速地返回先前的位置,並向前移動一行定位到第二行,然後讀取相關的列值並返回。
    4. 父級操作符取走返回的第二行數據。
    5. 當再次調用next(),發現數據行已經讀取完了,操作符會向緩存池請求“下一頁”。操作符定位到新頁後,會定位到每一行數據,然後讀取相關的列值並返回。
    6. 父級操作符取走返回的數據。
    7. 如此往複,直到讀取完表中最後一頁的最一行數據。操作符的保存的狀態信息會指向“已超出表尾”,沒有可以返回的數據了。
    8. 當年數據訪問操作符完成它的工作,不再返回數據,父級操作符會開始處理自己的操作(如排序,HASH等)。
    9. 數據訪問操作能被重繞(Rewind)。例如,掃描操作符作為嵌套迴圈連接的內表,當從外表中輸入一行,就需要重繞內表的掃描操作符。重繞會導致數據訪問操作符重置內部狀態信息,即重新從第一頁的第一行開始讀取數據。

    作為對比,再來看看數據訪問操作如何在B-Tree上訪問數據的:

    1. 當第一次調用 next(),數據訪問操作符根據請求的鍵找到第一行數據並返回。SQL Server 有元數據表保存哪些頁屬於哪一個索引的信息。它不像堆表直接定位到第一頁,而是通過元數據獲取到B-Tree根頁的ID,然後根據ID引用緩存池中的根頁副本。根據搜索的鍵值,數據訪問操作符定位到B-Tree中包含第一行數據的葉級頁或者當前搜索鍵值“後續”行的葉級頁。在樹中檢索路徑經過的頁都需要從緩存池中讀取出來,同樣的,如果這些頁在緩存池中,就需要等待將它們從磁碟載入到緩存池中來。數據訪問操作符在葉級頁中檢索並定位到與查找鍵值匹配的行並返回。

    這個“後續”的意思,檢索B-Tree可以是雙向的(ASC OR DESC),而且根據查詢提供的鍵值,可能找到完全匹配鍵值的行,也可能找不到匹配鍵值的行。匹配鍵值的行可以沒有,但是葉級頁總是存在的,並且頁上的行與鍵值所指向的行在同一個B-Tree查找區間內,所以稱之為“後續”。“後續”行位於鍵值匹配行的前面還是後面,由檢索方向決定。檢索方向與創建索引時指定的ASC或者DESC的排序方式是不同,前者就是指樹中的檢索方向,或者是指索引行的實際順序。BY Joe .TJ

    1. 父級操作符取走返回的數據。

    2. 如果操作符用作範圍掃描,則會再次調用next(),讀取已返回行的後一行。操作符會保存前一個返回行的鍵值和位置信息。然後再上一點拒描述的B-Tree檢索過程。如果當前葉級頁的數據已經被取完了,則會定位到下一頁的第一行並返回。索引頁通過雙向鏈表連接,每一頁上都會前一頁和下一頁的指針。

    3. 父級操作符再次取走返回的數據。

    4. 因為範圍掃描會包含範圍結束位置的鍵值,所以當調用next()從當前行移動到後一行,而後一行的鍵值超過了結束位置的鍵值,會返回false。此處的“超過”與B-Tree檢索方向和索引排序有關。

    5. B-Tree操作符除能被重繞(Rewind),還能被重新綁定(Rebind)。重繞會重置操作符的狀態,使其使用同樣的鍵值重新開始查找或者範圍掃描。重新綁定會改變用於查找的鍵值,也就是說之前的結果可能無效了。

  • 預讀(Read Ahead)

    掃描操作每次讀取完一頁上的所有數據後,再去讀取下一頁,如果下一頁不在緩存池,就需要等待將頁從磁碟載入到緩存池。如果每讀一頁,都需要等待載入,那性能就太差了。SQL Server 使用預計的機制來優化這種操作。掃描操作符通過非同步IO將現在還沒有被使用到,但是很快會被用到的頁提前載入到緩存池中來。

    嵌套迴圈中還有一種特殊的預讀叫隨機預讀(Random Prefetching),它是為了減少查找操作等待而提前將頁載入到緩存池。

    預讀一般會預先讀取500頁左右的數據,如果頁連續,則每一次非同步IO最多可以讀取64頁(512KB)的數據,也就是說理想情況下8次IO可以完成一次預讀。讀取數據頁時,根據IAM獲取要預計的頁地址,然後把連續的頁合併到同一次IO中。索引頁(指葉級頁),則是通過B-Tree的中間級的索引頁,獲取需要預讀的頁地址,然後合併連續的頁到同一次IO。兩者中不連續的頁,會單獨執行IO。這裡的“連續”都指的是邏輯上的連續。 BY Joe .TJ


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天碰到一個很奇怪的問題,關於exists的, 第一個語句如下: 產生的結果是:89584 第二個語句如下: 產生的結果是:432382 確實相當奇怪,對於exist子句來說,其判斷的是子查詢的值是否存在,也就是說,列名,和對列名求最大值沒什麼區別啊。 包括MySQL官方文檔中也提到 大意就是MyS ...
  • 當前全球正處在新一輪科技革命和產業變革的關鍵時期,以大數據為代表的信息技術產業變革,使各個企業在信息技術發展上處於新的起跑線上,如何充分挖掘利用大數據資源,是企業當前面臨的重大課題。 ...
  • 一、資料庫操作 1、查看資料庫 2、創建資料庫 3、使用資料庫 4、用戶管理 mysql> SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user; + + | query | + + | ...
  • 查詢方式:LIKE '%xx%' 普通: SELECT * FROM TABLE T WHERE T.COLUNM LIKE '%xx%' 優化:使用 INSTR SELECT * FROM TABLE T WHERE INSTR(T.COLUNM, xx)>0 在執行的時候,執行計劃顯示,消耗值, ...
  • hive odbc 驅動配置成功後,通過c#訪問就變得比較簡單了,分為查詢與更新操作,直接附上測試代碼。在此過程中需要註意c#工程編譯的目標平臺 讀寫訪問代碼示例: public class HiveOdbcClient { /// <summary> /// /// </summary> publ ...
  • 熟悉了Sqlserver的sqlserver management studio、Oracle的PL/SQL可視化資料庫查詢分析工具,在剛開始使用hive、phoenix等類sql組件時,一直在苦苦搜尋是否也有類似的工具,不負所望,SQuirrel Sql client 可視化資料庫工具基本可滿足要 ...
  • 針對Windows 32位和64位的系統對應有多個版本的 Hive ODBC Connector, 在安裝的過程中,版本必須完全匹配(即:32位的 connector 只能在32位的系統運行,64位的 connector 只能在 64位系統運行) 參考資料: http://doc.mapr.com/ ...
  • Hive是一個基於Hadoop的數據倉庫,最初由Facebook提供,使用HQL作為查詢介面、HDFS作為存儲底層、mapReduce作為執行層,設計目的是讓SQL技能良好,但Java技能較弱的分析師可以查詢海量數據,2008年facebook把Hive項目貢獻給Apache。Hive提供了比較完整 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...