SQL Server 2019 修復函數內聯 bug,速度提高 1000 倍
瀏覽量: 次 發布日期:2023-08-17 21:48:47
sql server 2019 修復函數內聯 bug,速度提高 1000 倍
作者 | Jonathan Allen 譯者 | 無明
與過去幾十年出現的大多數數據庫一樣,SQL Server 允許開發人員通過創建函數來擴展數據庫。但在即將發布的 SQL Server 2019 之前,SQL Server 對標量函數的支持存在很多問題。
在 SQL Server 中,返回單個值的函數被稱為“標量 UDF”或“標量用戶定義函數”。這些標量 UDF 由數據庫自動歸類為確定性或非確定性的。例如,非確定性函數用于讀取當前時間或從表中獲取數據。確定性函數始終為給定的參數集返回相同的值。理論上,數據庫如果知道正在使用的是確定性 UDF,那么就可以針對這些函數進行優化。
遺憾的是,SQL Server 從未將標量 UDF 支持完全集成到執行計劃生成器中。因此,它經常會做一些不必要的工作,例如會在每一行上執行確定性函數,但其實如果只針對每個唯一值執行一次函數會更快。
重復使用之前的值多少次以上才會帶來實際的好處?這個很難說。除非數據是預先排序的,或者它知道可能的輸入數量是有限的,否則緩存函數參數和結果的成本可能會超過收益。而這并不是標量 UDF 存在的唯一問題。
SQL Server 標量 UDF 的另一個問題是它們給并行化帶來了阻礙。跨多個 CPU 分發復雜查詢的能力是 SQL Server 的主要賣點。(很多開源替代品幾乎沒有并行支持或者只能依賴分布式數據庫)。如果沒有并行化,就很難證明 SQL Server 對得起它的價格。
說到價格,我們根本無法估計一個 SQL Server 標量函數究竟有多貴。所有的標量函數,不管是簡單的還是復雜的,在執行計劃中都會被賦予一個默認的成本。
標量 UDF 與調用它們的查詢之間是單獨進行解釋的。根據微軟的說法,對于每一行數據,都涉及到查詢與函數之間的上下文切換。我們可以假設上下文切換的成本可能會超過函數本身的成本。
出于這些原因,很多開發人員和 DBA 建議不要在對性能要求較高的代碼中使用標量函數,盡管將標量函數的內容粘貼到需要它的每個查詢、視圖和存儲過程中會導致大量代碼重復。于是,我們經常會聽到諸如“除了視圖之外的代碼重用不適合數據庫”這樣的說法。
標量函數的性能損失不容小覷,例如這個簡單的函數:
CREATE FUNCTION dbo.discount_price(@price DECIMAL(12,2), @discount DECIMAL(12,2))RETURNS DECIMAL (12,2) ASBEGIN RETURN @price * (1 - @discount);END
微軟研究員 Karthik Ramachandra 表示,這個標量函數可能會導致一個通常只需要 1.6 秒的查詢變成 29 分 11 秒那么久。雖然沒有語義差異,速度卻慢了 1000 倍。
解決方法是使用“內聯表值函數”或“內聯 TVF”代替標量函數。表值函數通常會返回一組行,但也可以將它們改寫成只返回一行。這個時候,可以使用 CROSS APPLY 運算符模擬正常的標量函數調用。 SQL Server 2019 中的函數內聯
從 SQL Server 2019 開始,可以內聯用使用 T-SQL 編寫的標量函數。這意味著它們可以被嵌入到查詢中,并且不會有 UDF 那樣的開銷。在查看執行計劃時,包含邏輯代碼的查詢與使用內聯標量函數的查詢之間是沒有區別的。
這個新功能不僅限于簡單的表達式。一些多語句 UDF 也可以被內聯,甚至可以內聯涉及從表中讀取數據的非確定性 UDF。它還以推斷出是否需要添加 JOIN 或 GROUP BY 運算符,以便將查詢中的表與函數中的表組合在一起。
當然,并非所有函數都可以被內聯。要進行內聯,UDF 需要滿足以下要求:
滿足以下所有條件的標量 T-SQL UDF 可以被內聯:
使用以下構造編寫的 UDF:
DECLARE、SET:變量聲明和賦值。
SELECT:具有單 / 多變量賦值的 SQL 查詢。
IF/ELSE:具有任意級別的嵌套分支。
RETURN:單個或多個返回語句。
UDF:嵌套 / 遞歸函數調用。
其他:關系操作,如 EXISTS、ISNULL。
不調用任何與時間相關的內部函數(例如 GETDATE())或具有副作用的函數(例如 NEWSEQUENTIALID())的 UFD。
使用 EXECUTE AS CALLER 子句(如果未指定 EXECUTE AS 子句,則這個為默認行為)的 UDF。
不引用表變量或表值參數的 UDF。
調用了標量 UDF,但其 GROUP BY 子句中不引用標量 UDF 調用的查詢。
不是原生編譯的 UDF。
不是被用在計算列或檢查約束定義中的 UDF。
不引用用戶定義類型的 UDF。
沒有添加任何簽名的 UDF。
不是用于分區的 UDF。
對于每個 T-SQL 標量 UDF,sys.sql_modules 視圖中都有對應的 is_inlineable 屬性,這個屬性用于指示 UDF 是否可以內聯。值為 1 表示它是可內聯的,0 表示不可以內聯。對于所有內聯 TVF,這個屬性的值均為 1。對于所有其他模塊,該值為 0。
可以通過將數據庫兼容級別設置為小于 150 或者將 TSQL_SCALAR_UDF_INLINING 的作用域配置設置為 OFF 來禁用 UDF 內聯。
也可以通過 OPTION (USE HINT(‘DISABLE_TSQL_SCALAR_UDF_INLINING’)) 在給定查詢上禁用內聯。
你也可以在聲明函數時使用 WITH INLINE = OFF 來表示永久禁用內聯。 Froid 項目和未來的機會
如果沒有 Froid 研究項目,可能就無法將該功能添加到 SQL Server。Froid 項目被描述為:
用于優化關系數據庫命令式程序的可擴展框架。Froid 自動將整個用戶定義函數(UDF)轉換為關系代數表達式,并將它們嵌入到 SQL 查詢中。這種形式可以進行基于成本的優化,并且可以生成有效的、面向集合的并行計劃,而不是 UDF 那種低效、迭代、串行的執行過程。Froid 還為 UDF 帶來了很多編譯器優化,而不需要進行額外的實現。我們介紹了 Froid 的設計,并展示了我們的實驗評估,它在實際工作負載上帶來了多達數個數量級的性能改進。
目前只知道 Froid 框架支持 T-SQL,但論文中也提到了 C#、Java、Python 和 R 語言。由于 SQL Server 現在支持這四門語言中的三門,因此將函數內聯擴展到其他語言會帶來很大好處。 英文原文
https://www.infoq.com/news/2019/01/SQL-Server-Scalar-UDF-Inlining
南京兆柏數據恢復中心 南京兆柏數據恢復中心
. 數據庫的恢復模式有哪些,SQL Server數據庫的恢復模式詳解
. SQL Server 恢復掛起狀態,原因、診斷與解決方法
. sqlserver備份到其他服務器,SQL Server數據庫備份到其他服務器的實用
. 2008數據庫怎么還原數據庫,SQL Server 2008 數據庫還原指南
. sqlserver數據庫恢復掛起狀態,什么是SQL Server數據庫恢復掛起狀態?
. sqlserver數據庫恢復50g大約多少時間,了解SQL Server數據庫恢復
. sql server數據庫顯示恢復掛起,什么是SQL Server數據庫恢復掛起?
. sqlserver數據庫恢復進度,什么是SQL Server數據庫恢復進度?
. sqlserver數據庫恢復步驟,SQL Server數據庫恢復步驟
. sqlserver數據庫恢復數據,什么是SQL Server數據庫恢復數據?
. sqlserver數據庫恢復,什么是SQL Server數據庫恢復?
. sqlserver數據庫恢復掛起什么導致的,什么是SQL Server數據庫恢復掛起
. sqlserver數據庫恢復掛起,什么是SQL Server數據庫恢復掛起?
. sqlserver數據庫恢復工具,什么是SQL Server數據庫恢復工具?
. sqlserver數據庫恢復模式,什么是SQL Server數據庫恢復模式?
. sqlserver數據庫恢復掛起怎么解決,什么是SQL Server數據庫恢復掛起?
. sqlserver誤刪數據庫怎么恢復,什么是SQL Server誤刪數據庫
. 不支持的sql server版本怎么解決,不支持的sql server版本
. MySQL server has gone away,為什么MySQL server