我需要學習如何在不阻塞其他執行緒的情況下同時更新檔案。讓我解釋一下它應該如何作業、需要以及我認為應該如何實作它,然后我問我的問題:
這是工人的作業方式:
- Worker 是多執行緒的。
- 有一個非常大的檔案(6 TB)。
- 每個執行緒都在更新這個檔案的一部分。
- 每次寫入等于一個或多個磁盤塊(4096 位元組)。
- 沒有兩個工人同時在同一個塊(或同一組塊)上寫入。
需求:
- 執行緒不應該阻塞其他塊(檔案上沒有鎖,或者應該使用盡可能少的鎖)
- 如果發生(任何型別的)故障,如果更新塊損壞,則沒有問題。
- 如果發生(任何型別的)故障,未更新的塊不應損壞。
- 如果檔案寫入成功,我們必須確保它沒有被緩沖并確保實際寫入磁盤(fsync)
- 我可以根據需要將這個大檔案轉換為盡可能多的小檔案(小至 4kb 檔案),但我不想這樣做。處理這么多檔案很困難,需要大量的檔案句柄打開/關閉操作,這對性能有負面影響。
我認為應該如何實施:
我對檔案操作及其在作業系統級別的作業方式不太熟悉,但我認為在單個塊上寫入不應該在發生錯誤時破壞其他塊。所以我認為這段代碼應該可以根據需要完美運行,無需任何更改:
char write_value[] = "...4096 bytes of data...";
int write_block = 12345;
int block_size = 4096;
FILE *fp;
fp = fopen("file.txt","w ");
fseek(fp, write_block * block_size, SEEK_SET);
fputs(write_value, fp);
fsync(fp);
fclose(fp);
問題:
顯然,我試圖了解它應該如何實施。所以歡迎任何建議。特別:
- 如果寫入大檔案的一個塊失敗,損壞其他資料塊的可能性有多大?
- 簡而言之,完善上面的代碼應該考慮哪些事情(根據最后一個問題)?
- 是否可以原子地用另一個檔案/塊替換一個資料塊?(例如 rename() 系統呼叫如何以原子方式將一個檔案替換為另一個檔案,但在塊級別。類似于替換檔案系統中前一個塊的下一個塊地址或其他任何東西)。
- 任何設備/檔案系統/作業系統特定說明?(此代碼將在 CentOS/FreeBSD 上運行(尚未確定),但如果有更好的解決方案,我可以更改作業系統。檔案位于一個 8TB SSD 上)。
uj5u.com熱心網友回復:
執行緒不應該阻塞其他塊(檔案上沒有鎖,或者應該使用盡可能少的鎖)
您的代碼示例使用fseek后跟fwrite. 如果不鎖定在這兩者之間,您就會遇到競爭條件,因為另一個執行緒可能會在兩者之間跳轉。合理的解決方案有以下三種:
- 使用
flockfile,然后是常規fseek,fwrite_unlocked然后是funlock。這些是 POSIX-2001 標準 - 每個執行緒使用單獨的檔案句柄
- 使用
preadandpwrite做IO,不用擔心seek位置
選項 3 最適合您。
您還可以使用異步 IO<aio.h>來處理多執行緒。它基本上與呼叫pwrite大多數 Unix 實作的執行緒池一起作業。
如果發生(任何型別的)故障,如果更新塊損壞,則沒有問題
我理解這意味著在任何故障狀態下都不應該有檔案損壞。據我所知,覆寫資料時這是不可能的。當系統在寫入命令中失敗時,無法保證寫入了多少位元組,至少在與檔案系統無關的版本中無法保證。
相反,您可以做的類似于資料庫事務:將新內容寫入檔案中的新位置。然后你做一個 fsync 以確保它在磁盤上。然后覆寫一個標題以指向新位置。如果您在寫入標頭之前崩潰,您的崩潰恢復將看到舊內容。如果標題被寫入,您會看到新內容。但是,我不是這個領域的專家。最后的標題更新有點像揮手致意。
如果發生(任何型別的)故障,未更新的塊不應損壞。
應該沒事
如果檔案寫入成功,我們必須確保它沒有被緩沖并確保實際寫入磁盤(fsync)
您的示例代碼稱為fsync,但在此之前忘記fflush了。或者您將檔案緩沖區設定為無緩沖使用setvbuf
我可以根據需要將這個大檔案轉換為盡可能多的小檔案(小至 4kb 檔案),但我不想這樣做。處理這么多檔案很困難,需要大量的檔案句柄打開/關閉操作,這對性能有負面影響。
無論如何,許多電話fsync都會扼殺你的表現。除了重新實作資料庫事務之外,這似乎是實作最大崩潰恢復的最佳選擇。該模式有據可查且易于理解:
- 在與要覆寫的資料相同的檔案系統上創建一個新的臨時檔案
- 讀取-復制-將舊內容更新到新的臨時檔案
- 呼叫 fsync
- 將新檔案重命名為舊檔案
單個檔案系統上的重命名是原子的。因此,此程序將確保在崩潰后您獲得舊資料或新資料。
uj5u.com熱心網友回復:
如果寫入大檔案的一個塊失敗,損壞其他資料塊的可能性有多大?
沒有任何。
是否可以原子地用另一個檔案/塊替換一個資料塊?(例如 rename() 系統呼叫如何以原子方式將一個檔案替換為另一個檔案,但在塊級別。類似于替換檔案系統中前一個塊的下一個塊地址或其他任何東西)。
不。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/505047.html
上一篇:Python問題寫入txt檔案
