SSIS工程師為您揭秘SQL Server數據流
我們在操作SQL Server數據庫的過程中,常常會聽到“數據流”這個術語,那么到底什么是數據流呢?本文我們就介紹一下數據流的相關知識,本文是轉載的一位SSIS工程師的文章,接下來就讓我們一起來了解一下這部分內容吧。
數據流一瞥
SSIS的引擎(engine)是內存式(in-memory)的:從源(source)讀數據,在內存中執行package,再把結果寫到端(destination)。盡量不碰外存是其高性能的原因之一。很多以前使用ETL(Extract-Transform-Load)工具的人需要對此調整觀念:那些工具先把數據加載到數據庫里再做SQL轉換,其實是ELT(Extract-Load-Transform)。Matt講了個很有趣的案例:有位客戶的package以前運行只要幾分鐘,自從服務器升級到新機器后竟然更慢了,要花一個小時。那個package很簡單,只是源到端拷貝,中間沒有轉換(transform),因此客戶很生氣。
Matt他們急忙去會診,才發現這個package的源和端以前就在它所運行的那臺機器上,在美國; 后來升級了的機器在中國,源和端都跑到了中國來,而package還是在美國那臺機器上運行。結果這個package所做的就是從中國讀出若干GB的數據到美國的內存,再拷回中國……Matt說,類似的客戶問題其實并不少見。SSIS在設計時(design time)階段就確定了數據流的元數據(metadata)。它在運行之前就精確知道了運行時的列將有多寬,轉換需要多少內存,等等。
數據流水線(pipeline)
當數據流啟動時,源就開始把一行行數據填到一個類似桶的緩存(buffer)中。源根本不知道下游是什么。一旦緩存滿了,桶就隨著流水線流到下游組件(component)上,同時引擎抓一個新的空緩存過來給源。源根本不知道這一切,它只是不斷地填桶。有時源填了太多的桶,轉換和端都來不及應付了;此時引擎會啟動反壓(backpressure)機制,讓源睡眠。
等到流水線又有空間之后,源被喚醒繼續填桶。其實在實現上,源甚至都不知道自己被催眠過(好可憐)……直到所有源數據行都發光了,源才在最后一個緩存上貼個“行集末(End Of Rowset)”的標簽,把它發出去,告訴下游組件再沒有新數據了。
轉換與緩存拷貝
SSIS的高性能有部分歸功于它在內存使用上比較聰明。在緩存之間拷貝數據是耗時的,因此引擎會盡量減少緩存拷貝。按照緩存使用的不同,可將眾多轉換組件分為三類。
第一類是同步(synchronous)轉換,它們一般逐行對數據做就地修改,從不拷貝緩存。它們有可能增加新行,比如數據轉換(Data Convert)和派生列(Derived Column)轉換,而仍然是同步的:引擎事先確定了新列將加在哪里,提前就在緩存里加了空列,只是上游組件看不到這些空列罷了。
異步(asynchronous)轉換會動態創建新緩存,包括兩小類: 部分阻塞(Partially Blocking)轉換,一伺新緩存滿了就把它輸出,比如聯合全體(Union All)組件接受多個輸入流,一旦從各輸入得到了足夠多的行就把它輸入到一個新緩存里。由于要拷貝數據,這種轉換比同步轉換慢;但和全阻塞(Blocking)轉換相比就好多了。排序(Sort)、聚集(Aggregate)這些全阻塞轉換在接收完所有輸入行之前,是不會輸出一行的。這是由運算本身的特點決定的:不到看到所有數據,是無法確定哪個是最小值的。
因此,在使用全阻塞轉換時要格外審慎,尤其是數據量很大時。一旦內存用完,緩存被置換到硬盤上,性能就完了。要想提高數據流性能,最好設法從package中去除全阻塞轉換。
線程機制
要理解數據流,還需要了解其線程機制。流水線在運行時被分成若干執行樹(Execution Trees)。每個創建新緩存的組件就是一棵新執行樹的起點;因此起點要么是個數據源,要么是個異步轉換。下圖的數據流中有5棵執行樹,如藍箭頭所示。引擎限定了每棵樹中最多工作的緩存數(目前定為五個),一旦更多緩存進來,就啟動反壓。注意到多播(Multicast)和條件分割(Conditional Split)轉換都是同步的,它們在分割數據流時并不創建新緩存;引擎只是創建了一些能映射到同一塊內存的虛擬緩存。所以即使你多播20次也不會看到內存消耗增多。
此圖修改自Matt的幻燈片
值得一提的是,數據流線程調度在SQL 2008版本中被改進了:在2005版中,每棵樹只分到一個線程執行,其問題是對于圖中右邊那種較長的樹,雖然樹里都是一序列同步轉換,但每次只能在樹中移動一個緩存,執行完它之后才能開始執行下一個緩存。很多人為了打碎較長的執行樹,就在中間插入一個單輸入的聯合全體(Union All)組件,由于它是異步的,就能間接引入另一個線程。
而現在,我們在2008版中改為讓每個緩存上都有一個線程在執行,這樣一棵樹中就可以有多個線程在執行。可能第一個線程先把一個緩存進行了三個轉換, 然后第二個線程撿起這個緩存繼續向下游轉換,同時第一個線程開始撿起下一個緩存。這樣就再也不需要上述間接的方法了。
關于SQL Server數據流的相關知識就介紹到這里了,希望本次的介紹能夠對您有所收獲!
【編輯推薦】