簡體   English   中英

使用帶有 Node.js 的流寫入和讀取大型 arrays

[英]Writing and Reading large arrays using streams with Node.js

我有一個巨大的 object 用作具有 270 萬個密鑰的 map。 我嘗試將 object 寫入文件系統,以便將其持久化,而不是在每次需要時重新計算它。 在另一個步驟中,我需要再次閱讀 object。 我需要訪問 memory 中的整個 object,因為它需要充當 map。
為了寫入,我將 object 轉換為數組,並將 stream 轉換為文件系統,下面是 function。 我首先將其轉換為數組的原因是 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 數組而不是 object 似乎要快得多。 寫作部分大約需要一分鍾,這很好。 output 文件的大小為 4.8GB。
我面臨的問題是嘗試讀取文件時。 為此,我創建了一個讀取 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 並解析內容。 但是,出於某種原因,我似乎遇到了某種 memory 限制。 我使用了各種不同的方法進行讀取和解析,它們似乎都可以工作,直到讀取了大約 50% 的數據(此時我機器上的節點進程占用 6GB memory,略低於我設置的限制)。 從那時起,讀取時間顯着增加了 10 倍,可能是因為節點接近使用最大分配的 memory 限制 (6144MB)。 感覺就像我做錯了什么。
我不明白的主要事情是為什么寫入不是問題,而讀取是,即使在寫入步驟中,整個數組也保存在 memory 中。 我正在使用節點v8.11.3

所以總結一下:

  • 我有一個大的 object 我需要使用流將文件系統作為數組持久化
  • 寫作效果很好
  • 讀取工作直到讀取了大約 50% 的數據,然后讀取時間顯着增加

我怎樣才能更高效地讀取文件?

我嘗試了各種庫,例如stream-to-arrayread-json-streamJSONStream

object 示例:

{ 'id': ['some_other_id_1', 'some_other_id_2'] }

然后在寫入之前將其轉換為數組:

[{ 'id': ['some_other_id_1', 'some_other_id_2'] }]

function 使用流將數組寫入文件系統:

import * as fs from 'fs'
import * as jsonStream from 'JSONStream'
import * as streamifyArray from 'stream-array'

async function writeFileAsStreamFromArray(pathToFile: string, fileContent: any[]): Promise<void> {
  return new Promise((resolve, reject) => {
    const fileWriterStream = fs.createWriteStream(pathToFile)
    const stringifierStream = jsonStream.stringify()
    const readStream = streamifyArray(fileContent)
    readStream.pipe(stringifierStream)
    stringifierStream.pipe(fileWriterStream)

    fileWriterStream.on('finish', () => {
      console.log('writeFileAsStreamFromArray: File written.')
      stringifierStream.end()
      resolve()
    })
    fileWriterStream.on('error', (err) => {
      console.log('err', err)
      reject(err)
    })
  })
}

function 使用 jsonStream 從 stream 獲取數組:

async function getArrayFromStreamUsingJsonStream(pathToFile: string): Promise<any[]> {
  return new Promise(async (resolve, reject) => {
    const readStream = fs.createReadStream(pathToFile)
    const parseStream = jsonStream.parse('*')
    const array = []
    const start = Date.now()

    const transformer = transform((entry) => {
      array.push(entry)
      if ((array.length % 100000) === 0) {
        const end = (Date.now() - start) / 1000
        console.log('array', array.length, end)
      }
    })
    readStream.pipe(parseStream)
    parseStream.pipe(transformer)

    readStream.on('end', () => {
      console.log('getArrayFromStreamUsingJsonStream: array created')
      parseStream.end()
      resolve(array)
    })
    readStream.on('error', (error) => {
      reject(error)
    })
  })
}

計時日志(在 1200000 個條目之后,我取消了執行,因為它需要很長時間):

array 100000 6.345
array 200000 12.863
array 300000 21.177
array 400000 29.638
array 500000 35.884
array 600000 42.079
array 700000 48.74
array 800000 65.662
array 900000 89.805
array 1000000 120.416
array 1100000 148.892
array 1200000 181.921
...

預期結果:應該比目前的性能更高。 這甚至可能嗎? 還是我錯過了一些明顯的東西?

任何幫助深表感謝!!

我懷疑它的 memory 用完了,因為您正試圖將所有條目讀入一個連續的數組中。 隨着數組填滿,節點將重新分配數組並將現有數據復制到新數組。 所以隨着數組變得越來越大,它變得越來越慢。 因為在重新分配時它必須有兩個 arrays,所以它也將使用更多的 memory 而不僅僅是陣列本身。

您可以使用數據庫,因為幾百萬行應該不是問題,或者編寫自己的讀/寫例程,確保使用允許非順序塊 memory 分配的東西,例如https://www.npmjs.com/包/大陣列

例如,預分配一個長 10k 個條目的數組,將 map 的前 10k 個條目讀入數組並將數組寫入文件。 然后將接下來的 10k 個條目讀入數組並將其寫入一個新文件。 重復直到處理完所有條目。 這應該會減少您的 memory 的使用,並有助於通過並行運行 IO 來加速,但代價是使用更多的 memory。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM