国产99久久精品_欧美日本韩国一区二区_激情小说综合网_欧美一级二级视频_午夜av电影_日本久久精品视频

最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

如何使用Python進(jìn)行穩(wěn)定可靠的文件操作

來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 14:15:07
文檔

如何使用Python進(jìn)行穩(wěn)定可靠的文件操作

如何使用Python進(jìn)行穩(wěn)定可靠的文件操作:程序需要更新文件。雖然大部分程序員知道在執(zhí)行I/O的時(shí)候會發(fā)生不可預(yù)期的事情,但是我經(jīng)常看到一些異常幼稚的代碼。在本文中,我想要分享一些如何在Python代碼中改善I/O可靠性的見解。 考慮下述Python代碼片段。對文件中的數(shù)據(jù)進(jìn)行某些操作,然后將結(jié)果保存
推薦度:
導(dǎo)讀如何使用Python進(jìn)行穩(wěn)定可靠的文件操作:程序需要更新文件。雖然大部分程序員知道在執(zhí)行I/O的時(shí)候會發(fā)生不可預(yù)期的事情,但是我經(jīng)常看到一些異常幼稚的代碼。在本文中,我想要分享一些如何在Python代碼中改善I/O可靠性的見解。 考慮下述Python代碼片段。對文件中的數(shù)據(jù)進(jìn)行某些操作,然后將結(jié)果保存
  程序需要更新文件。雖然大部分程序員知道在執(zhí)行I/O的時(shí)候會發(fā)生不可預(yù)期的事情,但是我經(jīng)常看到一些異常幼稚的代碼。在本文中,我想要分享一些如何在Python代碼中改善I/O可靠性的見解。

  考慮下述Python代碼片段。對文件中的數(shù)據(jù)進(jìn)行某些操作,然后將結(jié)果保存回文件中:

with open(filename) as f:
 input = f.read()
output = do_something(input)
with open(filename, 'w') as f:
 f.write(output)

  看起來很簡單吧?可能看起來并不像乍一看這么簡單。我在產(chǎn)品服務(wù)器中調(diào)試應(yīng)用,經(jīng)常會出現(xiàn)奇怪的行為。

  這是我看過的失效模式的例子:

  • 失控的服務(wù)器進(jìn)程溢出大量日志,磁盤被填滿。write()在截?cái)辔募髵伋霎惓#募兂煽盏摹?/p>

  • 應(yīng)用的幾個(gè)實(shí)例并行執(zhí)行。在各個(gè)實(shí)例結(jié)束之后,因?yàn)榛旌狭硕鄠€(gè)實(shí)例的輸出,文件內(nèi)容最終變成了天書。

  • 在完成了寫操作之后,應(yīng)用會觸發(fā)一些后續(xù)操作。幾秒鐘后斷電。在我們重啟了服務(wù)器之后,我們再一次看到了舊的文件內(nèi)容。已經(jīng)傳遞給其它應(yīng)用的數(shù)據(jù)與我們在文件中看到的不再一致。

  •   下面沒有什么新的內(nèi)容。本文的目的是為在系統(tǒng)編程方面缺少經(jīng)驗(yàn)的Python開發(fā)者提供常見的方法和技術(shù)。我將會提供代碼例子,使得開發(fā)者可以很容易的將這些方法應(yīng)用到自己的代碼中。

     “可靠性”意味著什么?

      廣義的講,可靠性意味著在所有規(guī)定的條件下操作都能執(zhí)行它所需的函數(shù)。至于文件的操作,這個(gè)函數(shù)就是創(chuàng)建,替換或者追加文件的內(nèi)容的問題。這里可以從數(shù)據(jù)庫理論上獲得靈感。經(jīng)典的事務(wù)模型的ACID性質(zhì)作為指導(dǎo)來提高可靠性。

      開始之前,讓我們先看看我們的例子怎樣和ACID4個(gè)性質(zhì)扯上關(guān)系:

  • 原子性(Atomicity)要求這個(gè)事務(wù)要么完全成功,要么完全失敗。在上面的實(shí)例中,磁盤滿了可能導(dǎo)致部分內(nèi)容寫入文件。另外,如果正當(dāng)在寫入內(nèi)容時(shí)其它程序又在讀取文件,它們可能獲得是部分完成的版本,甚至?xí)?dǎo)致寫錯誤

  • 一致性(Consistency) 表示操作必須從系統(tǒng)的一個(gè)狀態(tài)到另一個(gè)狀態(tài)。一致性可以分為兩部分:內(nèi)部和外部一致性。內(nèi)部一致性是指文件的數(shù)據(jù)結(jié)構(gòu)是一致的。外部一致性是指文件的內(nèi)容與它相關(guān)的數(shù)據(jù)是相符合的。在這個(gè)例子中,因?yàn)槲覀儾涣私膺@個(gè)應(yīng)用,所以很難推斷是否符合一致性。但是因?yàn)橐恢滦孕枰有裕覀冎辽倏梢哉f沒有保證內(nèi)部一致性。

  • 隔離性(Isolation)如果在并發(fā)的執(zhí)行事務(wù)中,多個(gè)相同的事務(wù)導(dǎo)致了不同的結(jié)果,就違反了隔離性。很明顯上面的代碼對操作失敗或者其它隔離性失敗都沒有保護(hù)。

  • 持久性(Durability)意味著改變是持久不變的。在我們告訴用戶成功之前,我們必須確保我們的數(shù)據(jù)存儲是可靠的并且不只是一個(gè)寫緩存。上面的代碼已經(jīng)成功寫入數(shù)據(jù)的前提是假設(shè)我們調(diào)用write()函數(shù),磁盤I/O就立即執(zhí)行。但是POSIX標(biāo)準(zhǔn)是不保證這個(gè)假設(shè)的。

  •  盡可能使用數(shù)據(jù)庫系統(tǒng)

      如果我們能夠獲得ACID 四個(gè)性質(zhì),那么我們增加可靠性方面取得了長遠(yuǎn)發(fā)展。但是這需要很大的編碼功勞。為什么重復(fù)發(fā)明輪子?大多數(shù)數(shù)據(jù)庫系統(tǒng)已經(jīng)有ACID事務(wù)了。

      可靠性數(shù)據(jù)存儲已經(jīng)是一個(gè)已解決的問題。如果你需要可靠性存儲,請使用數(shù)據(jù)庫。很可能,沒有幾十年的功夫,你自己解決這方面的能力沒有那些已經(jīng)專注這方面好些年的人好。如果你不想安裝一個(gè)大數(shù)據(jù)庫服務(wù)器,那么你可以使用sqlite,它具有ACID事務(wù),很小,免費(fèi)的,而且它包含在Python的標(biāo)準(zhǔn)庫中。

      文章本該在這里就結(jié)束的,但是還有一些有根有據(jù)的原因,就是不使用數(shù)據(jù)。它們通常是文件格式或者文件位置約束。這兩個(gè)在數(shù)據(jù)庫系統(tǒng)中都不好控制。理由如下:

  • 我們必須處理其它應(yīng)用產(chǎn)生的固定格式或者在固定位置的文件,

  • 我們必須為了其它應(yīng)用的消耗而寫文件(和應(yīng)用了同樣的限制條件)

  • 我們的文件必須方便人閱讀或者修改。

  •   ...等等。你懂的。

      如果我們自己動手實(shí)現(xiàn)可靠的文件更新,那么這里有一些編程技術(shù)供參考。下面我將展示四種常見的操作文件更新模式。在那之后,我會討論采取哪些步驟在每個(gè)文件更新模式下滿足ACID性質(zhì)。

     文件更新模式

      文件可以以多種方式更新,但是我認(rèn)為至少有四種常見的模式。這四種模式將做為本文剩余部分的基礎(chǔ)。

      截?cái)?寫

      這可能是最基本的模式。在下述例子中,假設(shè)的域模型代碼讀數(shù)據(jù),執(zhí)行一些計(jì)算,然后以寫模式重新打開存在的文件:

    with open(filename, 'r') as f:
     model.read(f)
    model.process()
    with open(filename, 'w') as f:
     model.write(f)

      此模式的一個(gè)變種以讀寫模式打開文件(Python中的“加”模式),尋找到開始的位置,顯式調(diào)用truncate(),重寫文件內(nèi)容。

    with open(filename, 'a+') as f:
     f.seek(0)
     model.input(f.read())
     model.compute()
     f.seek(0)
     f.truncate()
     f.write(model.output())

      該變種的優(yōu)勢是只打開文件一次,始終保持文件打開。舉例來說,這樣可以簡化加鎖。

      寫-替換

      另外一種廣泛使用的模式是將新內(nèi)容寫到臨時(shí)文件,之后替換原始文件:

    with tempfile.NamedTemporaryFile(
     'w', dir=os.path.dirname(filename), delete=False) as tf:
     tf.write(model.output())
     tempname = tf.name
    os.rename(tempname, filename)

      該方法與截?cái)?寫方法相比對錯誤更具有魯棒性。請看下面對原子性和一致性的討論。很多應(yīng)用使用該方法。

      這兩個(gè)模式很常見,以至于linux內(nèi)核中的ext4文件系統(tǒng)甚至可以自動檢測到這些模式,自動修復(fù)一些可靠性缺陷。但是不要依賴這一特性:你并不是總是使用ext4,而且管理員可能會關(guān)掉這一特性。

      追加

      第三種模式就是追加新數(shù)據(jù)到已存在的文件:

    with open(filename, 'a') as f:
     f.write(model.output())

      這個(gè)模式用來寫日志文件和其它累積處理數(shù)據(jù)的任務(wù)。從技術(shù)上講,它的顯著特點(diǎn)就是極其簡單。一個(gè)有趣的擴(kuò)展應(yīng)用就是常規(guī)操作中只通過追加操作更新,然后定期重新整理文件,使之更緊湊。

      Spooldir

      這里我們將目錄做為邏輯數(shù)據(jù)存儲,為每條記錄創(chuàng)建新的唯一命名的文件:

    with open(unique_filename(), 'w') as f:
     f.write(model.output())

      該模式與附加模式一樣具有累積的特點(diǎn)。一個(gè)巨大的優(yōu)勢是我們可以在文件名中放入少量元數(shù)據(jù)。舉例來說,這可以用于傳達(dá)處理狀態(tài)的信息。spooldir模式的一個(gè)特別巧妙的實(shí)現(xiàn)是maildir格式。maildirs使用附加子目錄的命名方案,以可靠的、無鎖的方式執(zhí)行更新操作。md和gocept.filestore庫為maildir操作提供了方便的封裝。

      如果你的文件名生成不能保證唯一的結(jié)果,甚至有可能要求文件必須實(shí)際上是新的。那么調(diào)用具有合適標(biāo)志的低等級os.open():

    fd = os.open(filename, os.O_WRONLY | os.O_CREAT| os.O_EXCL, 0o666)
    with os.fdopen(fd, 'w') as f:
     f.write(...)

      在以O(shè)_EXCL方式打開文件后,我們用os.fdopen將原始的文件描述符轉(zhuǎn)化為普通的Python文件對象。

     應(yīng)用ACID屬性到文件更新

      下面,我將嘗試加強(qiáng)文件更新模式。反過來讓我們看看可以做些什么來滿足ACID屬性。我將會盡可能保持簡單,因?yàn)槲覀儾⒉皇且獙懸粋€(gè)完整的數(shù)據(jù)庫系統(tǒng)。請注意本節(jié)的材料并不徹底,但是可以為你自己的實(shí)驗(yàn)提供一個(gè)好的起點(diǎn)。

      原子性

      寫-替換模式提供了原子性,因?yàn)榈讓拥膐s.rename()是原子性的。這意味著在任意給定時(shí)間點(diǎn),進(jìn)程或者看到舊的文件,或者看到新的文件。該模式對寫錯誤具有天然的魯棒性:如果寫操作觸發(fā)異常,重命名操作就不會被執(zhí)行,所有就沒有用損壞的新文件覆蓋正確的舊文件的風(fēng)險(xiǎn)。

      附加模式并不是原子性的,因?yàn)橛懈郊硬煌暾涗浀娘L(fēng)險(xiǎn)。但是有個(gè)技巧可以使更新具有原子性:為每個(gè)寫操作標(biāo)注校驗(yàn)和。之后讀日志的時(shí)候,忽略所有沒有有效校驗(yàn)和的記錄。以這種方式,只有完整的記錄才會被處理。在下面的例子中,應(yīng)用做周期性的測量,每次在日志中附加一行JSON記錄。我們計(jì)算記錄的字節(jié)表示形式的CRC32校驗(yàn)和,然后附加到同一行:

    with open(logfile, 'ab') as f:
     for i in range(3):
     measure = {'timestamp': time.time(), 'value': random.random()}
     record = json.dumps(measure).encode()
     checksum = '{:8x}'.format(zlib.crc32(record)).encode()
     f.write(record + b' ' + checksum + b'
    ')

      該例子代碼通過每次創(chuàng)建隨機(jī)值模擬測量。

    $ cat log
    {"timestamp": 1373396987.258189, "value": 0.9360123151217828} 9495b87a
    {"timestamp": 1373396987.25825, "value": 0.40429005476999424} 149afc22
    {"timestamp": 1373396987.258291, "value": 0.232021160265939} d229d937

      想要處理這個(gè)日志文件,我們每次讀一行記錄,分離校驗(yàn)和,與讀到的記錄比較。

    with open(logfile, 'rb') as f:
     for line in f:
     record, checksum = line.strip().rsplit(b' ', 1)
     if checksum.decode() == '{:8x}'.format(zlib.crc32(record)):
     print('read measure: {}'.format(json.loads(record.decode())))
     else:
     print('checksum error for record {}'.format(record))

      現(xiàn)在我們通過截?cái)嘧詈笠恍心M被截?cái)嗟膶懖僮鳎?/p>

    $ cat log
    {"timestamp": 1373396987.258189, "value": 0.9360123151217828} 9495b87a
    {"timestamp": 1373396987.25825, "value": 0.40429005476999424} 149afc22
    {"timestamp": 1373396987.258291, "value": 0.23202

      當(dāng)讀日志的時(shí)候,最后不完整的一行被拒絕:

    $ read_checksummed_log.py log
    read measure: {'timestamp': 1373396987.258189, 'value': 0.9360123151217828}
    read measure: {'timestamp': 1373396987.25825, 'value': 0.40429005476999424}
    checksum error for record b'{"timestamp": 1373396987.258291, "value":'

      添加校驗(yàn)和到日志記錄的方法被用于大量應(yīng)用,包括很多數(shù)據(jù)庫系統(tǒng)。

      spooldir中的單個(gè)文件也可以在每個(gè)文件中添加校驗(yàn)和。另外一個(gè)可能更簡單的方法是借用寫-替換模式:首先將文件寫到一邊,然后移到最終的位置。設(shè)計(jì)一個(gè)保護(hù)正在被消費(fèi)者處理的文件的命名方案。在下面的例子中,所有以.tmp結(jié)尾的文件都會被讀取程序忽略,因此在寫操作的時(shí)候可以安全的使用。

    newfile = generate_id()
    with open(newfile + '.tmp', 'w') as f:
     f.write(model.output())
    os.rename(newfile + '.tmp', newfile)

      最后,截?cái)?寫是非原子性的。很遺憾我不能提供滿足原子性的變種。在執(zhí)行完截取操作后,文件是空的,還沒有新內(nèi)容寫入。如果并發(fā)的程序現(xiàn)在讀文件或者有異常發(fā)生,程序中止,我們既看不久的版本也看不到新的版本。

      一致性

      我談?wù)摰年P(guān)于原子性的大部分內(nèi)容也可以應(yīng)用到一致性。實(shí)際上,原子性更新是內(nèi)部一致性的前提條件。外部一致性意味著同步更新幾個(gè)文件。這不容易做到,鎖文件可以用來確保讀寫訪問互不干涉。考慮某目錄下的文件需要互相保持一致。常用的模式是指定鎖文件,用來控制對整個(gè)目錄的訪問。

      寫程序的例子:

    with open(os.path.join(dirname, '.lock'), 'a+') as lockfile:
     fcntl.flock(lockfile, fcntl.LOCK_EX)
     model.update(dirname)

      讀程序的例子:

    with open(os.path.join(dirname, '.lock'), 'a+') as lockfile:
     fcntl.flock(lockfile, fcntl.LOCK_SH)
     model.readall(dirname)

      該方法只有控制所有讀程序才生效。因?yàn)槊看沃挥幸粋€(gè)寫程序活動(獨(dú)占鎖阻塞所有共享鎖),所有該方法的可擴(kuò)展性有限。

      更進(jìn)一步,我們可以對整個(gè)目錄應(yīng)用寫-替換模式。這涉及為每次更新創(chuàng)建新的目錄,更新完成后改變符合鏈接。舉例來說,鏡像應(yīng)用維護(hù)一個(gè)包含壓縮包和列出了文件名、文件大小和校驗(yàn)和的索引文件的目錄。當(dāng)上流的鏡像更新,僅僅隔離地對壓縮包和索引文件進(jìn)項(xiàng)原子性更新是不夠的。相反,我們需要同時(shí)提供壓縮包和索引文件以免校驗(yàn)和不匹配。為了解決這個(gè)問題,我們?yōu)槊看紊删S護(hù)一個(gè)子目錄,然后改變符號鏈接激活該次生成。

    mirror
    |-- 483
    | |-- a.tgz
    | |-- b.tgz
    | `-- index.json
    |-- 484
    | |-- a.tgz
    | |-- b.tgz
    | |-- c.tgz
    | `-- index.json
    `-- current -> 483

      新的生成484正在被更新的過程中。當(dāng)所有壓縮包準(zhǔn)備好,索引文件更新后,我們可以用一次原子調(diào)用os.symlink()來切換current符號鏈接。其它應(yīng)用總是或者看到完全舊的或者完全新的生成。讀程序需要使用os.chdir()進(jìn)入current目錄,很重要的是不要用完整路徑名指定文件。否在當(dāng)讀程序打開current/index.json,然后打開current/a.tgz,但是同時(shí)符號鏈接已經(jīng)改變時(shí)就會出現(xiàn)競爭條件。

      隔離性

      隔離性意味著對同一文件的并發(fā)更新是可串行化的——存在一個(gè)串行調(diào)度使得實(shí)際執(zhí)行的并行調(diào)度返回相同的結(jié)果。“真實(shí)的”數(shù)據(jù)庫系統(tǒng)使用像MVCC這種高級技術(shù)維護(hù)可串行性,同時(shí)允許高等級的可并行性。回到我們的場景,我們最后使用加鎖來串行文件更新。

      對截?cái)?寫更新進(jìn)行加鎖是容易的。僅僅在所有文件操作前獲取一個(gè)獨(dú)占鎖就可以。下面的例子代碼從文件中讀取一個(gè)整數(shù),然后遞增,最后更新文件:

    def update():
     with open(filename, 'r+') as f:
     fcntl.flock(f, fcntl.LOCK_EX)
     n = int(f.read())
     n += 1
     f.seek(0)
     f.truncate()
     f.write('{}
    '.format(n))

      使用寫-替換模式加鎖更新就有點(diǎn)兒麻煩啦。像 截?cái)?寫那樣使用鎖可能導(dǎo)致更新沖突。某個(gè)幼稚的實(shí)現(xiàn)可能看起來像這樣:

    def update():
     with open(filename) as f:
     fcntl.flock(f, fcntl.LOCK_EX)
     n = int(f.read())
     n += 1
     with tempfile.NamedTemporaryFile(
     'w', dir=os.path.dirname(filename), delete=False) as tf:
     tf.write('{}
    '.format(n))
     tempname = tf.name
     os.rename(tempname, filename)

      這段代碼有什么問題呢?設(shè)想兩個(gè)進(jìn)程競爭更新某個(gè)文件。第一個(gè)進(jìn)程運(yùn)行在前面,但是第二個(gè)進(jìn)程阻塞在fcntl.flock()調(diào)用。當(dāng)?shù)谝粋€(gè)進(jìn)程替換了文件,釋放了鎖,現(xiàn)在在第二個(gè)進(jìn)程中打開的文件描述符指向了一個(gè)包含舊內(nèi)容的“幽靈”文件(任意路徑名都不可達(dá))。想要避免這個(gè)沖突,我們必須檢查打開的文件是否與fcntl.flock()返回的相同。所以我寫了一個(gè)新的LockedOpen上下文管理器來替換內(nèi)建的open上下文。來確保我們實(shí)際打開了正確的文件:

    class LockedOpen(object):
    
     def __init__(self, filename, *args, **kwargs):
     self.filename = filename
     self.open_args = args
     self.open_kwargs = kwargs
     self.fileobj = None
    
     def __enter__(self):
     f = open(self.filename, *self.open_args, **self.open_kwargs)
     while True:
     fcntl.flock(f, fcntl.LOCK_EX)
     fnew = open(self.filename, *self.open_args, **self.open_kwargs)
     if os.path.sameopenfile(f.fileno(), fnew.fileno()):
     fnew.close()
     break
     else:
     f.close()
     f = fnew
     self.fileobj = f
     return f
    
     def __exit__(self, _exc_type, _exc_value, _traceback):
     self.fileobj.close()
     def update(self):
     with LockedOpen(filename, 'r+') as f:
     n = int(f.read())
     n += 1
     with tempfile.NamedTemporaryFile(
     'w', dir=os.path.dirname(filename), delete=False) as tf:
     tf.write('{}
    '.format(n))
     tempname = tf.name
     os.rename(tempname, filename)

      給追加更新上鎖如同給截?cái)?寫更新上鎖一樣簡單:需要一個(gè)排他鎖,然后追加就完成了。需要長期運(yùn)行的會將文件長久的打開的進(jìn)程,可以在更新時(shí)釋放鎖,讓其它進(jìn)入。

      spooldir模式有個(gè)很優(yōu)美的性質(zhì)就是它不需要任何鎖。此外,你建立在使用靈活的命名模式和一個(gè)健壯的文件名分代。郵件目錄規(guī)范就是一個(gè)spooldir模式的好例子。它可以很容易的適應(yīng)其它情況,不僅僅是處理郵件。

      持久性

      持久性有點(diǎn)特殊,因?yàn)樗粌H依賴于應(yīng)用,也與OS和硬件配置有關(guān)。理論上來說,我們可以假定,如果數(shù)據(jù)沒有到達(dá)持久存儲,os.fsync()或os.fdatasync()調(diào)用就沒有返回結(jié)果。在實(shí)際情況中,我們有可能會遇到幾個(gè)問題:我們可能會面對不完整的fsync實(shí)現(xiàn),或者糟糕的磁盤控制器配置,它們都無法提供任何持久化的保證。有一個(gè)來自 MySQL 開發(fā)者 的討論對哪里會發(fā)生錯誤進(jìn)行了詳盡的討論。有些像PostgreSQL 之類的數(shù)據(jù)庫系統(tǒng),甚至提供了持久化機(jī)制的選擇 ,以便管理員在運(yùn)行時(shí)刻選擇最佳的一個(gè)。然而不走運(yùn)的人只能使用os.fsync(),并期待它可以被正確的實(shí)現(xiàn)。

      通過截?cái)?寫模式,在結(jié)束寫操作以后關(guān)閉文件以前,我們需要發(fā)送一個(gè)同步信號。注意通常這還牽涉到另一個(gè)層次的寫緩存。glibc 緩存 甚至?xí)趯懖僮鱾鬟f到內(nèi)核以前,在進(jìn)程內(nèi)部攔住它。同樣為了得到空的glibc緩存,我們需要在同步以前對它flush():

    with open(filename, 'w') as f:
     model.write(f)
     f.flush()
     os.fdatasync(f)

      要不,你也可以帶參數(shù)-u調(diào)用Python,以此為所有的文件I/O獲得未緩沖的寫。

      大多數(shù)時(shí)候相較os.fsync()我更喜歡os.fdatasync(),以此避免同步元數(shù)據(jù)的更新(所有權(quán)、大小、mtime…)。元數(shù)據(jù)的更新可最終導(dǎo)致磁盤I/O搜索操作,這會使整個(gè)過程慢不少。

      對寫-替換風(fēng)格更新使用同樣的技巧只是成功了一半。我們得確保在代替舊文件之前,新寫入文件的內(nèi)容已經(jīng)寫入了非易失性存儲器上了,但是替換操作怎么辦?我們不能保證那個(gè)目錄更新是否執(zhí)行的剛剛好。在網(wǎng)絡(luò)上有很多關(guān)于怎么讓同步目錄更新的長篇大論。但是在我們這種情況,舊文件和新文件都在同一個(gè)目錄下,我們可以使用簡單的解決方案來逃避這個(gè)這題。

    os.rename(tempname, filename)
    dirfd = os.open(os.path.dirname(filename), os.O_DIRECTORY)
    os.fsync(dirfd)
    os.close(dirfd)

      我們調(diào)用底層的os.open()來打開目錄(Python自帶的open()方法不支持打開目錄),然后在目錄文件描述符上執(zhí)行os.fsync()。

      對待追加更新和我以及說過的截?cái)?寫是相似的。

      spooldir模式與寫-替換模式同樣的目錄同步問題。幸運(yùn)地是,可以使用同樣的解決方案:第一步同步文件,然后同步目錄。

     總結(jié)

      這使可靠的更新文件成為可能。我已經(jīng)演示了滿足ACID的四大性質(zhì)。這些展示的實(shí)例代碼充當(dāng)一個(gè)工具箱。掌握這編程技術(shù)最大的滿足你的需求。有時(shí),你并不需要滿足所有的ACID性質(zhì),可能僅僅需要一到兩個(gè)。我希望這篇文章可以幫助你去做已充分了解的決定,什么該去實(shí)現(xiàn)以及什么該舍棄。

      英文來源:http://blog.gocept.com/2013/07/15/reliable-file-updates-with-python/

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    如何使用Python進(jìn)行穩(wěn)定可靠的文件操作

    如何使用Python進(jìn)行穩(wěn)定可靠的文件操作:程序需要更新文件。雖然大部分程序員知道在執(zhí)行I/O的時(shí)候會發(fā)生不可預(yù)期的事情,但是我經(jīng)常看到一些異常幼稚的代碼。在本文中,我想要分享一些如何在Python代碼中改善I/O可靠性的見解。 考慮下述Python代碼片段。對文件中的數(shù)據(jù)進(jìn)行某些操作,然后將結(jié)果保存
    推薦度:
    標(biāo)簽: 文件 操作 使用
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 精品国产一区二区二三区在线观看 | 国产精品高清一区二区三区 | 国产手机视频在线观看 | 久久99久久99精品 | 亚洲高清一区二区三区 | 国产成人精品日本亚洲语音2 | 伊人久久婷婷 | 免费国产在线视频 | 国产一区二区三区在线看 | 久久久国产精品视频 | 欧美国产成人精品一区二区三区 | 亚洲免费久久 | 国产一区二区三区不卡免费观看 | 二区三区不卡不卡视频 | 亚洲青草 | 日韩欧美国产综合 | 中文字幕第一页亚洲 | 日本a黄 | 国产成人黄网址在线视频 | 免费一区二区三区在线视频 | 国产成人精品一区二区免费视频 | 亚洲国产成人久久综合区 | 欧美黄色网页 | 精品国产网站 | 亚洲成人一区 | 久久久久久久岛国免费播放 | 国产香蕉视频在线 | 老司机精品视频一区二区 | 国产在线日韩 | 亚洲小视频在线 | 91精品国产91久久久久久 | 国产成人久久精品激情91 | 国产激情视频一区二区三区 | 欧美精品午夜久久久伊人 | 日韩精品 电影一区 亚洲高清 | 国产成人精品一区二区免费视频 | 国产淫语打电话对白在线播放 | 亚洲欧美自拍偷拍 | 日韩欧美色视频 | 国产在线精品观看一区 | 一区二区免费在线观看 |