以下命令大約需要10分鐘才能輸出結果
find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head
我怎樣才能提高它的性能?
答案1
這已經是相當優化了。如果不了解更多細節,就很難知道瓶頸是什麼:
- 儲存類型(HD、SSD、網路、RAID)
- 匹配文件的數量和平均大小
- 目錄和其他不符檔案的數量
- 每行的字段數
- 線的平均長度
在任何情況下你都可以做的事情:
- 如果您的/支持,請替換
-print | xargs為-exec cmd {} +或。不僅是錯誤的,而且還更昂貴,因為需要解碼字元以找出哪些字元是空白並進行一些昂貴的引用處理。-print0 | xargs -r0findxargs-print | xargsxargs - 將語言環境固定為 C (
export LC_ALL=C)。由於此處涉及的所有字元(|以及檔案內容的十進制數字和檔案名稱的拉丁字母、句點和下劃線)都是可移植字元集的一部分,因此如果您的字元集是UTF-8 或其他一些多位元組字元集,請切換具有單字節字元集的 C 語言將為find和awk。 - 將部分簡化
awk為:awk -F "|" '$14 == "20160920100643" && $22 == "567094398953"'. - 由於您要將輸出透過管道傳輸到
head,您可能需要停用 的輸出緩衝,awk以便它儘早輸出這 10 行。通過gawk或mawk,您可以使用fflush()它。或者你可以加一個if (++n == 10) exitinawk.
總結:
(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -exec zcat {} + |
awk -F "|" '$14 == "20160920100643" && $22 == "567094398953" {
print; if (++n == 10) exit}')
如果 CPU 是瓶頸,在多核心 GNU 系統上,您可以嘗試:
(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -print0 |
xargs -r0P 4 -n 100 sh -c '
zcat "$@" |
awk -F "|" "\$14 == "20160920100643" && \$22 == "567094398953" {
print; fflush()}"' sh | head)
zcat | awk對 100 個檔案批次並行執行 4 個作業。
如果這20160920100643是一個時間戳,您可能想要排除在此之前最後修改的檔案。對於 GNU 或 BSD find,新增-newermt '2016-09-20 10:06:42'.
如果行有大量字段,您會因awk拆分行並分配如此多的$n字段而受到懲罰。使用僅考慮前 22 個欄位的方法可以加快速度:
grep -E '^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)'
而不是awk命令。使用 GNU grep,新增--line-buffered選項以在平行方法中儘早輸出行,或-m 10在非平行方法中在 10 個匹配後停止。
總而言之,如果 CPU 是瓶頸,並且您的系統上至少有 4 個 CPU 核心,並且至少有 400 個 muc* 文件,並且您使用的是 GNU 系統(通常grep比 GNU 快得多awk):
(export LC_ALL=C
find . -name "muc*_*_20160920_*.unl*" -newermt '2016-09-20 10:06:42' -print0 |
xargs -r0P 4 -n 100 sh -c '
zcat "$@" |
grep --line-buffered -E \
"^([^|]*\|){13}20160920100643(\|[^|]*){7}\|567094398953(\||$)"
' sh | head)
請注意,在平行方法中,您可能會得到命令相互混合的輸出grep(儘管使用行緩衝並且提供的行小於幾千位元組大,但應保留行邊界)。
答案2
@Stéphane Chazelas 的回答提供了有關如何優化命令管道的大量詳細信息
find . -name "muc*_*_20160920_*.unl*" | xargs zcat |
awk -F "|" '{if($14=="20160920100643" && $22=="567094398953") print $0}'| head
我將提供另一種方法來解決這個問題,您可以實際測量自己在哪裡花費最多的時間。一旦找到時間花在哪裡,您就可以決定如何處理它。如果你想提高 10 分鐘的運行時間,優化一個需要 2 秒的步驟幾乎是沒有用的。
當我查看命令管道時,有三件事引起了我的注意:
find .- 目錄結構是什麼樣的?每個目錄有多少個檔案?該目錄是運行命令的系統的本地目錄嗎?遠端檔案系統將是很多慢點。-name "muc*_*_20160920_*.unl*"- 目錄結構中所有檔案名稱的接近程度如何?它們是否都與名稱“接近”並且難以/CPU 密集型匹配?因為每一個目錄樹中的檔案必須從磁碟讀取其名稱並與模式進行比較。xargs zcat-xargs在我看來,這並不是一個太大的效能問題,特別是與find上述問題及其zcat本身相比。即使是 10,000 個甚至 10,000,000 個檔案名,與所花費的時間相比,僅傳遞和解析名稱所花費的時間幾乎可以忽略不計發現名稱,然後打開並解壓縮所有檔案本身。文件有多大?因為你正在解壓縮整個每一個與您的檔案名稱模式相符的檔案find。
您如何確定主要的效能問題是什麼?測量管道中每個命令的效能。 (看https://stackoverflow.com/questions/13294554/how-to-use-gnu-time-with-pipeline有關對整個管道進行計時的詳細資訊。
/usr/bin/time find .- 這告訴您運行目錄樹需要多長時間。如果速度很慢,則需要更好的儲存系統。 刷新你的檔案系統快取在對其進行計時以獲得最壞情況測量之前,然後再次運行計時find並查看快取對效能的影響有多大。如果該目錄不是本機目錄,請嘗試在檔案所在的實際系統上執行該指令。
/usr/bin/time find . -name "muc*_*_20160920_*.unl*"- 這將告訴您模式匹配檔案名稱需要多長時間。再次刷新檔案系統快取並運行兩次。
/usr/bin/time bash -c "find . -name 'muc*_*_20160920_*.unl*' | xargs zcat > /dev/null"- 我懷疑這是您的管道長時間運行時間的主要組成部分。如果這是問題所在,那麼並行化zcat每個 Stéphane Chazelas 答案的命令可能是最好的答案。
繼續將原始命令管道中的步驟添加到正在測試的管道中,直到您找到您花費最多時間的地方。我再次懷疑這就是zcat步驟。如果是這樣,zcat@Stéphane Chazelas 發布的並行化也許會有所幫助。
並行化zcat可能沒有幫助——甚至可能傷害性能並減慢處理速度。由於zcat一次只運行一個,IO 可能會處於良好的流模式,從而最大限度地減少磁碟尋道。當多個zcat進程同時運行時,IO 操作可能會發生競爭,並且實際上會減慢處理速度,因為磁碟頭需要尋道,並且任何預讀操作的效率都會降低。
如果該zcat步驟是您的主要效能瓶頸,並且zcat一次運行多個進程無濟於事或實際上會減慢您的速度,則您的管道受 IO 限制,並且您需要透過使用更快的儲存來解決該問題。
再說一次 - 如果目錄不是運行命令管道的電腦的本機目錄,請嘗試在檔案系統實際所在的電腦上執行它。
答案3
正如評論中指出的茲格列普是此類任務的更好選擇戒指球星允許用作的**選項all path inside the directory except hidden
shopt -s globstar
zgrep -m 10 '^\([^|]*|\)\{13\}20160920100643|\([^|]*|\)\{7\}567094398953' ./**muc*_*_20160920_*.unl*
shopt -u globstar
答案4
正如所指出的,如果沒有一些額外的細節,就不可能給出正確的答案。
locate -0 -b -r '^muc.*_.*_20160920_.*.unl.*gz' |
xargs -0 zcat |
awk -F "|" '$14=="20160920100643" && $22=="567094398953"'| head
**1:locate(如果可用)比或快得多find;使用的正規表示式必須調整...2和3:PO的過濾器
正如 @rudmeier 明智地指出的那樣,存在有關 . 的可用性和更新狀態的問題locate。 (例如,在大多數 Linux 機器中,locate 每天都會更新;這樣它將無法找到今天建立的檔案)
然而,如果locate可用,這將產生非常令人印象深刻的加速。
如果採購訂單能夠提供time ...各種解決方案,那就很有趣了


