本文節選自著名儲存專家冬瓜頭即將4月份出版的《大話儲存2》一書,希望大家到時支援這本書。
100Mb/s,這個速率意味著什麼呢?有人說,每秒可以傳輸10MB的資料(8/10b 編碼下)。通常情況下,這種說法是對的。但是如果發送方與接收方之間的距離變得很遠,比如數百公裡甚至一千公裡,那麼這種說法,你會發現根本不成立。我們現在就來分析一下。
大家知道,光或者電訊號的傳輸是有固定速度的,即近似每秒30萬公裡(其實遠未達到,光在光纜中的傳播速率只有20萬公裡每秒,電訊號在電纜中的傳播速率則近似21萬公裡每秒。基本上是光在真空或者空氣中速率的三分之二)。如果兩點之間距離為1000公裡,那麼訊號傳一個來回(傳到對端,然後對端給以ACK應答)所耗費的時間就是1000÷300000×2≈6.6ms。什麼概念呢?也就是你想把1bit的資料轉送到一千公裡之外的地方,那麼至少你要耗費6.6ms。那麼傳輸10bit、100bit、1Kb、100Mb,需要多長時間呢?首先想到的是,至少比傳1bit要慢。到底需要多長時間?來看這個公式:傳輸來回時間=(資料量÷鏈路速率×2)+(傳輸距離÷光速×2)。資料在傳輸的時候,是首先會被通過編碼電路將資料序列化編碼然後放到電路或者光路上傳輸,這個編碼速率,就是鏈路頻寬,100Mb/s的頻寬與1000Mb/s的頻寬,區別就在於後者在單位時間內可以編碼相當於前者10倍量的資料,但是不管鏈路頻寬有多少,資料被編碼之後,資料在電路上的傳輸所耗費的時間對各種速率的鏈路來講都是一樣的,因為傳輸的時候已經與鏈路編碼速率(頻寬)無關了,傳輸到對方之後,對方還需要解碼(所以編碼所耗費的時間也要乘以2),同樣也是取決於鏈路頻寬。
所以,當兩點之間傳輸距離很近的時候,比如1千米,那麼傳輸時延≈0.0066ms,基本上可以忽略了。所以那個公式變為:傳輸時間=(資料量÷鏈路速率)。所以說,鏈路速率越大,只代表其編碼速度越快,而不代表傳輸速度越快,傳輸速度是固定的,都是光速。再打個比喻,有一輛長途車,50個人排隊上車,排隊上車需要120s,汽車行駛需要60000s,50個人排隊下車需要120s。50個人被排隊送上車,就好比資料被串列編碼放到電路上傳輸,汽車行駛相當於電路訊號從一端傳遞到另一端,50個人被排隊下車,就好比對端的解碼過程了,然而到此還沒有結束,當汽車抵達目的地之後,司機必須在返回出發點進行報信,這就好比TCP協議在收到資料之後發送給源端的ACK應答一樣。可以司機空著車跑回去報信(單獨發送ACK應答包),也可以在目的端捎帶著一些回程客人返回去報信(TCP可以在反向流量中夾帶ACK應答資訊以提高效率)。但是在容災系統中,資料總是從源端流向目的端的,或者在災難回切的時候從目的端流向源端的,總之只有一個方向有實體資料流動,那麼此時回程ACK都是獨立的ACK應答包(獨立ACK包很小所以其編解碼所耗費的時間也忽略掉即可)。
另外,一輛汽車能承載的人數是有限的,也就是說,得一趟一趟的拉,這就好比TCP每次所發送的最大資料長度,也就是TCP的滑動視窗長度,TCP得分批把使用者資料傳送出去,每次的發送量必須小於TCP滑動視窗的長度,每次傳輸之後均需要對方發送一個ACK(這裡不考慮ACK合并等特殊情況)。每批資料雖然到了底層可能被切分,比如TCP的MSS(Max Segment Size)切片,一般等於底層鏈路的MTU,底層鏈路再用MTU的值來切片,但是這些底層的切片在被傳輸到對端之後,並不需要對端底層協議的應答,只有對端的TCP在完整的收到TCP發送的一批資料之後,才會應答。
那麼我們來算算在相隔1000千米的兩點之間,每秒到底能夠傳送多少個來回:1000ms÷6.6ms=151個來回。如果按照TCP的典型滑動視窗即16KB來計算的話(每次發送16KB資料然後就等待應答,不考慮延遲應答或者合并應答等特殊情況),那麼每秒輸送量僅為151×16KB=2416KB,也就是2.4MB每秒。誇張嗎?
當然,上述算式是忽略了編解碼所耗費的時間以及整個鏈路上各種中繼、轉寄或者協議轉換裝置所帶來的處理延遲,如果算上這兩者,則輸送量會更低。更加準確的實際資料轉送輸送量計算公式為V=TCP Window Size÷2(TCP Window Size÷鏈路頻寬+距離÷光速+鏈路裝置處理延遲)。總之,距離越遠,實際傳輸輸送量就越低,在實際應用中一定要有底。
距離很短時,可以忽略距離帶來的延遲,此時顯然誰頻寬高誰傳的就快;而距離很長時,此時頻寬再高也無濟於事,因為大頭都被距離給耗掉了。另外,即便是底層鏈路的頻寬相同,距離也相同的情況下,使用不同的協議進行傳輸,所帶來的延遲也是不同的。但是設想一下,不管鏈路跨越了多長的距離,如果這條鏈路上永遠都有資料在傳著,那麼發送方與接收方就可以以鏈路頻寬的原生速率來收發資訊,只不過有時延,就像衛星電視那樣,此時傳輸速率並不會打折,如果做到這一點,那麼對於一個容災系統來講是非常好的事情,充其量只會丟失幾毫秒之內的資料。但是,事實卻並非如此。超遠距離傳輸,怕的就是資料流的卡殼,卡一次兩次不要緊,頻繁卡殼,那就根本無法利用起鏈路頻寬了。這就好比磁碟尋道操作一樣,本來磁頭能以很高的速度讀寫碟片上的資料,但是沒辦法,必須換道,這一換道,外部速率驟降。碰巧的是,15K轉每秒的SAS盤其平均尋道時間為5.5ms,而一千公裡距離的傳輸時延為6.6ms,這兩個值倒是接近而且還挺有意思。
傳輸協議無法避免“卡殼”,因為總要傳一段歇一段來等待對方吱個聲,看看收到沒有。比如TCP,這樣就平白無故的浪費了底層鏈路時隙;再加上長距離下的高傳輸延遲,一來一回更浪費了大量時間,所以會出現上文中的即便是千兆鏈路下,1000公裡的距離每秒也只能傳輸2.4MB的理論值,實際值將會更低了。
另外,如果在長距離下使用諸如iSCSI等協議的話,那將更是一筆驚人的浪費。大家知道SCSI層本身就有傳輸保障機制,人家自己有ACK那一套,而底層TCP再來這一套顯然就顯得多此一舉了。按理說有了SCSI層的傳輸保障機制,其下層協議棧就應該是個無狀態的類似鏈路層協議了,應該直接將資料一股腦傳過去,但是現實是它非得傳一段,停一段,等待對方說個OK,然後再傳再停,慢慢騰騰;不僅如此,再加上SCSI也要傳傳停停,那就是變本加厲。所以長距離上跑諸如FCP、iSCSI等這種SCSI協議與FC/TCPIP協議的合體協議,將會是個夢魘。
降低不必要的ACK數量,增加滑動視窗,這些都是廣域網路加速的技術,對傳輸速率會有一定程度的提高。但是最終解決辦法,還是要盡量縮短兩地距離,或者開發專用最佳化的協議了。
說到私人協議,這裡就展開講一下。上述所有情境,均建立在兩點之間只有單TCP串連,即單流的情境下,此時的鏈路頻寬當然無法被充分利用,而且也提過,如果底層鏈路一刻也不閑著,那麼其有效頻寬就可以更高的被利用,怎麼辦呢?顯然,通過提高並發串連的數量,就可以充分利用起底層鏈路的時隙。關於這個思想,在磁碟陣列控制器如何充分利用起後端FCAL環路的頻寬方面也是類似的,大家可以閱讀附錄1中的第5問。
大家知道iSCSI裡有個Multi Connection Per Session的概念,使用Microsoft的軟iSCSI Initiator的話,裡面就可以進行設定,讓Initiator端可以同時與iSCSI Target端建立多條並發的TCP串連,從而提高遠距離傳輸時的效率,當然這個特性需要iSCSI Target端的支援配合。但是對於FCP來講,就沒有這種特殊考慮的並發串連設計了。經過考量設計的可並發串連的私人協議可以極大提高遠端資料傳輸的效率。
既然說到了多流並發,那麼索性就再展開一些。對於一個非同步模式的資料容災複製系統,最起碼要保證的是災備端資料的一致性,而資料一致性又有多個層面,最底層的一致性就是所謂“時序一致性”,災備端起碼要保證每個IO都按照其在源端被執行的順序刷入災備端資料集中。如果使用單流TCPIP則可以保證時序,但是傳輸效率很低;但是在多流並發的情況下,因為原本流與流之間是無關聯的,可能在源端先執行的IO被傳送到對端之後卻被後執行了,此時就需要引入更複雜的邏輯來保證同步過去的資料被按照順序執行。這裡又有兩種辦法可以考慮,一種是保證RPO,在多個流之間維護強一致性,將多個流強制關聯以保證收發順序,此時災備端可以立即將收到的IO資料刷入底層資料集;第二種則是犧牲RPO,主備網站之間之間採用端到端的一致性組技術,在資料批與資料批之間保證時序性,而不是每個IO之間。此時災備端不能在收到資料後立即刷入,比如等待一批資料全部收到之後才可以刷入。這麼做雖然可能導致丟失一批資料而不是幾個IO,但是可以方便的保證資料一致性。本文節選自尚未出版的《大話儲存2》,發出來讓大家先睹為快。同時歡迎提出錯誤和建議,謝謝!