摘要:寫緩存與寫磁盤先看下寫文件操作的流程結(jié)構(gòu)圖磁盤緩存是物理內(nèi)存的一部分,專門供操作系統(tǒng)用作讀寫磁盤的緩沖之用。這印證了前面的說法,字符串在文件關(guān)閉前只在磁盤緩存里,還未真正寫到磁盤上,所以讀進程無法讀出。
多進程讀寫同一個文件的問題
不考慮文件內(nèi)容的錯亂,多進程是可以同時讀寫一個文件的。當(dāng)一個進程在寫,讀的進程能否讀到最新的內(nèi)容,取決于最新的內(nèi)容是否真正寫到了磁盤上。
寫緩存與寫磁盤先看下寫文件操作的流程結(jié)構(gòu)圖:
磁盤緩存是物理內(nèi)存的一部分,專門供操作系統(tǒng)用作讀寫磁盤的緩沖之用。磁盤緩存與“硬盤自帶的緩存”是不一樣的概念,它的大小是可以動態(tài)設(shè)置的,而不像硬盤緩存在出廠的時候固定就是32M或64M。
我們通常用到的寫文件API,其實是寫到磁盤緩存上,可用python語言做一個實驗:
if opt == "-w": with open("1.txt", "w") as writer: writer.write("hehe ") time.sleep(10) elif opt == "-r": with open("1.txt") as fp: for line in fp: line = line.rstrip() print line
我們在用-w選項寫hehe之后不會立刻關(guān)閉文件,而是sleep了10s,方便使用-r選項去讀文件,讀的時候我們發(fā)現(xiàn),除非文件關(guān)閉,否則讀不出任何內(nèi)容。這印證了前面的說法,hehe字符串在文件關(guān)閉前只在磁盤緩存里,還未真正寫到磁盤上,所以讀進程無法讀出。
如何確保寫到磁盤上而不只是磁盤緩存里呢?python文檔給出了建議:
file.flush() Flush the internal buffer. Note flush() does not necessarily write the file’s data to disk. Use flush() followed by os.fsync() to ensure this behavior.
文檔建議我們flush+fsync,確保內(nèi)容確實更新到了磁盤。
fsync的幫助也指出了這一點:
os.fsync(fd) Force write of file with filedescriptor fd to disk. On Unix, this calls the native fsync() function; on Windows, the MS _commit() function. If you’re starting with a Python file object f, first do f.flush(), and then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk.
所謂的“內(nèi)部緩存”就是磁盤緩存,強制更新到磁盤,linux下用的是大家熟知的fsync,windows下則是_commit函數(shù)。
我們將寫的代碼改成:
if opt == "-w": with open("1.txt", "w") as writer: writer.write("hehe ") writer.flush() os.fsync(writer.fileno()) time.sleep(10)
果然,讀進程就能在文件尚未關(guān)閉時讀到hehe字符串了。
但是,fsync是要慎用的,因為每條內(nèi)容都強制刷新到磁盤,雖然非??煽?,卻會帶來性能的急劇下降,我們可以在上述例子的基礎(chǔ)上改成寫10萬條字符串,對比“普通寫”與“fsync寫”的效率,會發(fā)現(xiàn)后者的耗時是前者的數(shù)千倍甚至是上萬倍!這也正是redis的AOF日志雖然提供了fsync級別的磁盤同步卻不建議我們使用的原因(也因此redis的日志做不到絕對的單點可靠)。
這里還有一個疑問,按python的文檔,flush并不一定能將最新的內(nèi)容更新到磁盤上,我們查看java file API的文檔,發(fā)現(xiàn)也有類似的說法。這是為何?我個人的猜測,flush只是簡單的把磁盤緩存的內(nèi)容放到磁盤驅(qū)動程序的寫請求隊列里就返回,本質(zhì)上是異步的,而fsync除了放內(nèi)容到寫請求隊列還會等待磁盤驅(qū)動程序的返回結(jié)果,本質(zhì)上是同步的。由于fsync還要額外經(jīng)歷:等待寫請求到隊列首部+磁盤驅(qū)動程序調(diào)用磁盤控制器+磁盤控制器寫到物理磁盤等步驟,自然就拖慢了fsync的速度。
進程內(nèi)緩存與磁盤緩存進程內(nèi)緩存指的是我在寫磁盤緩存前,在自己的程序里再做一個緩存,將多條消息累積到一定的大小,再一次提交給磁盤緩存,這樣能提升寫的效率。java里一般要在FileWriter之上再套一層BufferedWriter寫入,就是這個用途,實測下來,也能有一倍的效率提升。
python語言里沒有BufferedWriter,對于10萬條字符串的寫可以考慮別的方法,比如我們可以每500條拼成一個大的字符串再做寫入,實測也有一倍的效率提升。
不過,如同前面的“普通寫”與“fsync寫”一樣,效率的提升不是全無代價,它往往伴隨著可靠性的降低。進程內(nèi)緩存是屬于某個進程的,一旦該進程突然core掉,進程內(nèi)緩存就會丟失,從用戶層面看來,就是我明明已經(jīng)write好了的數(shù)據(jù),很可能并未寫到磁盤里。相比之下,磁盤緩存就更可靠一些,因為它是由操作系統(tǒng)管理的,與進程無關(guān),除非是機器斷電,否則它不會丟失數(shù)據(jù),也就是說,即使我的進程core掉,之前write的內(nèi)容依然可以安全到達磁盤上。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/61906.html
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...
摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:可以通過大數(shù)據(jù)生態(tài)的一系列工具生態(tài)來解決大數(shù)據(jù)問題數(shù)據(jù)分片主要有兩種方式哈希和范圍。哈希的問題是范圍查詢支持不佳,范圍的問題是可能冷熱數(shù)據(jù)不均。 后端好書閱讀與推薦系列文章:后端好書閱讀與推薦后端好書閱讀與推薦(續(xù))后端好書閱讀與推薦(續(xù)二)后端好書閱讀與推薦(續(xù)三)后端好書閱讀與推薦(續(xù)四)后端好書閱讀與推薦(續(xù)五)后端好書閱讀與推薦(續(xù)六) Elasticsearch權(quán)威指南 El...
摘要:可以通過大數(shù)據(jù)生態(tài)的一系列工具生態(tài)來解決大數(shù)據(jù)問題數(shù)據(jù)分片主要有兩種方式哈希和范圍。哈希的問題是范圍查詢支持不佳,范圍的問題是可能冷熱數(shù)據(jù)不均。 后端好書閱讀與推薦系列文章:后端好書閱讀與推薦后端好書閱讀與推薦(續(xù))后端好書閱讀與推薦(續(xù)二)后端好書閱讀與推薦(續(xù)三)后端好書閱讀與推薦(續(xù)四)后端好書閱讀與推薦(續(xù)五)后端好書閱讀與推薦(續(xù)六) Elasticsearch權(quán)威指南 El...
閱讀 1403·2023-04-26 00:10
閱讀 2492·2021-09-22 15:38
閱讀 4105·2021-09-22 15:13
閱讀 3605·2019-08-30 13:11
閱讀 703·2019-08-30 11:01
閱讀 3105·2019-08-29 14:20
閱讀 3269·2019-08-29 13:27
閱讀 1785·2019-08-29 11:33