我正在尝试将Wikipedia CirrusSearch dump转换为450G 16核GCP实例上按标题索引的Parquet支持的dask数据帧。 CirrusSearch转储以单个json行格式的文件形式出现。 英文Wipedia转储包含500万个卡片,并经过12G压缩和90 + G扩展。 一个重要的细节是记录不是完全平坦的。
最简单的方法是
import json
import dask
from dask import bag as db, dataframe as ddf
from toolz import curried as tz
from toolz.curried import operator as op
blocksize=2**24
npartitions='auto'
parquetopts=dict(engine='fastparquet', object_encoding='json')
lang = 'en'
wiki = 'wiki'
date = 20180625
path='./'
source = f'{path}{lang}{wiki}-{date}-cirrussearch-content.json'
(
db
.read_text(source, blocksize=blocksize)
.map(json.loads)
.filter(tz.flip(op.contains, 'title'))
.to_dataframe()
.set_index('title', npartitions=npartitions)
.to_parquet(f'{lang}{wiki}-{date}-cirrussearch.pq', **parquetopts)
)
第一个问题是,使用默认调度程序时,它仅利用一个内核。可以通过明确使用分布式或多处理调度程序来避免该问题。
我尝试过的所有调度程序和设置的更大问题是内存使用率。似乎在建立索引时dask尝试将整个数据帧加载到内存中。甚至450G的内存也不够用。
答案 0 :(得分:3)
其中的JSON解析部分可能是GIL绑定的,您想使用进程。但是,当您最终计算出某些内容时,您将使用数据帧,这些数据帧通常假定计算释放了GIL(这在Pandas中很常见),因此默认情况下它使用线程后端。如果您主要受GIL解析阶段的约束,则可能要使用多处理调度程序。这应该可以解决您的问题:
dask.config.set(scheduler='multiprocessing')
是的,set_index计算需要完整的数据集。这是一个难题。如果您正在使用单机调度程序(您似乎正在这样做),那么它应该使用核外数据结构来执行此排序过程。我很惊讶它的内存不足。
不幸的是,很难用任何语言来估计内存中类似JSON的数据的大小。使用平面架构,这要容易得多。
这不能解决您的核心问题,但是您可以考虑在尝试对所有内容进行排序之前,先以Parquet格式存储数据。然后尝试单独进行dd.read_parquet(...).set_index(...).to_parquet(...)
。这可能有助于隔离一些成本。