हम 25GB से अधिक विशाल फ़ाइल को विभाजित करने और Redshift DWH में आयात करने पर विचार कर रहे हैं। अब तक, हम फ़ाइल को केवल 1000000 लाइनों से विभाजित कर सकते हैं, लेकिन इसे 111 फ़ाइलों में विभाजित करने में लगभग दो घंटे का समय लगा।

$i=1; cat .\TRGET_FILE.csv -ReadCount 1000000 | % { $_ > TRGET_FILE_$i.csv;$i++ }

साथ ही हम प्रत्येक फाइल में '\' से '\\' शब्द से बचना चाहते हैं, ताकि Redshift डेटा लोड को रोका न जा सके। लेकिन इस तरह से इसमें समय और संसाधन दोनों की खपत होती है।

क्या आप कृपया मुझे बता सकते हैं कि क्या आपको कोई बेहतर समाधान पता होगा? धन्यवाद।

3
Sachiko 13 जुलाई 2021, 01:13

3 जवाब

सबसे बढ़िया उत्तर

cat Get-Content के लिए एक उपनाम है जो फ़ाइलों को पढ़ने में बेहद धीमा है। मुझे यह भी संदेह है कि लक्ष्य फ़ाइल को लिखने के लिए पुनर्निर्देशन का उपयोग करना सबसे अच्छा तरीका नहीं हो सकता है।

आइए इसके बजाय कुछ .Net क्लासेस और विधियों का उपयोग करने का प्रयास करें:

$bigfile = '.\TRGET_FILE.csv'
$outfile = '.\TRGET_FILE{0:d5}.csv'
$linecount = 1000000
$i = 0
$outstream = $null

foreach ($line in [System.IO.File]::ReadLines($bigfile)) {
    if (($i % $linecount) -eq 0) {
        if ($null -ne $outstream) { $outstream.Close() }
        $outstream = [System.IO.StreamWriter]::new(($outfile -f ($i/$linecount)))
    }

    $outstream.WriteLine($line.Replace('\', '\\'))

    $i++
}
$outstream.Close()

[System.IO.File]::ReadLines विधि बहुत तेज़ है, और हम पहले फ़ाइल की प्रत्येक पंक्ति को पढ़े बिना, foreach लूप के साथ उस पर तेज़ी से पुनरावृति कर सकते हैं।

चूंकि लूप का प्रत्येक पुनरावृत्ति एक पंक्ति को संसाधित करता है, हम इसे लक्ष्य फ़ाइल में लिखेंगे। लेकिन हम Out-File -Append जैसे कुछ का उपयोग नहीं करना चाहते क्योंकि यह हर बार फ़ाइल को फिर से खोलेगा और बंद करेगा।

इसलिए इसके बजाय, जब हम गणना करते हैं कि हम पंक्तियों की संख्या (या पहले पुनरावृत्ति पर) तक पहुँच चुके हैं, तो हम एक नई फ़ाइल को [System.IO.File]::StreamWriter के रूप में खोल सकते हैं, और इस तरह हमारे पास यह पहले से ही खुला है, और लिख सकते हैं प्रत्येक पंक्ति।

जब हम लाइन लिखते हैं, तो हम एक सिंगल बैकस्लैश \ कैरेक्टर को भी दो बैकस्लैश से बदल देते हैं।

जब हम लाइनों की वांछित संख्या तक पहुँचते हैं, तो हम पहले जाँचते हैं कि क्या $outstream $null है (यह पहली बार $null होगा), और यदि यह शून्य नहीं है, तो हम स्ट्रीम को बंद कर देते हैं ( आउटपुट फ़ाइल को बंद करने के लिए), और फिर नए फ़ाइल नाम के साथ एक नया बनाएँ।

आउटपुट फ़ाइल नाम को स्ट्रिंग टेंपलेटिंग के माध्यम से 5 अंकों की संख्या में टेम्पलेट किया जाता है। {0:d5} -- 0 का अर्थ है टेम्पलेट में पहला आइटम, d एक संख्या के लिए है, 5 यह सुनिश्चित करने के लिए कहता है कि संख्या कम से कम 5 अंकों की है, इसलिए यह होगा इसे शून्य के साथ पैड करें। इससे फाइल ऑर्डर करने में मदद मिलेगी।

अंत में लूप के अंत में, जो वांछित संख्या में लाइनों (फ़ाइल की पूंछ) तक पहुंचने से पहले समाप्त हो जाएगा, हम उस अंतिम स्ट्रीम को बंद कर देते हैं।

頑 張 っ て ね

4
briantist 12 जुलाई 2021, 23:12

इस सटीक काम को करने के लिए लिनक्स शेल कमांड स्प्लिट बनाया गया है। यह आपकी इच्छानुसार किसी भी लाइन काउंट की लाइनों से फाइलों को विभाजित कर सकता है। देखें https://man7.org/linux/man-pages/man1 /split.1.html

split -l 1000000 -da 4 file_in file_out 
1
Bill Weiner 13 जुलाई 2021, 03:33

क्या आप रीडकाउंट को कम करके चंक आकार को 1m ~ 2m तक सीमित करने का प्रयास कर सकते हैं? कुछ इस तरह:

$i = 1; $j = 1
Get-Content .\TRGET_FILE.csv -ReadCount 10000 | 
    ForEach-Object { 
        $_ | Add-Content -Path TRGET_FILE_$i.csv
        $j++
        if ($j -eq 100) { 
            j=1; i++ 
        } 
    }

यदि यह बेहतर काम करता है तो आप इस तरह प्रतिस्थापन लागू कर सकते हैं:

$i = 1; $j = 1 
Get-Content .\TRGET_FILE.csv -ReadCount 10000 | 
    ForEach-Object { 
        $_.replace('\', '\\') | Add-Content -Path TRGET_FILE$i.csv
        $j++
        if ($j -eq 100) {
            j=1; i++
        }
    }

इसमें से बहुत कुछ डिस्क IO पर निर्भर करता है, इसलिए एक डिस्क है जो पढ़ने और लिखने पर सुपर फास्ट है ब्लॉक मदद करेगा।

1
Daniel 13 जुलाई 2021, 05:24