遊標腳本性能問題詳解之案例實踐篇

2020-11-23 IT168

    【IT168 專稿】遊標類型對性能影響的實例。下面的兩個遊標腳本分別創建並執行了dynamic和fast forward only兩種類型的遊標。

  不理想的遊標類型:(dynamic遊標)

declare @p1 int  set @p1=NULL  
declare @p2
int  set @p2=0  
declare @p5
int  set @p5=4098
declare @p6
int  set @p6=8193  
declare @p7
int  set @p7=0  

exec sp_cursorprepexec @p1 output,@p2 output,N
'@P1 varchar(30),@P2 varchar(15)',
N'
SELECT       T1.CONFLICT_ID
FROM         dbo.S_AUDIT_ITEM T1            
LEFT OUTER JOIN dbo.S_USER T2
ON T1.USER_ID = T2.PAR_ROW_ID    
WHERE  ((T1.BC_BASE_TBL
= @P1)  
AND  (T1.RECORD_ID = @P2))    
ORDER BY  T1.OPERATION_DT DESC  
OPTION (FAST 40)
',
@p5 output,@p6 output,@p7 output,'1-10350J','S_PARTY'  

print
'fetch'
exec sp_cursorfetch @p2,2,4,1  

exec sp_cursorclose @p2

  理想的遊標類型(fast forward only遊標)

declare @p1 int  set @p1=NULL  
declare @p2
int  set @p2=0  
declare @p5
int  set @p5=4112
declare @p6
int  set @p6=8193  
declare @p7
int  set @p7=0  

exec sp_cursorprepexec @p1 output,@p2 output,N
'@P1 varchar(30),@P2 varchar(15)',
N'
SELECT       T1.CONFLICT_ID
FROM         dbo.S_AUDIT_ITEM T1            
LEFT OUTER JOIN dbo.S_USER T2
ON T1.USER_ID = T2.PAR_ROW_ID    
WHERE  ((T1.BC_BASE_TBL
= @P1)  
AND  (T1.RECORD_ID = @P2))    
ORDER BY  T1.OPERATION_DT DESC  
OPTION (FAST 40)
',
@p5 output,@p6 output,@p7 output,'S_SRV_REQ','1-WUQTM6'  

select @p1, @p2, @p5, @p6, @p7

print
'2'
exec sp_cursorfetch @p2,2,1,1  
print
'3'
exec sp_cursorclose @p2

  註:腳本中用到的和遊標有關的存儲過程,請參考:http://jtds.sourceforge.net/apiCursors.html#_sp_cursorprepexec

  如何解讀遊標的類型

sp_cursorprepexec [@handle =] statement_handle OUTPUT,
     [@cursor
=] cursor_handle OUTPUT,
     [@paramdef
=] N'parameter_name data_type, [,...n]'
     [@stmt =] N'stmt',
    [, [@scrollopt =] scroll_options OUTPUT]
    [, [@ccopt
=] concurrency_options OUTPUT]
    [, [@rowcount
=] rowcount OUTPUT]

@scrollopt

 


 

  [@ccopt

  @p5=4098 轉成16進位就是1002,對應的遊標類型為Parameterized query + Dynamic cursor

  @p5=4112 轉成16進位就是1010,對應的遊標類型為Parameterized query + Fast forward-only cursor

  問題的現象是,左邊的遊標類型下,該腳本執行時間遠大於右邊的遊標類型。

  二、如何比較兩個不同執行計劃的優劣

  在繼續以下內容之前,這裡要介紹一些查看和比較語句執行計劃的知識。通常情況下,我們從management studio中輸出圖形界面的執行計劃進行直觀的比較,查看每個表用的訪問方式,使用index還是table scan,使用了哪個index,表和表之間使用的join 方式有什麼不一樣。但是如果是一個複雜的語句,在不同的資料庫上使用了不同的執行計劃,對於同樣表的訪問,使用了不同的index,如何比較哪種執行計劃更加優化呢?比較整個語句的執行時間是一種方法,但是這個比較的結果並不準確。語句的執行時間很容易受到其他外在因素的影響:

  1. 不同機器上CPU,memory和disk的性能會影響執行時間。

  2. 測試的時候有沒有其他人在使用同樣的數據造成阻塞

  3. 其他人堆資料庫的使用佔用了系統資源

  以上這些原因都有可能影響的語句的執行時間,從而影響到我們對語句性能結果的比較。因此我們不能把語句的執行時間作為衡量語句性能的標準。

  這裡介紹一種比較語句cost的方法。我們對於語句cost的衡量,主要是通過比對語句總的logical reads.

  我們可以通過在management studio裡的query window 執行」set statistics io on」 ,在當前窗口中對所有執行的語句輸出信息:

set statistics io on
select * from dbo.test_TicketFact
set statistics io on

  執行語句兩次,以消除physical reads和read-ahead reads的影響。

  輸出的結果如下:

(320 row(s) affected)
Table
'test_TicketFact'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

  這裡列印出來了語句中訪問過的table的訪問次數,總共的logical reads,physical reads等信息

  這裡我們需要關注的是logic reads的值,這個值實際上決定了對於IO和DISK以及內存的消耗。當語句是第一次執行,我們會看到physical reads的數字,以,而當語句第二次執行的時候,這些數據已經被讀到memory裡面了,因此我們會看到physical read和read-ahead reads都變為0,而logical reads的值就變成了語句所有使用的data的量。

  為什麼logic reads是我們需要關注的值呢?因為logic reads決定了語句要訪問數據的量。如果我們的系統瓶頸在IO上,一旦語句需要訪問的數據從內存裡面清除,這個語句原本所有的logic reads會全部轉為physical reads.因此那些大量使用logic reads就是可能導致大量physical reads的元兇。如果我們的bottleneck是CPU,這些做大量logical reads的語句同樣有可能導致大量的memory 讀,而讀memory是需要消耗CPU資源的。因此,無論是CPU,memory還是DISK的瓶頸,那些做大量logical reads的語句都非常可能是造成問題的原因。

  由以上內容,我們可以得出結論,語句的性能好壞,取決與這個語句做了多少logical reads.因此,如果同樣的語句,使用了不同的執行計劃,那麼總的logical reads低的那個執行計劃就是相對優化的。

  三、分析本案例中兩種遊標的執行計劃

  現在我們回到需要研究的腳本,在這裡,語句是一樣的,不同的只是遊標的類型。不同的執行時間說明很可能這個語句使用了不同的執行計劃。現在問題變成了,同樣語句使用了不同的執行計劃,得到了不同的執行時間。我們首先從」set statistics io on」 的結果入手:

  1.左邊使用dynamic遊標有大量的邏輯讀,情況如下:

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'S_AUDIT_ITEM'. Scan count 1, logical reads 9770695, physical reads 0, read-ahead reads 1, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

     2.而右邊使用fast forward only遊標只有三次邏輯讀,情況為:

 

Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'S_AUDIT_ITEM'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    從這裡輸出的結果的區別,說明了在table S_AUDIT_ITEM上SQL Server使用了不同的訪問方式

  接下來我們分析兩個腳本的執行計劃:

  1. dynamic遊標對應的不理想的執行計劃中,SQL Server選擇了索引掃描(index scan)及索引S_AUDIT_ITEM_M4來查閱S_AUDIT_ITEM表。因此我們會在這裡看到大量的IO。

  這個索引掃描實際上訪問了整張表的數據。

  2.而fast forward only遊標對應的理想的執行計劃中,SQL Server選擇的是索引查找(index seek)及索引S_AUDIT_ITEM_M3來查閱S_AUDIT_ITEM表。所以我們只看到3個邏輯讀。索引S_AUDIT_ITEM_M3包含4個列,第一個列是RECORD_ID。另外,在語句中,有WHERE條件T1.RECORD_ID=@P2
 


  四、嘗試解決問題

  首先我們嘗試更新統計信息:UPDATE STATISTICS ON S_AUDIT_ITEM WITH FULLSCAN,但是這個操作在此問題案例中沒有作用。

  從以上的分析中,我們已經發現,如果使用index S_AUDIT_ITEM_M3訪問S_AUDIT_ITEM表,得到的執行計劃非常好,我們可以直接用index hint來解決這個問題:

declare @p1 int set @p1=NULL

  declare @p2
int set @p2=0

  declare @p5
int set @p5=4098

  declare @p6
int set @p6=8193

  declare @p7
int set @p7=0

  exec sp_cursorprepexec @p1 output,@p2 output,N
'@P1 varchar(30),@P2 varchar(15)',

  N
'

  
SELECT T1.CONFLICT_ID

  FROM dbo.S_AUDIT_ITEM T1
with (INDEX=S_AUDIT_ITEM_M3) /* 解決方案2 */

  
LEFT OUTER JOIN dbo.S_USER T2

  
ON T1.USER_ID = T2.PAR_ROW_ID

  WHERE ((T1.BC_BASE_TBL
= @P1)

  
AND (T1.RECORD_ID = @P2))

  ORDER BY T1.OPERATION_DT DESC

  
OPTION (FAST 40)

  
',

  @p5 output,@p6 output,@p7 output,
'1-10350J','S_PARTY'

  print
'fetch'

  exec sp_cursorfetch @p2,
2,4,1

  exec sp_cursorclose @p2

相關焦點

  • 如何使用SQL Server遊標
    那麼如何解決這種問題呢?遊標為我們提供了一種極為優秀的解決方案。  1.遊標和遊標的優點  在資料庫中,遊標是一個十分重要的概念。遊標提供了一種對從表中檢索出的數據進行操作的靈活手段,就本質而言,遊標實際上是一種能從包括多條數據記錄的結果集中每次提取一條記錄的機制。
  • 資料|MATLAB優化算法案例分析與應用(進階篇)
    from=leiphonecolumn_res0817內容簡介 · · · · · ·《MATLAB優化算法案例分析與應用(進階篇)》是深受廣大讀者歡迎的《MATLAB優化算法案例分析與應用》一書的姊妹篇,即進階篇。本書全面、系統、深入地介紹了MATLAB算法及案例應用。
  • 知識貼:遊標永磁電機及相關科研問題的研究進展
    為了解決傳統永磁電機轉矩密度的提升越來越緩慢,跟不上新能源汽車、新能源發電等新興行業需求的問題,研究人員從電機基礎理論和拓撲結構出發,提出了一種新型電機,即遊標永磁電機,它翻譯自英文「Vernier motor」。
  • SQL遊標原理和使用方法
    那麼如何解決這種問題呢?遊標為我們提供了一種極為優秀的解決方案。1.1 遊標和遊標的優點在資料庫中,遊標是一個十分重要的概念。遊標提供了一種對從表中檢索出的數據進行操作的靈活手段,就本質而言,遊標實際上是一種能從包括多條數據記錄的結果集中每次提取一條記錄的機制。
  • 買珠子看不懂遊標卡尺?!是時候再科普一遍,遊標卡尺的使用方法了!
    請關掉這篇文章,因為你早已掌握了這項簡單的初中水平基礎技能。或者是什麼其他的數值,那請您一定要好好學習一下遊標卡尺的使用方法。明明買到的東西沒有任何問題,非得因為自己不會讀遊標卡尺認為自己吃藥了,空惹自己不高興不說,傳出去也讓人笑話啊!
  • Python 腳本案例:為流域插值雨量計
    我們不僅更新完善了諸多功能,最重要的是加入了Python 腳本。什麼是Python?Python是一種解釋型腳本語言。Python自誕生之日起,便有類、函數、異常處理,並且可以調用很多C語言庫文件。隨著人工智慧大數據的快速發展,Python一路綠燈,備受關注。
  • python3腳本篇1-串口通訊腳本
    前言本文我們來學習Python軟體包pyserial的使用,硬體採用wemos D1開發板,直接燒錄串口章節代碼即可,本文主要編寫PC端Python腳本,完成腳本和外接串口設備通訊的功能。運行python腳本進入run.py根目錄,終端(本文用VS Code下終端,也可在CMD命令窗口執行)輸入python3 run.py即可運行程序,界面如下:手動輸入hello world,wemos收到數據後會立刻返回。
  • 遊標卡尺的測量原理
    打開APP 遊標卡尺的測量原理 佚名 發表於 2009-06-08 23:38:45 遊標卡尺的測量原理遊標尺上只有
  • 遊標卡尺的原理和使用
    請認真研讀,相信讀者可以體會到遊標卡尺的設計精妙之處,與讀數注意事項。遊標卡尺可以幹什麼用遊標卡尺可以方便的測量一下工件的外徑(使用外測量爪)、內徑(使用內測量爪)、深度(使用深度尺)遊標卡尺讀數方法小結:
  • SQL SERVER 參考:遊標(Cursor)的講解與實例
    遊標總是與一條T_SQL 選擇語句相關聯,因為遊標由結果集(可以是零條、一條或由相關的選擇語句檢索出的多條記錄)和結果集中指向特定記錄的遊標位置組成。  當決定對結果集進行處理時,必須聲明一個指向該結果集的遊標。如果曾經用 C 語言寫過對文件進行處理的程序,那麼遊標就像您打開文件所得到的文件句柄一樣,只要文件打開成功,該文件句柄就可代表該文件。對於遊標而言,其道理是相同的。
  • 遊標卡尺原理與讀數方法解析
    、雙縫幹涉實驗的測量等,工廠用的高度尺、深度尺,經緯儀上使用角度遊標、微分天平的讀數等也使用遊標刻度來讀取微小的值。同時在部分資料上出現了遊標卡尺的反讀試題,讓沒有真正掌握遊標卡尺的原理的學生感到難以下手,因此結合個人在教學中的實踐,整理總結出遊標卡尺的兩種讀數方法,以饗讀者。
  • 遊標卡尺讀數原理,遊標卡尺讀數方法
    遊標卡尺是目前工程工業中使用最廣泛的一種測量工具,它的出現可以說是具有劃時代意義的。在測量工件內徑、外徑、深度、寬度等方面有著很大的優勢。但遊標卡尺的形式也有很多,很多使用者對於某些形式的遊標卡尺的使用還存在著一定的誤解。今天我們就講一講遊標卡尺的知識。
  • 中山大學管理學院講座預告丨案例研究近路之爭:案例研究最佳實踐...
    中山大學管理學與經濟學系列前沿講座之四一四講暨「案例思享會」專題講座第17講講座主題案例研究近路之爭:案例研究最佳實踐主講嘉賓李平 教授(寧波諾丁漢大學)主持人梁劍平 副教授講座時間2020
  • 遊標卡尺的讀數方法 遊標卡尺怎麼讀數
    遊標卡尺是目前工程工業中使用最廣泛的一種測量工具,它的出現可以說是具有劃時代意義的。在測量工件內徑、外徑、深度、寬度等方面有著很大的優勢。但遊標卡尺的形式也有很多,很多使用者對於某些形式的遊標卡尺的使用還存在著一定的誤解。下面我們就一起了解一下遊標卡尺的知識吧。
  • 遊標卡尺讀數 遊標卡尺讀數怎麼讀
    上中學的時候大家應該都有接觸過遊標卡尺,遊標卡尺是目前工程工業中使用最廣泛的一種測量工具,很多使用者對遊標卡尺的讀數還不太清楚,下面一起來看看吧。遊標卡尺讀數一刻度線為準,刻度位於兩個數值之間,讀取要取整數,遊標知識的數值要和尺身上的刻度線齊平,然後按照公式讀出測量值,需要注意的是10分度遊標卡尺遊標分度與主尺相差
  • 34個農村汙水治理典型案例詳解(下篇)
    前不久,2018年度村鎮汙水治理案例評啟動,本屆評選分為農村汙水治理案例、鄉鎮汙水治理案例兩大類,分別評選出「2018年度農村汙水治理優秀案例
  • 遊標卡尺的測量與讀數原理
    遊標卡尺由主尺和遊標尺組成,我們讀數的時候需要先讀主尺上的數值,之後再讀遊標尺上的數值,之後兩者相加就是遊標卡尺的所測量的最後數值。遊標卡尺是通過主尺的單位刻度與遊標尺的單位刻度的微量差值來提高測量精度的。我們根據遊標尺上的刻度數,遊標卡尺分為10分度、20分度、50分度三種。
  • MSSQL遊標函數-@@CURSOR_ROWS、CURSOR_STATUS、@@FETCH_STATUS
    @@CURSOR_ROWS全局變量返回在連接上打開的上一個遊標中當前擁有的限定行的數目。為了提高性能, SQL Server可以異步填充大型鍵集和靜態遊標。可調用@@CURSOR_ROWS以確定當@@CURSOR_ROWS被調用時檢索了遊標符合條件的行數。語法【SELECT @@CURSOR_ROWS】,其中返回類型為integer數據類型。
  • 遊標卡尺的介紹及使用方法
    遊標卡尺的概述遊標卡尺作為一種被廣泛使用的高精度測量工具,它是由主尺和附在主尺上能滑動的遊標兩部分構成。如果按遊標的刻度值來分,遊標卡尺又分0.1、0.05、0.02mm三種。副尺0 線後的第9條線表示: 0.02x9= 0.18mm所以被測工件的尺寸為: 64+0.18=64.18mm遊標卡尺的使用方法將量爪併攏,查看遊標和主尺身的零刻度線是否對齊。