|
英文原文:The Google File System,編譯:ImportNew - 儲曉穎 新浪微博:@瘋狂編碼中的xiaoY
【譯者預(yù)讀】
GFS這三個字母無需過多修飾,《Google File System》的論文也早有譯版。但是這不妨礙我們加點批注、重溫經(jīng)典,并結(jié)合上篇Haystack的文章,將GFS、TFS、Haystack進行一次全方位的對比,一窺各巨頭的架構(gòu)師們是如何權(quán)衡利弊、各取所需。
1. 介紹
我們設(shè)計和實現(xiàn)了GFS來滿足Google與日俱增的數(shù)據(jù)處理需求。與傳統(tǒng)的分布式文件系統(tǒng)一樣,GFS著眼在幾個重要的目標,比如性能、可伸縮性、可靠性和可用性。不過它也會優(yōu)先考慮我們自身應(yīng)用場景的特征和技術(shù)環(huán)境,所以與早先一些文件系統(tǒng)的設(shè)計思想還是有諸多不同。我們?nèi)鹘y(tǒng)方案之精華、根據(jù)自身需求做了大膽的設(shè)計創(chuàng)新。在我們的場景中:
首先,組件故障是常態(tài)而不是異常。文件系統(tǒng)包含成百上千的存儲機器,而且是廉價的普通機器,被大量的客戶端機器訪問。這樣的機器質(zhì)量和數(shù)量導(dǎo)致任何時間點都可能有一些機器不可用,甚至無法從當前故障中恢復(fù)。導(dǎo)致故障的原因很多,比如應(yīng)用bug、操作系統(tǒng)bug、人為錯誤,以及磁盤、內(nèi)存、連接器、網(wǎng)絡(luò)等硬件故障,甚至是電力供應(yīng)。因此,持續(xù)監(jiān)控、錯誤偵測、故障容忍和自動恢復(fù)必須全面覆蓋整個系統(tǒng)。
其次,用傳統(tǒng)視角來看,我們要處理的文件很多都是巨型的,好幾GB的文件也很常見。通常情況下每個文件中包含了多個應(yīng)用對象,比如web文檔。面對快速增長、TB級別、包含數(shù)十億對象的數(shù)據(jù)集合,如果按數(shù)十億個KB級別的小文件來管理,即使文件系統(tǒng)能支持,也是非常不明智的。因此,一些設(shè)計上的假設(shè)和參數(shù),比如I/O操作和塊大小,需要被重新審視。
第三,大部分文件發(fā)生變化是通過append新數(shù)據(jù),而不是覆蓋、重寫已有的數(shù)據(jù),隨機寫幾乎不存在。被寫入時,文件變成只讀,而且通常只能是順序讀。很多數(shù)據(jù)場景都符合這些特征。比如文件組成大型的庫,使用數(shù)據(jù)分析程序?qū)ζ鋻呙琛1热缬蛇\行中的程序持續(xù)生成的數(shù)據(jù)流。比如歸檔數(shù)據(jù)。還可能是分布式計算的中間結(jié)果,在一臺機器上產(chǎn)生、然后在另一臺處理。這些數(shù)據(jù)場景都是由制造者持續(xù)增量的產(chǎn)生新數(shù)據(jù),再由消費者讀取處理。在這種模式下append是性能優(yōu)化和保證原子性的焦點。然而在客戶端緩存數(shù)據(jù)塊沒有太大意義。
第四,向應(yīng)用提供類似文件系統(tǒng)API,增加了我們的靈活性。松弛的一致性模型設(shè)計也極大的簡化了API,不會給應(yīng)用程序強加繁重負擔(dān)。我們將介紹一個原子的append操作,多客戶端能并發(fā)的對一個文件執(zhí)行append,不需考慮任何同步。
當前我們部署了多個GFS集群,服務(wù)不同的應(yīng)用。最大的擁有超過1000個存儲節(jié)點,提供超過300TB的磁盤存儲,被成百上千個客戶端機器大量訪問。
2 設(shè)計概覽
2.1 假設(shè)
設(shè)計GFS過程中我們做了很多的設(shè)計假設(shè),它們既意味著挑戰(zhàn),也帶來了機遇。現(xiàn)在我們詳細描述下這些假設(shè)。
- 系統(tǒng)是構(gòu)建在很多廉價的、普通的組件上,組件會經(jīng)常發(fā)生故障。它必須不間斷監(jiān)控自己、偵測錯誤,能夠容錯和快速恢復(fù)。
- 系統(tǒng)存儲了適當數(shù)量的大型文件,我們預(yù)期幾百萬個,每個通常是100MB或者更大,即使是GB級別的文件也需要高效管理。也支持小文件,但是不需要著重優(yōu)化。
- 系統(tǒng)主要面對兩種讀操作:大型流式讀和小型隨機讀。在大型流式讀中,單個操作會讀取幾百KB,也可以達到1MB或更多。相同客戶端發(fā)起的連續(xù)操作通常是在一個文件讀取一個連續(xù)的范圍。小型隨機讀通常在特定的偏移位置上讀取幾KB。重視性能的應(yīng)用程序通常會將它們的小型讀批量打包、組織排序,能顯著的提升性能。
- 也會面對大型的、連續(xù)的寫,將數(shù)據(jù)append到文件。append數(shù)據(jù)的大小與一次讀操作差不多。一旦寫入,幾乎不會被修改。不過在文件特定位置的小型寫也是支持的,但沒有著重優(yōu)化。
- 系統(tǒng)必須保證多客戶端對相同文件并發(fā)append的高效和原子性。我們的文件通常用于制造者消費者隊列或者多路合并。幾百個機器運行的制造者,將并發(fā)的append到一個文件。用最小的同步代價實現(xiàn)原子性是關(guān)鍵所在。文件被append時也可能出現(xiàn)并發(fā)的讀。
- 持久穩(wěn)定的帶寬比低延遲更重要。我們更注重能夠持續(xù)的、大批量的、高速度的處理海量數(shù)據(jù),對某一次讀寫操作的回復(fù)時間要求沒那么嚴格。
2.2 接口
GFS提供了一個非常親切的文件系統(tǒng)接口,盡管它沒有全量實現(xiàn)標準的POSIX API。像在本地磁盤中一樣,GFS按層級目錄來組織文件,一個文件路徑(path)能作為一個文件的唯一ID。我們支持常規(guī)文件操作,比如create、delete、open、close、read和write。
除了常規(guī)操作,GFS還提供快照和record append操作。快照可以用很低的花費為一個文件或者整個目錄樹創(chuàng)建一個副本。record append允許多個客戶端并發(fā)的append數(shù)據(jù)到同一個文件,而且保證它們的原子性。這對于實現(xiàn)多路合并、制造消費者隊列非常有用,大量的客戶端能同時的append,也不用要考慮鎖等同步問題。這些特性對于構(gòu)建大型分布式應(yīng)用是無價之寶。快照和record append將在章節(jié)3.4、3.3討論。
2.3 架構(gòu)
一個GFS集群包含單個master和多個chunkserver,被多個客戶端訪問,如圖1所示。圖1中各組件都是某臺普通Linux機器上運行在用戶級別的一個進程。在同一臺機器上一起運行chunkserver和客戶端也很容易,只要機器資源允許。
文件被劃分為固定大小的chunk。每個chunk在創(chuàng)建時會被分配一個chunk句柄,chunk句柄是一個不變的、全局唯一的64位的ID。chunkserver在本地磁盤上將chunk存儲為Linux文件,按照chunk句柄和字節(jié)范圍來讀寫chunk數(shù)據(jù)。為了可靠性,每個chunk被復(fù)制到多個chunkserver上,默認是3份,用戶能為不同命名空間的文件配置不同的復(fù)制級別。
master維護所有的文件系統(tǒng)元數(shù)據(jù)。包括命名空間,訪問控制信息,從文件到chunk的映射,和chunk位置。它也負責(zé)主導(dǎo)一些影響整個系統(tǒng)的活動,比如chunk租賃管理、孤兒chunk的垃圾回收,以及chunkserver之間的chunk遷移。master會周期性的與每臺chunkserver通訊,使用心跳消息,以發(fā)號施令或者收集chunkserver狀態(tài)。
每個應(yīng)用程序會引用GFS的客戶端API,此API與正規(guī)文件系統(tǒng)API相似,并且負責(zé)與master和chunkserver通訊,基于應(yīng)用的行為來讀寫數(shù)據(jù)。客戶端只在獲取元數(shù)據(jù)時與master交互,真實的數(shù)據(jù)操作會直接發(fā)至chunkserver。我們不需提供嚴格完整的POSIX API,因此不需要hook到Linux的vnode層面。
客戶端和chunkserver都不會緩存文件數(shù)據(jù)。客戶端緩存文件數(shù)據(jù)收益很小,因為大部分應(yīng)用通常會順序掃描大型文件,緩存重用率不高,要么就是工作集合太大緩存很困難。沒有緩存簡化了客戶端和整個系統(tǒng),排除緩存一致性問題。(但是客戶端會緩存元數(shù)據(jù)。)chunkserver不需要緩存文件數(shù)據(jù)因為chunk被存儲為本地文件,Linux提供的OS層面的buffer緩存已經(jīng)保存了頻繁訪問的文件。
2.4 單一Master
單一master大大的簡化了我們的設(shè)計,單一master能夠放心使用全局策略執(zhí)行復(fù)雜的chunk布置、制定復(fù)制決策等。然而,我們必須在讀寫過程中盡量減少對它的依賴,它才不會成為一個瓶頸。客戶端從不通過master讀寫文件,它只會詢問master自己應(yīng)該訪問哪個chunkserver。客戶端會緩存這個信息一段時間,隨后的很多操作即可以復(fù)用此緩存,與chunkserver直接交互。
我們利用圖1來展示一個簡單讀操作的交互過程。首先,使用固定的chunk size,客戶端將應(yīng)用程序指定的文件名和字節(jié)偏移量翻譯為一個GFS文件及內(nèi)部chunk序號,隨后將它們作為參數(shù),發(fā)送請求到master。master找到對應(yīng)的chunk句柄和副本位置,回復(fù)給客戶端。客戶端緩存這些信息,使用GFS文件名+chunk序號作為key。
客戶端然后發(fā)送一個讀請求到其中一個副本,很可能是最近的那個。請求中指定了chunk句柄以及在此chunk中讀取的字節(jié)范圍。后面對相同chunk的讀不再與master交互,直到客戶端緩存信息過期或者文件被重新打開。事實上,客戶端通常會在一個與master的請求中順帶多索要一些其他chunk的信息,而且master也可能將客戶端索要chunk后面緊跟的其他chunk信息主動回復(fù)回去。這些額外的信息避免了未來可能發(fā)生的一些client-master交互,幾乎不會導(dǎo)致額外的花費。
2.5 chunk size
chunk size是其中一個關(guān)鍵的設(shè)計參數(shù)。我們選擇了64MB,這是比典型的文件系統(tǒng)的塊大多了。每個chunk副本在chunkserver上被存儲為一個普通的Linux文件,只在必要的時候才去擴展。懶惰的空間分配避免了內(nèi)部碎片導(dǎo)致的空間浪費,chunk size越大,碎片的威脅就越大。
chunk size較大時可以提供幾種重要的優(yōu)勢。首先,它減少了客戶端與master的交互,因為對同一個chunk的讀寫僅需要對master執(zhí)行一次初始請求以獲取chunk位置信息。在我們的應(yīng)用場景中大部分應(yīng)用會順序的讀寫大型文件,chunk size較大(chunk數(shù)量就較少)能有效的降低與master的交互次數(shù)。對于小型的隨機讀,即使整個數(shù)據(jù)集合達到TB級別,客戶端也能舒服的緩存所有的chunk位置信息(因為chunk size大,chunk數(shù)量小)。其次,既然用戶面對的是較大的chunk,它更可能愿意在同一個大chunk上執(zhí)行很多的操作(而不是操作非常多的小chunk),這樣就可以對同一個chunkserver保持長期的TCP連接以降低網(wǎng)絡(luò)負載。第三,它減少了master上元數(shù)據(jù)的大小,這允許我們放心的在內(nèi)存緩存元數(shù)據(jù),章節(jié)2.6.1會討論繼而帶來的各種好處。
不過chunk size如果很大,即使使用懶惰的空間分配,也有它的缺點。一個小文件包含chunk數(shù)量較少,可能只有一個。在chunkserver上這些chunk可能變成熱點,因為很多客戶端會訪問相同的文件(如果chunk size較小,那小文件也會包含很多chunk,資源競爭可以分擔(dān)到各個小chunk上,就可以緩解熱點)。不過實際上熱點沒有導(dǎo)致太多問題,因為我們的應(yīng)用大部分都是連續(xù)的讀取很大的文件,包含很多chunk(即使chunk size較大)。
然而,熱點確實曾經(jīng)導(dǎo)致過問題,當GFS最初被用在批量隊列系統(tǒng)時:用戶將一個可執(zhí)行程序?qū)懭隚FS,它只占一個chunk,然后幾百臺機器同時啟動,請求此可執(zhí)行程序。存儲此可執(zhí)行文件的chunkserver在過多的并發(fā)請求下負載較重。我們通過提高它的復(fù)制級別解決了這個問題(更多冗余,分擔(dān)壓力),并且建議該系統(tǒng)交錯安排啟動時間。一個潛在的長期解決方案是允許客戶端從其他客戶端讀取數(shù)據(jù)(P2P模式~)。
2.6 元數(shù)據(jù)
master主要存儲三種類型的元數(shù)據(jù):文件和chunk的命名空間,從文件到chunk的映射,每個chunk副本的位置。所有的元數(shù)據(jù)被保存在master的內(nèi)存中。前兩種也會持久化保存,通過記錄操作日志,存儲在master的本地磁盤并且復(fù)制到遠程機器。使用操作日志允許我們更簡單可靠的更新master狀態(tài),不會因為master的當機導(dǎo)致數(shù)據(jù)不一致。master不會持久化存儲chunk位置,相反,master會在啟動時詢問每個chunkserver以獲取它們各自的chunk位置信息,新chunkserver加入集群時也是如此。
2.6.1 內(nèi)存中數(shù)據(jù)結(jié)構(gòu)
因為元數(shù)據(jù)存儲在內(nèi)存中,master可以很快執(zhí)行元數(shù)據(jù)操作。而且可以簡單高效的在后臺周期性掃描整個元數(shù)據(jù)狀態(tài)。周期性的掃描作用很多,有些用于實現(xiàn)chunk垃圾回收,有些用于chunkserver故障導(dǎo)致的重新復(fù)制,以及為了均衡各機器負載與磁盤使用率而執(zhí)行的chunk遷移。章節(jié)4.3和4.4將討論其細節(jié)。
這么依賴內(nèi)存不免讓人有些顧慮,隨著chunk的數(shù)量和今后整體容量的增長,整個系統(tǒng)將受限于master有多少內(nèi)存。不過實際上這不是一個很嚴重的限制。每個64MB的chunk,master為其維護少于64byte的元數(shù)據(jù)。大部分chunk是填充滿數(shù)據(jù)的,因為大部分文件包含很多chunk,只有少數(shù)可能只填充了部分。同樣的,對于文件命名空間數(shù)據(jù),每個文件只能占用少于64byte,文件名稱會使用前綴壓縮緊密的存儲。
如果整個文件系統(tǒng)真的擴展到非常大的規(guī)模,給master添點內(nèi)存條、換臺好機器scale up一下也是值得的。為了單一master+內(nèi)存中數(shù)據(jù)結(jié)構(gòu)所帶來的簡化、可靠性、性能和彈性,咱豁出去了。
2.6.2 Chunk位置
master不會持久化的保存哪個chunkserver有哪些chunk副本。它只是在自己啟動時拉取chunkserver上的信息(隨后也會周期性的執(zhí)行拉取)。master能保證它自己的信息時刻都是最新的,因為它控制了所有的chunk布置操作,并用常規(guī)心跳消息監(jiān)控chunkserver狀態(tài)。
我們最初嘗試在master持久化保存chunk位置信息,但是后來發(fā)現(xiàn)這樣太麻煩,每當chunkserver加入或者離開集群、改變名稱、故障、重啟等等時候就要保持master信息的同步。一般集群都會有幾百臺服務(wù)器,這些事件經(jīng)常發(fā)生。
話說回來,只有chunkserver自己才對它磁盤上存了哪些chunk有最終話語權(quán)。沒理由在master上費盡心機的維護一個一致性視圖,chunkserver上發(fā)生的一個錯誤就可能導(dǎo)致chunk莫名消失(比如一個磁盤可能失效)或者運維人員可能重命名一個chunkserver等等。
2.6.3 操作日志
操作日志是對重要元數(shù)據(jù)變更的歷史記錄。它是GFS的核心之一。不僅因為它是元數(shù)據(jù)唯一的持久化記錄,而且它還要承擔(dān)一個邏輯上的時間標準,為并發(fā)的操作定義順序。各文件、chunk、以及它們的版本(見章節(jié)4.5),都會根據(jù)它們創(chuàng)建時的邏輯時間被唯一的、永恒的標識。
既然操作日志這么重要,我們必須可靠的存儲它,而且直至元數(shù)據(jù)更新被持久化完成(記錄操作日志)之后,才能讓變化對客戶端可見。否則,我們有可能失去整個文件系統(tǒng)或者最近的客戶端操作,即使chunkserver沒有任何問題(元數(shù)據(jù)丟了或錯了,chunkserver沒問題也變得有問題了)。因此,我們將它復(fù)制到多個遠程機器,直到日志記錄被flush到本地磁盤以及遠程機器之后才會回復(fù)客戶端。master會捆綁多個日志記錄,一起flush,以減少flush和復(fù)制對整個系統(tǒng)吞吐量的沖擊。
master可以通過重放操作日志來恢復(fù)它的元數(shù)據(jù)狀態(tài)。為了最小化master的啟動時間,日志不能太多(多了重放就需要很久)。所以master會在適當?shù)臅r候執(zhí)行“存檔”,每當日志增長超過一個特定的大小就會執(zhí)行存檔。所以它不需要從零開始回放日志,僅需要從本地磁盤裝載最近的存檔,并回放存檔之后發(fā)生的有限數(shù)量的日志。存檔是一個緊密的類B樹結(jié)構(gòu),它能直接映射到內(nèi)存,不用額外的解析。通過這些手段可以加速恢復(fù)和改進可用性。
因為構(gòu)建一個存檔會消耗點時間,master的內(nèi)部狀態(tài)做了比較精細的結(jié)構(gòu)化設(shè)計,創(chuàng)建一個新的存檔不會延緩持續(xù)到來的請求。master可以快速切換到一個新的日志文件,在另一個后臺線程中創(chuàng)建存檔。這個新存檔能體現(xiàn)切換之前所有的變異結(jié)果。即使一個有幾百萬文件的集群,創(chuàng)建存檔也可以在短時間完成。結(jié)束時,它也會寫入本地和遠程的磁盤。
恢復(fù)元數(shù)據(jù)時,僅僅需要最后完成的存檔和其后產(chǎn)生的日志。老的存檔和日志文件能被自由刪除,不過我們保險起見不會隨意刪除。在存檔期間如果發(fā)生故障(存檔文件爛尾了)也不會影響正確性,因為恢復(fù)代碼能偵測和跳過未完成的存檔。
2.7 一致性模型
GFS松弛的一致性模型能很好的支持我們高度分布式的應(yīng)用,而且實現(xiàn)起來非常簡單高效。我們現(xiàn)在討論GFS的一致性保證。
2.7.1 GFS的一致性保證
文件命名空間變化(比如文件創(chuàng)建)是原子的,只有master能處理此種操作:master中提供了命名空間的鎖機制,保證了原子性的和正確性(章節(jié)4.1);master的操作日志為這些操作定義了一個全局統(tǒng)一的順序(章節(jié)2.6.3)
各種數(shù)據(jù)變異在不斷發(fā)生,被它們改變的文件區(qū)域處于什么狀態(tài)?這取決于變異是否成功了、有沒有并發(fā)變異等各種因素。表1列出了所有可能的結(jié)果。對于文件區(qū)域A,如果所有客戶端從任何副本上讀到的數(shù)據(jù)都是相同的,那A就是一致的。如果A是一致的,并且客戶端可以看到變異寫入的完整數(shù)據(jù),那A就是defined。當一個變異成功了、沒有受到并發(fā)寫的干擾,它寫入的區(qū)域?qū)莇efined(也是一致的):所有客戶端都能看到這個變異寫入的完整數(shù)據(jù)。對同個區(qū)域的多個并發(fā)變異成功寫入,此區(qū)域是一致的,但可能是undefined:所有客戶端看到相同的數(shù)據(jù),但是它可能不會反應(yīng)任何一個變異寫的東西,可能是多個變異混雜的碎片。一個失敗的變異導(dǎo)致區(qū)域不一致(也是undefined):不同客戶端可能看到不同的數(shù)據(jù)在不同的時間點。下面描述我們的應(yīng)用程序如何區(qū)分defined區(qū)域和undefined區(qū)域。
數(shù)據(jù)變異可能是寫操作或者record append。寫操作導(dǎo)致數(shù)據(jù)被寫入一個用戶指定的文件偏移。而record append導(dǎo)致數(shù)據(jù)(record)被原子的寫入GFS選擇的某個偏移(正常情況下是文件末尾,見章節(jié)3.3),GFS選擇的偏移被返回給客戶端,其代表了此record所在的defined區(qū)域的起始偏移量。另外,某些異常情況可能會導(dǎo)致GFS在區(qū)域之間插入了padding或者重復(fù)的record。他們占據(jù)的區(qū)域可認為是不一致的,不過數(shù)據(jù)量不大。
如果一系列變異都成功寫入了,GFS保證發(fā)生變異的文件區(qū)域是defined的,并完整的包含最后一個變異。GFS通過兩點來實現(xiàn):(a) chunk的所有副本按相同的順序來實施變異(章節(jié)3.1);(b) 使用chunk版本數(shù)來偵測任何舊副本,副本變舊可能是因為它發(fā)生過故障、錯過了變異(章節(jié)4.5)。執(zhí)行變異過程時將跳過舊的副本,客戶端調(diào)用master獲取chunk位置時也不會返回舊副本。GFS會盡早的通過垃圾回收處理掉舊的副本。
因為客戶端緩存了chunk位置,所以它們可能向舊副本發(fā)起讀請求。不過緩存項有超時機制,文件重新打開時也會更新。而且,我們大部分的文件是append-only的,這種情況下舊副本最壞只是無法返回數(shù)據(jù)(append-only意味著只增不減也不改,版本舊只意味著會丟數(shù)據(jù)、少數(shù)據(jù)),而不會返回過期的、錯誤的數(shù)據(jù)。一旦客戶端與master聯(lián)系,它將立刻得到最新的chunk位置(不包含舊副本)。
在一個變異成功寫入很久之后,組件的故障仍然可能腐化、破壞數(shù)據(jù)。GFS中,master和所有chunkserver之間會持續(xù)handshake通訊并交換信息,借此master可以識別故障的chunkserver并且通過檢查checksum偵測數(shù)據(jù)腐化(章節(jié)5.2)。一旦發(fā)現(xiàn)此問題,會盡快執(zhí)行一個restore,從合法的副本復(fù)制合法數(shù)據(jù)替代腐化副本(章節(jié)4.3)。一個chunk也可能發(fā)生不可逆的丟失,那就是在GFS反應(yīng)過來采取措施之前,所有副本都被丟失。通常GFS在分鐘內(nèi)就能反應(yīng)。即使出現(xiàn)這種天災(zāi),chunk也只是變得不可用,而不會腐化:應(yīng)用收到清晰的錯誤而不是錯誤的數(shù)據(jù)。
【譯者注】一致性的問題介紹起來難免晦澀枯燥,下面譯者用一些比較淺顯的例子來解釋GFS中的一致、不一致、defined、undefined四種狀態(tài)。
讀者可以想象這樣一個場景,某人和他老婆共用同一個Facebook賬號,同時登陸,同時看到某張照片,他希望將其順時針旋轉(zhuǎn)90度,他老婆希望將其逆時針旋轉(zhuǎn)90度。兩人同時點了修改按鈕,F(xiàn)acebook應(yīng)該聽誰的?俗話說意見相同聽老公的,意見不同聽老婆的。但是Facebook不懂這個算法,當他們重新打開頁面時可能會:1. 都看到圖片順時針旋轉(zhuǎn)了90度;2. 都看到圖片逆時針旋轉(zhuǎn)了90度;3. 其他情況。對于1、2兩種情況,都是可以接受的,小夫妻若來投訴那只能如實相告讓他們自己回去猜拳,不關(guān)Facebook的事兒。因為1、2既滿足一致性(兩人在并發(fā)修改發(fā)生后都一直看到一致相同的內(nèi)容),又滿足defined(內(nèi)容是其中一人寫入的完整數(shù)據(jù))。對于3會有哪些其他情況呢?如果這事兒發(fā)生在單臺電腦的本地硬盤(相當于兩人同時打開一個圖片軟件、編輯同一個圖片、然后并發(fā)提交保存),若不加鎖讓其串行,則可能導(dǎo)致數(shù)據(jù)碎片,以簡單的代碼為例:
File file = new File("D:/temp.txt");FileOutputStream fos1 = new FileOutputStream(file);FileOutputStream fos2 = new FileOutputStream(file);fos1.write('1');fos1.write('2');fos1.write('3');fos2.write('a');fos2.write('b');fos2.write('c');fos1.close();fos2.close();
it知識庫:經(jīng)典論文翻譯導(dǎo)讀之《Google File System》,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。