按测量名高效拆分10GB+ InfluxDB行协议导出文件的脚本方案
大体积InfluxDB行协议文件按测量名分拆方案
问题背景
我有一个大小超过10GB的InfluxDB行协议导出文件,行协议的标准格式如下:
measurement,tag1=value1,tag2=value2,... value=XXX timestamp
文件内容示例:
deconz.0.Sensors.10.pressure value=998 1622621407241000000 deconz.0.Sensors.10.pressure value=999 1622621970836000000 deconz.0.Sensors.10.pressure value=999 1622624177180000000 deconz.0.Sensors.10.pressure value=999 1622625419255000000 deconz.0.Sensors.10.pressure value=998 1622625975843000000 deconz.0.Sensors.11.battery value=85 1622621407241000000 deconz.0.Sensors.11.battery value=88 1622623616070000000 deconz.0.Sensors.11.battery value=88 1622660536826000000 deconz.0.Sensors.11.battery value=85 1622663594301000000 deconz.0.Sensors.11.battery value=88 1622666692089000000 deconz.0.Sensors.11.temperature value=21.44 1622621407241000000 deconz.0.Sensors.11.temperature value=21.61 1622646781032000000 deconz.0.Sensors.11.temperature value=21.64 1622650221200000000 deconz.0.Sensors.12.humidity value=55.54 1622621407242000000 deconz.0.Sensors.12.humidity value=55.7 1622633302339000000 deconz.0.Sensors.12.humidity value=55.73 1622636722283000000 deconz.0.Sensors.12.humidity value=55.89 1622640061715000000 deconz.0.Sensors.12.humidity value=55.96 1622643481822000000 deconz.0.Sensors.13.battery value=85 1622621407242000000 deconz.0.Sensors.13.battery value=85 1622908043752000000 deconz.0.Sensors.13.temperature value=24.01 1622621407242000000 deconz.0.Sensors.13.temperature value=24.13 1622626969228000000 deconz.0.Sensors.13.temperature value=24.21 1622630216027000000 deconz.0.Sensors.13.temperature value=24.33 1622630974954000000 deconz.0.Sensors.14.humidity value=47.72 1622632937200000000 deconz.0.Sensors.14.humidity value=47.8 1622633311833000000 deconz.0.Sensors.14.humidity value=46.7 1622636659393000000 deconz.0.Sensors.15.pressure value=1002 1622673441206000000 deconz.0.Sensors.15.pressure value=1002 1622685777307000000 deconz.0.Sensors.15.pressure value=1003 1622686242842000000 deconz.0.Sensors.16.temperature value=23.47 1622654455194000000 deconz.0.Sensors.16.temperature value=23.55 1622655939005000000 deconz.0.Sensors.16.temperature value=23.57 1622655959670000000 energymeter_total,uuid=c4695262-624c-11ea-b2f7-374e5ccddc43 value=30436.6 1622594844107000000 energymeter_total,uuid=c4695262-624c-11ea-b2f7-374e5ccddc43 value=30436.6 1622594908800000000 energymeter_total,uuid=c4695262-624c-11ea-b2f7-374e5ccddc43 value=30436.6 1622594973493000000 energymeter_total,uuid=c4695262-624c-11ea-b2f7-374e5ccddc43 value=30436.6 1622595158917000000 energymeter_total,manual=true value=26984.9 1592641140000000000
需求是按测量名拆分文件:提取每行第一个逗号或空格前的内容作为测量名,以测量名为目标文件名,存储对应行的完整内容。
目前使用的shell脚本可以实现无标签行的拆分,但无法正确处理带逗号标签的场景,且速度极慢,在Intel i5+SSD的设备上跑完需要8小时:
cat ../influx_export | while read FILE VAL TS ; do echo "$FILE $VAL $TS" >> "$FILE" ; done
已知存在无需编译的脚本类解决方案,处理速度至少可提升10倍,且因为源文件过大无法全量载入内存,方案必须支持流式处理,不需要全量加载文件,求基于awk、perl、sed、ruby等工具的高效实现。
高效实现方案
方案1:awk实现(优先选择,无依赖,速度最优)
awk原生支持流式文本处理,单进程运行自带IO缓存,不需要逐行调用外部命令,比原shell循环快20倍以上。代码自动匹配每行开头到第一个逗号/空格的内容作为测量名,无论行内是否带标签都能正确识别,全程逐行处理,内存占用稳定在几MB级别,不会加载全量文件。
最优性能版本(适合测量名数量在几千以内的场景):
# 先调大单进程打开文件数限制,减少文件开关开销 ulimit -n 65535 awk ' match($0, /^[^, ]+/, res) { out = res[0] print >> out } ' ../influx_export
高兼容版本(适合测量名数量极多,超过系统文件句柄限制的场景,速度仅下降10%左右):
awk ' match($0, /^[^, ]+/, res) { out = res[0] print >> out close(out) } ' ../influx_export
方案2:Perl实现
如果环境预装了Perl,也可以使用以下版本,性能和awk基本持平:
ulimit -n 65535 perl -e ' while (<>) { /^([^, ]+)/; $out_file = $1; open($handle{$out_file}, ">>", $out_file) unless exists $handle{$out_file}; print {$handle{$out_file}} $_; } ' ../influx_export
原脚本性能问题说明
- 原shell循环每执行一次
echo >> 文件,都会触发一次独立的文件打开、写入、关闭系统调用,10GB文件通常包含上亿行,光系统调用开销就占了99%的运行时间 - 原脚本按空格拆分字段,遇到带逗号的标签行时,会把
measurement,tag1=value1整体识别为文件名,拆分结果完全错误 - 逐行写入没有缓存,每次写入都直接触发磁盘IO,SSD的顺序写性能被完全浪费
额外提速提示
- 处理过程中避免在源文件所在磁盘运行其他高IO负载的任务
- 不要使用
cat 源文件 | 处理命令的多余管道写法,直接让处理工具读取源文件,减少一次管道拷贝开销 - 测量名数量不多时优先使用不主动关闭文件句柄的版本,速度提升更明显
内容的提问来源于stack exchange,提问作者Jens
相关产品推荐
相关产品推荐

